Compare commits

..

No commits in common. "bec4439fb0fd1fa2823f49a0412aa53f114a8d45" and "4eb7f2e19dbc0e260909dbe43cdd2e306f0eed99" have entirely different histories.

306 changed files with 5691 additions and 5778 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: # the following command should return a JSON array with just all customers:
curl \ curl \
-H 'current-subject: superuser-alex@hostsharing.net' \ -H 'current-user: superuser-alex@hostsharing.net' \
http://localhost:8080/api/test/customers 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: # the following command should return a JSON array with just all packages visible for the admin of the customer yyy:
curl \ curl \
-H 'current-subject: superuser-alex@hostsharing.net' -H 'assumed-roles: rbactest.customer#yyy:ADMIN' \ -H 'current-user: superuser-alex@hostsharing.net' -H 'assumed-roles: test_customer#yyy:ADMIN' \
http://localhost:8080/api/test/packages http://localhost:8080/api/test/packages
# add a new customer # add a new customer
curl \ curl \
-H 'current-subject: superuser-alex@hostsharing.net' -H "Content-Type: application/json" \ -H 'current-user: superuser-alex@hostsharing.net' -H "Content-Type: application/json" \
-d '{ "prefix":"ttt", "reference":80001, "adminUserName":"admin@ttt.example.com" }' \ -d '{ "prefix":"ttt", "reference":80001, "adminUserName":"admin@ttt.example.com" }' \
-X POST http://localhost:8080/api/test/customers -X POST http://localhost:8080/api/test/customers

View File

@ -1,36 +0,0 @@
#!/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 ### Technical Background
The session variable `hsadminng.currentSubject` contains the accessing (domain-level) user, which is unrelated to the PostgreSQL user). The session variable `hsadminng.currentUser` contains the accessing (domain-level) user, which is unrelated to the PostgreSQL user).
Given is a stored function `isPermissionGrantedToSubject` which detects if the accessing subject has a given permission (e.g. 'view'). Given is a stored function `isPermissionGrantedToSubject` which detects if the accessing user 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. 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. Very flexible access, programmatic, rules could be implemented.
The role-hierarchy and permissions for current subjects (e.g. logged-in users) could be cached in the backend. The role-hierarchy and permissions for currently logged-in users user could be cached in the backend.
The access logic can be tested in pure Java unit tests. 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 FOR SELECT
TO restricted TO restricted
USING ( USING (
rbac.isPermissionGrantedToSubject(rbac.findEffectivePermissionId('customer', id, 'view'), currentSubjectUuid()) isPermissionGrantedToSubject(findEffectivePermissionId('customer', id, 'view'), currentUserUuid())
); );
SET SESSION AUTHORIZATION restricted; SET SESSION AUTHORIZATION restricted;
SET hsadminng.currentSubject TO 'alex@example.com'; SET hsadminng.currentUser TO 'alex@example.com';
SELECT * from customer; -- will only return visible rows SELECT * from customer; -- will only return visible rows
#### Advantages #### Advantages
@ -101,10 +101,10 @@ We are bound to PostgreSQL, including integration tests and testing the RBAC sys
CREATE OR REPLACE RULE "_RETURN" AS CREATE OR REPLACE RULE "_RETURN" AS
ON SELECT TO cust_view ON SELECT TO cust_view
DO INSTEAD DO INSTEAD
SELECT * FROM customer WHERE rbac.isPermissionGrantedToSubject(rbac.findEffectivePermissionId('customer', id, 'view'), currentSubjectUuid()); SELECT * FROM customer WHERE isPermissionGrantedToSubject(findEffectivePermissionId('customer', id, 'view'), currentUserUuid());
SET SESSION AUTHORIZATION restricted; SET SESSION AUTHORIZATION restricted;
SET hsadminng.currentSubject TO 'alex@example.com'; SET hsadminng.currentUser TO 'alex@example.com';
SELECT * from customer; -- will only return visible rows SELECT * from customer; -- will only return visible rows
#### Advantages #### 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 CREATE OR REPLACE VIEW cust_view AS
SELECT c.id, c.reference, c.prefix SELECT c.id, c.reference, c.prefix
FROM customer AS c FROM customer AS c
JOIN queryAllPermissionsOfSubjectId(currentSubjectUuid()) AS p JOIN queryAllPermissionsOfSubjectId(currentUserUuid()) AS p
ON p.tableName='customer' AND p.rowId=c.id AND p.op='view'; ON p.tableName='customer' AND p.rowId=c.id AND p.op='view';
GRANT ALL PRIVILEGES ON cust_view TO restricted; GRANT ALL PRIVILEGES ON cust_view TO restricted;
SET SESSION SESSION AUTHORIZATION restricted; SET SESSION SESSION AUTHORIZATION restricted;
SET hsadminng.currentSubject TO 'alex@example.com'; SET hsadminng.currentUser TO 'alex@example.com';
SELECT * from cust_view; -- will only return visible rows 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. Alternatively the JOIN could also be applied in a "ON SELECT DO INSTEAD"-RULE, if there is any advantage for later features.

View File

@ -206,14 +206,14 @@ Limit (cost=6549.08..6549.35 rows=54 width=16)
```SQL ```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 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 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_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_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 LEFT JOIN hs_office_person_rv h1_0 ON h1_0.uuid=hore1_0.holderuuid
WHERE hore1_0.uuid=$1 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 ### 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 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 ```SQL
ALTER FUNCTION rbac.queryAccessibleObjectUuidsOfSubjectIds SET enable_hashjoin = off; ALTER FUNCTION queryAccessibleObjectUuidsOfSubjectIds SET enable_hashjoin = off;
``` ```
did not improve the performance though. The HashJoin was actually still applied, but no full table scan anymore: 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 | | 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 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 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 | | 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 grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) | 31316 | 0 | 1 |
| call buildRbacSystemForHsHostingAsset(NEW) | 2258 | 0 | 7 | | call buildRbacSystemForHsHostingAsset(NEW) | 2258 | 0 | 7 |
| select * from rbac.isGranted(array[granteeId], grantedId) | 44613 | 0 | 0 | | select * from 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 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 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 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 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 | | 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 | | 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 | | 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 :::small
| query | calls | total (min) | mean (ms) | | 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 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 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 | | 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 | | call grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) | 31316 | 0 | 1 |
| select * from rbac.isGranted(array[granteeId], grantedId) | 44613 | 0 | 0 | | select * from isGranted(array[granteeId], grantedId) | 44613 | 0 | 0 |
| call buildRbacSystemForHsHostingAsset(NEW) | 2258 | 0 | 7 | | 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 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 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 | | 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 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 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 | | 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 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 | | 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 | | 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) | | 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) | | 3 | 13.144 | 4 | 21 | call buildRbacSystemForHsHostingAsset(NEW) |
| 4 | 96.632 | 3 | 2 | call rbac.grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) | | 4 | 96.632 | 3 | 2 | call grantRoleToRole(roleUuid, superRoleUuid, superRoleDesc.assumed) |
| 5 | 120.815 | 3 | 2 | select * from rbac.isGranted(array[granteeId], grantedId) | | 5 | 120.815 | 3 | 2 | select * from 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) | | 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 | | 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 | | 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)] ) | | 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)] ) | | 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. 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 ## 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. 3. Inverting the recursion of the CTE-query, combined with the type condition.
Instead of starting the recursion with `currentSubjectOrAssumedRolesUuids()`, Instead of starting the recursion with `currentsubjectsuuids()`,
we could start it with the target table name and row-type, we could start it with the target table name and row-type,
then recurse down to the `currentSubjectOrAssumedRolesUuids()`. then recurse down to the `currentsubjectsuuids()`.
In the end, we need the object UUIDs, though. In the end, we need the object UUIDs, though.
But if we start with the join of `rbacObject` with `rbacPermission`, But if we start with the join of `rbacObject` with `rbacPermission`,

View File

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

View File

@ -24,13 +24,13 @@ delete from hs_hosting_asset where uuid='5aea68d2-3b55-464f-8362-b05c76c5a681'::
commit; commit;
-- single version at point in time -- single version at point in time
-- 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 (select max(txid) from tx_context where txtimestamp<='2024-08-27 12:13:13.450821');
set hsadminng.tx_history_txid to ''; set hsadminng.tx_history_txid to '';
set hsadminng.tx_history_timestamp to '2024-08-29 12:42'; set hsadminng.tx_history_timestamp to '2024-08-29 12:42';
-- all versions -- all versions
select base.tx_history_txid(), txc.txtimestamp, txc.currentSubject, txc.currentTask, haex.* select tx_history_txid(), txc.txtimestamp, txc.currentUser, txc.currentTask, haex.*
from hs_hosting_asset_ex haex from hs_hosting_asset_ex haex
join base.tx_context txc on haex.txid=txc.txid join tx_context txc on haex.txid=txc.txid
where haex.identifier = 'test@thi.example.org'; 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'; 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 rbac.isGranted(rbac.findRoleId('administrators'), rbac.findRoleId('rbactest.package#aaa00:OWNER')); select isGranted(findRoleId('administrators'), findRoleId('test_package#aaa00:OWNER'));
select rbac.isGranted(rbac.findRoleId('rbactest.package#aaa00:OWNER'), rbac.findRoleId('administrators')); select isGranted(findRoleId('test_package#aaa00:OWNER'), findRoleId('administrators'));
-- call rbac.grantRoleToRole(findRoleId('rbactest.package#aaa00:OWNER'), findRoleId('administrators')); -- call grantRoleToRole(findRoleId('test_package#aaa00:OWNER'), findRoleId('administrators'));
-- call rbac.grantRoleToRole(findRoleId('administrators'), findRoleId('rbactest.package#aaa00:OWNER')); -- call grantRoleToRole(findRoleId('administrators'), findRoleId('test_package#aaa00:OWNER'));
select count(*) select count(*)
FROM rbac.queryAllPermissionsOfSubjectIdForObjectUuids(rbac.findRbacSubject('superuser-fran@hostsharing.net'), FROM queryAllPermissionsOfSubjectIdForObjectUuids(findRbacUser('superuser-fran@hostsharing.net'),
ARRAY(select uuid from rbactest.customer where reference < 1100000)); ARRAY(select uuid from customer where reference < 1100000));
select count(*) select count(*)
FROM rbac.queryAllPermissionsOfSubjectId(findRbacSubject('superuser-fran@hostsharing.net')); FROM queryAllPermissionsOfSubjectId(findRbacUser('superuser-fran@hostsharing.net'));
select * select *
FROM rbac.queryAllPermissionsOfSubjectId(findRbacSubject('alex@example.com')); FROM queryAllPermissionsOfSubjectId(findRbacUser('alex@example.com'));
select * select *
FROM rbac.queryAllPermissionsOfSubjectId(findRbacSubject('rosa@example.com')); FROM queryAllPermissionsOfSubjectId(findRbacUser('rosa@example.com'));
select * select *
FROM rbac.queryAllRbacSubjectsWithPermissionsFor(rbac.findEffectivePermissionId('customer', FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('customer',
(SELECT uuid FROM rbac.RbacObject WHERE objectTable = 'customer' LIMIT 1), (SELECT uuid FROM RbacObject WHERE objectTable = 'customer' LIMIT 1),
'add-package')); 'add-package'));
select * select *
FROM rbac.queryAllRbacSubjectsWithPermissionsFor(rbac.findEffectivePermissionId('package', FROM queryAllRbacUsersWithPermissionsFor(findEffectivePermissionId('package',
(SELECT uuid FROM rbac.RbacObject WHERE objectTable = 'package' LIMIT 1), (SELECT uuid FROM RbacObject WHERE objectTable = 'package' LIMIT 1),
'DELETE')); 'DELETE'));
DO LANGUAGE plpgsql DO LANGUAGE plpgsql
@ -33,13 +33,13 @@ $$
userId uuid; userId uuid;
result bool; result bool;
BEGIN BEGIN
userId = rbac.findRbacSubject('superuser-alex@hostsharing.net'); userId = findRbacUser('superuser-alex@hostsharing.net');
result = (SELECT * FROM rbac.isPermissionGrantedToSubject(rbac.findPermissionId('package', 94928, 'add-package'), userId)); result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'add-package'), userId));
IF (result) THEN IF (result) THEN
RAISE EXCEPTION 'expected permission NOT to be granted, but it is'; RAISE EXCEPTION 'expected permission NOT to be granted, but it is';
end if; end if;
result = (SELECT * FROM rbac.isPermissionGrantedToSubject(rbac.findPermissionId('package', 94928, 'SELECT'), userId)); result = (SELECT * FROM isPermissionGrantedToSubject(findPermissionId('package', 94928, 'SELECT'), userId));
IF (NOT result) THEN IF (NOT result) THEN
RAISE EXCEPTION 'expected permission to be granted, but it is NOT'; RAISE EXCEPTION 'expected permission to be granted, but it is NOT';
end if; end if;

View File

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

View File

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

View File

@ -38,53 +38,53 @@ public class Context {
private HttpServletRequest request; private HttpServletRequest request;
@Transactional(propagation = MANDATORY) @Transactional(propagation = MANDATORY)
public void define(final String currentSubject) { public void define(final String currentUser) {
define(currentSubject, null); define(currentUser, null);
} }
@Transactional(propagation = MANDATORY) @Transactional(propagation = MANDATORY)
public void define(final String currentSubject, final String assumedRoles) { public void define(final String currentUser, final String assumedRoles) {
define(toTask(request), toCurl(request), currentSubject, assumedRoles); define(toTask(request), toCurl(request), currentUser, assumedRoles);
} }
@Transactional(propagation = MANDATORY) @Transactional(propagation = MANDATORY)
public void define( public void define(
final String currentTask, final String currentTask,
final String currentRequest, final String currentRequest,
final String currentSubject, final String currentUser,
final String assumedRoles) { final String assumedRoles) {
final var query = em.createNativeQuery(""" final var query = em.createNativeQuery("""
call base.defineContext( call defineContext(
cast(:currentTask as varchar(127)), cast(:currentTask as varchar(127)),
cast(:currentRequest as text), cast(:currentRequest as text),
cast(:currentSubject as varchar(63)), cast(:currentUser as varchar(63)),
cast(:assumedRoles as varchar(1023))); cast(:assumedRoles as varchar(1023)));
"""); """);
query.setParameter("currentTask", shortenToMaxLength(currentTask, 127)); query.setParameter("currentTask", shortenToMaxLength(currentTask, 127));
query.setParameter("currentRequest", currentRequest); query.setParameter("currentRequest", currentRequest);
query.setParameter("currentSubject", currentSubject); query.setParameter("currentUser", currentUser);
query.setParameter("assumedRoles", assumedRoles != null ? assumedRoles : ""); query.setParameter("assumedRoles", assumedRoles != null ? assumedRoles : "");
query.executeUpdate(); query.executeUpdate();
} }
public String fetchCurrentTask() { public String getCurrentTask() {
return (String) em.createNativeQuery("select current_setting('hsadminng.currentTask');").getSingleResult(); return (String) em.createNativeQuery("select current_setting('hsadminng.currentTask');").getSingleResult();
} }
public String fetchCurrentSubject() { public String getCurrentUser() {
return String.valueOf(em.createNativeQuery("select base.currentSubject()").getSingleResult()); return String.valueOf(em.createNativeQuery("select currentUser()").getSingleResult());
} }
public UUID fetchCurrentSubjectUuid() { public UUID getCurrentUserUUid() {
return (UUID) em.createNativeQuery("select rbac.currentSubjectUuid()", UUID.class).getSingleResult(); return (UUID) em.createNativeQuery("select currentUserUUid()", UUID.class).getSingleResult();
} }
public String[] fetchAssumedRoles() { public String[] getAssumedRoles() {
return (String[]) em.createNativeQuery("select base.assumedRoles() as roles", String[].class).getSingleResult(); return (String[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult();
} }
public UUID[] fetchCurrentSubjectOrAssumedRolesUuids() { public UUID[] currentSubjectsUuids() {
return (UUID[]) em.createNativeQuery("select rbac.currentSubjectOrAssumedRolesUuids() as uuids", UUID[].class).getSingleResult(); return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
} }
public static String getCallerMethodNameFromStackFrame(final int skipFrames) { 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.booking.project.HsBookingProjectRealEntity;
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;

View File

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

View File

@ -4,9 +4,9 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.hs.booking.project.HsBookingProjectRbacEntity; import net.hostsharing.hsadminng.hs.booking.project.HsBookingProject;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import jakarta.persistence.AttributeOverride; import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides; import jakarta.persistence.AttributeOverrides;
@ -15,20 +15,19 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.io.IOException; import java.io.IOException;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NULLABLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
@Entity @Entity
@Table(name = "hs_booking_item_rv") @Table(name = "hs_booking_item_rv")
@ -46,10 +45,10 @@ public class HsBookingItemRbacEntity extends HsBookingItem {
.withIdentityView(SQL.projection("caption")) .withIdentityView(SQL.projection("caption"))
.withRestrictedViewOrderBy(SQL.expression("validity")) .withRestrictedViewOrderBy(SQL.expression("validity"))
.withUpdatableColumns("version", "caption", "validity", "resources") .withUpdatableColumns("version", "caption", "validity", "resources")
.toRole(GLOBAL, ADMIN).grantPermission(INSERT) // TODO.impl: Why is this necessary to insert test data? .toRole("global", ADMIN).grantPermission(INSERT) // TODO.impl: Why is this necessary to insert test data?
.toRole(GLOBAL, ADMIN).grantPermission(DELETE) .toRole("global", ADMIN).grantPermission(DELETE)
.importEntityAlias("project", HsBookingProjectRbacEntity.class, usingDefaultCase(), .importEntityAlias("project", HsBookingProject.class, usingDefaultCase(),
dependsOnColumn("projectUuid"), dependsOnColumn("projectUuid"),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
NULLABLE) NULLABLE)
@ -75,7 +74,7 @@ public class HsBookingItemRbacEntity extends HsBookingItem {
with.permission(SELECT); with.permission(SELECT);
}) })
.limitDiagramTo("bookingItem", "project", "rbac.global"); .limitDiagramTo("bookingItem", "project", "global");
} }
public static void main(String[] args) throws IOException { 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.booking.debitor.HsBookingDebitorEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -17,16 +17,15 @@ import java.util.UUID;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@MappedSuperclass @MappedSuperclass
@ -68,11 +67,11 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
} }
public static RbacView rbac() { public static RbacView rbac() {
return rbacViewFor("project", HsBookingProjectRbacEntity.class) return rbacViewFor("project", HsBookingProject.class)
.withIdentityView(SQL.query(""" .withIdentityView(SQL.query("""
SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || base.cleanIdentifier(bookingProject.caption) as idName SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || cleanIdentifier(bookingProject.caption) as idName
FROM hs_booking_project bookingProject 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")) .withRestrictedViewOrderBy(SQL.expression("caption"))
.withUpdatableColumns("version", "caption") .withUpdatableColumns("version", "caption")
@ -86,13 +85,13 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
dependsOnColumn("debitorUuid"), dependsOnColumn("debitorUuid"),
fetchedBySql(""" fetchedBySql("""
SELECT ${columns} SELECT ${columns}
FROM hs_office.relation debitorRel FROM hs_office_relation debitorRel
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
WHERE debitor.uuid = ${REF}.debitorUuid WHERE debitor.uuid = ${REF}.debitorUuid
"""), """),
NOT_NULL) NOT_NULL)
.toRole("debitorRel", ADMIN).grantPermission(INSERT) .toRole("debitorRel", ADMIN).grantPermission(INSERT)
.toRole(GLOBAL, ADMIN).grantPermission(DELETE) .toRole("global", ADMIN).grantPermission(DELETE)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.incomingSuperRole("debitorRel", AGENT).unassumed(); with.incomingSuperRole("debitorRel", AGENT).unassumed();
@ -106,7 +105,7 @@ public abstract class HsBookingProject implements Stringifyable, BaseEntity<HsBo
with.permission(SELECT); with.permission(SELECT);
}) })
.limitDiagramTo("project", "debitorRel", "rbac.global"); .limitDiagramTo("project", "debitorRel", "global");
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {

View File

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

View File

@ -6,30 +6,29 @@ import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.io.IOException; import java.io.IOException;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
@Entity @Entity
@Table(name = "hs_booking_project_rv") @Table(name = "hs_booking_project_rv")
@ -42,9 +41,9 @@ public class HsBookingProjectRbacEntity extends HsBookingProject {
public static RbacView rbac() { public static RbacView rbac() {
return rbacViewFor("project", HsBookingProjectRbacEntity.class) return rbacViewFor("project", HsBookingProjectRbacEntity.class)
.withIdentityView(SQL.query(""" .withIdentityView(SQL.query("""
SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || base.cleanIdentifier(bookingProject.caption) as idName SELECT bookingProject.uuid as uuid, debitorIV.idName || '-' || cleanIdentifier(bookingProject.caption) as idName
FROM hs_booking_project bookingProject 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")) .withRestrictedViewOrderBy(SQL.expression("caption"))
.withUpdatableColumns("version", "caption") .withUpdatableColumns("version", "caption")
@ -58,13 +57,13 @@ public class HsBookingProjectRbacEntity extends HsBookingProject {
dependsOnColumn("debitorUuid"), dependsOnColumn("debitorUuid"),
fetchedBySql(""" fetchedBySql("""
SELECT ${columns} SELECT ${columns}
FROM hs_office.relation debitorRel FROM hs_office_relation debitorRel
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
WHERE debitor.uuid = ${REF}.debitorUuid WHERE debitor.uuid = ${REF}.debitorUuid
"""), """),
NOT_NULL) NOT_NULL)
.toRole("debitorRel", ADMIN).grantPermission(INSERT) .toRole("debitorRel", ADMIN).grantPermission(INSERT)
.toRole(GLOBAL, ADMIN).grantPermission(DELETE) .toRole("global", ADMIN).grantPermission(DELETE)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.incomingSuperRole("debitorRel", AGENT).unassumed(); with.incomingSuperRole("debitorRel", AGENT).unassumed();
@ -78,7 +77,7 @@ public class HsBookingProjectRbacEntity extends HsBookingProject {
with.permission(SELECT); with.permission(SELECT);
}) })
.limitDiagramTo("project", "debitorRel", "rbac.global"); .limitDiagramTo("project", "debitorRel", "global");
} }
public static void main(String[] args) throws IOException { 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.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider; import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;

View File

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

View File

@ -4,33 +4,33 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRbacEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItem;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.io.IOException; import java.io.IOException;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef.inCaseOf; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inCaseOf;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NULLABLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.GUEST; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.GUEST;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.REFERRER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.REFERRER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Entity @Entity
@Table(name = "hs_hosting_asset_rv") @Table(name = "hs_hosting_asset_rv")
@ -47,7 +47,7 @@ public class HsHostingAssetRbacEntity extends HsHostingAsset {
.withUpdatableColumns("version", "caption", "config", "assignedToAssetUuid", "alarmContactUuid") .withUpdatableColumns("version", "caption", "config", "assignedToAssetUuid", "alarmContactUuid")
.toRole(GLOBAL, ADMIN).grantPermission(INSERT) // TODO.impl: Why is this necessary to insert test data? .toRole(GLOBAL, ADMIN).grantPermission(INSERT) // TODO.impl: Why is this necessary to insert test data?
.importEntityAlias("bookingItem", HsBookingItemRbacEntity.class, usingDefaultCase(), .importEntityAlias("bookingItem", HsBookingItem.class, usingDefaultCase(),
dependsOnColumn("bookingItemUuid"), dependsOnColumn("bookingItemUuid"),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
NULLABLE) NULLABLE)
@ -106,7 +106,7 @@ public class HsHostingAssetRbacEntity extends HsHostingAsset {
"parentAsset", "parentAsset",
"assignedToAsset", "assignedToAsset",
"alarmContact", "alarmContact",
"rbac.global"); "global");
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {

View File

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

View File

@ -3,8 +3,8 @@ package net.hostsharing.hsadminng.hs.office.bankaccount;
import lombok.*; import lombok.*;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -12,14 +12,14 @@ import jakarta.persistence.*;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(schema = "hs_office", name = "bankaccount_rv") @Table(name = "hs_office_bankaccount_rv")
@Getter @Getter
@Setter @Setter
@Builder @Builder
@ -62,7 +62,7 @@ public class HsOfficeBankAccountEntity implements BaseEntity<HsOfficeBankAccount
.withIdentityView(SQL.projection("iban")) .withIdentityView(SQL.projection("iban"))
.withUpdatableColumns("holder", "iban", "bic") .withUpdatableColumns("holder", "iban", "bic")
.toRole(GLOBAL, GUEST).grantPermission(INSERT) .toRole("global", GUEST).grantPermission(INSERT)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.owningUser(CREATOR); with.owningUser(CREATOR);

View File

@ -11,7 +11,7 @@ import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.mapper.PatchableMapWrapper; import net.hostsharing.hsadminng.mapper.PatchableMapWrapper;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;

View File

@ -34,10 +34,10 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficeContactResource>> listContacts( public ResponseEntity<List<HsOfficeContactResource>> listContacts(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final String caption) { final String caption) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = contactRepo.findContactByOptionalCaptionLike(caption); final var entities = contactRepo.findContactByOptionalCaptionLike(caption);
@ -48,11 +48,11 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeContactResource> addContact( public ResponseEntity<HsOfficeContactResource> addContact(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final HsOfficeContactInsertResource body) { final HsOfficeContactInsertResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entityToSave = mapper.map(body, HsOfficeContactRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); final var entityToSave = mapper.map(body, HsOfficeContactRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
@ -70,11 +70,11 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficeContactResource> getContactByUuid( public ResponseEntity<HsOfficeContactResource> getContactByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID contactUuid) { final UUID contactUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = contactRepo.findByUuid(contactUuid); final var result = contactRepo.findByUuid(contactUuid);
if (result.isEmpty()) { if (result.isEmpty()) {
@ -86,10 +86,10 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deleteContactByUuid( public ResponseEntity<Void> deleteContactByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID contactUuid) { final UUID contactUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = contactRepo.deleteByUuid(contactUuid); final var result = contactRepo.deleteByUuid(contactUuid);
if (result == 0) { if (result == 0) {
@ -102,12 +102,12 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeContactResource> patchContact( public ResponseEntity<HsOfficeContactResource> patchContact(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID contactUuid, final UUID contactUuid,
final HsOfficeContactPatchResource body) { final HsOfficeContactPatchResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var current = contactRepo.findByUuid(contactUuid).orElseThrow(); final var current = contactRepo.findByUuid(contactUuid).orElseThrow();

View File

@ -3,20 +3,20 @@ package net.hostsharing.hsadminng.hs.office.contact;
import lombok.*; import lombok.*;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.io.IOException; import java.io.IOException;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Entity @Entity
@Table(schema = "hs_office", name = "contact_rv") @Table(name = "hs_office_contact_rv")
@Getter @Getter
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor

View File

@ -10,7 +10,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Entity @Entity
@Table(schema = "hs_office", name = "contact") @Table(name = "hs_office_contact")
@Getter @Getter
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor

View File

@ -37,12 +37,12 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficeCoopAssetsTransactionResource>> listCoopAssets( public ResponseEntity<List<HsOfficeCoopAssetsTransactionResource>> listCoopAssets(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID membershipUuid, final UUID membershipUuid,
final @DateTimeFormat(iso = ISO.DATE) LocalDate fromValueDate, final @DateTimeFormat(iso = ISO.DATE) LocalDate fromValueDate,
final @DateTimeFormat(iso = ISO.DATE) LocalDate toValueDate) { final @DateTimeFormat(iso = ISO.DATE) LocalDate toValueDate) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange( final var entities = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(
membershipUuid, membershipUuid,
@ -56,11 +56,11 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> addCoopAssetsTransaction( public ResponseEntity<HsOfficeCoopAssetsTransactionResource> addCoopAssetsTransaction(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final HsOfficeCoopAssetsTransactionInsertResource requestBody) { final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
validate(requestBody); validate(requestBody);
final var entityToSave = mapper.map(requestBody, HsOfficeCoopAssetsTransactionEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); final var entityToSave = mapper.map(requestBody, HsOfficeCoopAssetsTransactionEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
@ -79,9 +79,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> getCoopAssetTransactionByUuid( public ResponseEntity<HsOfficeCoopAssetsTransactionResource> getCoopAssetTransactionByUuid(
final String currentSubject, final String assumedRoles, final UUID assetTransactionUuid) { final String currentUser, final String assumedRoles, final UUID assetTransactionUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = coopAssetsTransactionRepo.findByUuid(assetTransactionUuid); final var result = coopAssetsTransactionRepo.findByUuid(assetTransactionUuid);
if (result.isEmpty()) { if (result.isEmpty()) {

View File

@ -8,8 +8,8 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;
@ -21,20 +21,20 @@ import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(schema = "hs_office", name = "coopassetstransaction_rv") @Table(name = "hs_office_coopassetstransaction_rv")
@Getter @Getter
@Setter @Setter
@Builder @Builder

View File

@ -39,12 +39,12 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> listCoopShares( public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> listCoopShares(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID membershipUuid, final UUID membershipUuid,
final @DateTimeFormat(iso = ISO.DATE) LocalDate fromValueDate, final @DateTimeFormat(iso = ISO.DATE) LocalDate fromValueDate,
final @DateTimeFormat(iso = ISO.DATE) LocalDate toValueDate) { final @DateTimeFormat(iso = ISO.DATE) LocalDate toValueDate) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange( final var entities = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
membershipUuid, membershipUuid,
@ -58,11 +58,11 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeCoopSharesTransactionResource> addCoopSharesTransaction( public ResponseEntity<HsOfficeCoopSharesTransactionResource> addCoopSharesTransaction(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final HsOfficeCoopSharesTransactionInsertResource requestBody) { final HsOfficeCoopSharesTransactionInsertResource requestBody) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
validate(requestBody); validate(requestBody);
final var entityToSave = mapper.map(requestBody, HsOfficeCoopSharesTransactionEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER); final var entityToSave = mapper.map(requestBody, HsOfficeCoopSharesTransactionEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
@ -81,9 +81,9 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficeCoopSharesTransactionResource> getCoopShareTransactionByUuid( public ResponseEntity<HsOfficeCoopSharesTransactionResource> getCoopShareTransactionByUuid(
final String currentSubject, final String assumedRoles, final UUID shareTransactionUuid) { final String currentUser, final String assumedRoles, final UUID shareTransactionUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = coopSharesTransactionRepo.findByUuid(shareTransactionUuid); final var result = coopSharesTransactionRepo.findByUuid(shareTransactionUuid);
if (result.isEmpty()) { if (result.isEmpty()) {

View File

@ -7,9 +7,9 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity; import net.hostsharing.hsadminng.hs.office.membership.HsOfficeMembershipEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -19,20 +19,20 @@ import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(schema = "hs_office", name = "coopsharestransaction_rv") @Table(name = "hs_office_coopsharestransaction_rv")
@Getter @Getter
@Setter @Setter
@Builder @Builder

View File

@ -8,7 +8,7 @@ import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeDebito
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -48,11 +48,11 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficeDebitorResource>> listDebitors( public ResponseEntity<List<HsOfficeDebitorResource>> listDebitors(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final String name, final String name,
final Integer debitorNumber) { final Integer debitorNumber) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = debitorNumber != null final var entities = debitorNumber != null
? debitorRepo.findDebitorByDebitorNumber(debitorNumber) ? debitorRepo.findDebitorByDebitorNumber(debitorNumber)
@ -65,11 +65,11 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeDebitorResource> addDebitor( public ResponseEntity<HsOfficeDebitorResource> addDebitor(
String currentSubject, String currentUser,
String assumedRoles, String assumedRoles,
HsOfficeDebitorInsertResource body) { HsOfficeDebitorInsertResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRelUuid() == null, Validate.isTrue(body.getDebitorRel() == null || body.getDebitorRelUuid() == null,
"ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both"); "ERROR: [400] exactly one of debitorRel and debitorRelUuid must be supplied, but found both");
@ -112,11 +112,11 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficeDebitorResource> getDebitorByUuid( public ResponseEntity<HsOfficeDebitorResource> getDebitorByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID debitorUuid) { final UUID debitorUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = debitorRepo.findByUuid(debitorUuid); final var result = debitorRepo.findByUuid(debitorUuid);
if (result.isEmpty()) { if (result.isEmpty()) {
@ -128,10 +128,10 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deleteDebitorByUuid( public ResponseEntity<Void> deleteDebitorByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID debitorUuid) { final UUID debitorUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = debitorRepo.deleteByUuid(debitorUuid); final var result = debitorRepo.deleteByUuid(debitorUuid);
if (result == 0) { if (result == 0) {
@ -144,12 +144,12 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeDebitorResource> patchDebitor( public ResponseEntity<HsOfficeDebitorResource> patchDebitor(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID debitorUuid, final UUID debitorUuid,
final HsOfficeDebitorPatchResource body) { final HsOfficeDebitorPatchResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var current = debitorRepo.findByUuid(debitorUuid).orElseThrow(); final var current = debitorRepo.findByUuid(debitorUuid).orElseThrow();

View File

@ -11,9 +11,9 @@ import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.GenericGenerator;
@ -40,21 +40,20 @@ import static jakarta.persistence.CascadeType.PERSIST;
import static jakarta.persistence.CascadeType.REFRESH; import static jakarta.persistence.CascadeType.REFRESH;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NULLABLE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NULLABLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(schema = "hs_office", name = "debitor_rv") @Table(name = "hs_office_debitor_rv")
@Getter @Getter
@Setter @Setter
@Builder(toBuilder = true) @Builder(toBuilder = true)
@ -87,10 +86,10 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
value = """ value = """
( (
SELECT DISTINCT partner.uuid SELECT DISTINCT partner.uuid
FROM hs_office.partner_rv partner FROM hs_office_partner_rv partner
JOIN hs_office.relation_rv dRel JOIN hs_office_relation_rv dRel
ON dRel.uuid = debitorreluuid AND dRel.type = 'DEBITOR' ON dRel.uuid = debitorreluuid AND dRel.type = 'DEBITOR'
JOIN hs_office.relation_rv pRel JOIN hs_office_relation_rv pRel
ON pRel.uuid = partner.partnerRelUuid AND pRel.type = 'PARTNER' ON pRel.uuid = partner.partnerRelUuid AND pRel.type = 'PARTNER'
WHERE pRel.holderUuid = dRel.anchorUuid WHERE pRel.holderUuid = dRel.anchorUuid
) )
@ -170,14 +169,14 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
.withIdentityView(SQL.query(""" .withIdentityView(SQL.query("""
SELECT debitor.uuid AS uuid, SELECT debitor.uuid AS uuid,
'D-' || (SELECT partner.partnerNumber 'D-' || (SELECT partner.partnerNumber
FROM hs_office.partner partner FROM hs_office_partner partner
JOIN hs_office.relation partnerRel JOIN hs_office_relation partnerRel
ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER' ON partnerRel.uuid = partner.partnerRelUUid AND partnerRel.type = 'PARTNER'
JOIN hs_office.relation debitorRel JOIN hs_office_relation debitorRel
ON debitorRel.anchorUuid = partnerRel.holderUuid AND debitorRel.type = 'DEBITOR' ON debitorRel.anchorUuid = partnerRel.holderUuid AND debitorRel.type = 'DEBITOR'
WHERE debitorRel.uuid = debitor.debitorRelUuid) WHERE debitorRel.uuid = debitor.debitorRelUuid)
|| debitorNumberSuffix as idName || debitorNumberSuffix as idName
FROM hs_office.debitor AS debitor FROM hs_office_debitor AS debitor
""")) """))
.withRestrictedViewOrderBy(SQL.projection("defaultPrefix")) .withRestrictedViewOrderBy(SQL.projection("defaultPrefix"))
.withUpdatableColumns( .withUpdatableColumns(
@ -189,7 +188,7 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
"vatBusiness", "vatBusiness",
"vatReverseCharge", "vatReverseCharge",
"defaultPrefix") "defaultPrefix")
.toRole(GLOBAL, ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("debitorRel", HsOfficeRelationRbacEntity.class, usingCase(DEBITOR), .importRootEntityAliasProxy("debitorRel", HsOfficeRelationRbacEntity.class, usingCase(DEBITOR),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),
@ -209,8 +208,8 @@ public class HsOfficeDebitorEntity implements BaseEntity<HsOfficeDebitorEntity>,
dependsOnColumn("debitorRelUuid"), dependsOnColumn("debitorRelUuid"),
fetchedBySql(""" fetchedBySql("""
SELECT ${columns} SELECT ${columns}
FROM hs_office.relation AS partnerRel FROM hs_office_relation AS partnerRel
JOIN hs_office.relation AS debitorRel JOIN hs_office_relation AS debitorRel
ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid ON debitorRel.type = 'DEBITOR' AND debitorRel.anchorUuid = partnerRel.holderUuid
WHERE partnerRel.type = 'PARTNER' WHERE partnerRel.type = 'PARTNER'
AND ${REF}.debitorRelUuid = debitorRel.uuid AND ${REF}.debitorRelUuid = debitorRel.uuid

View File

@ -32,11 +32,11 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficeMembershipResource>> listMemberships( public ResponseEntity<List<HsOfficeMembershipResource>> listMemberships(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
UUID partnerUuid, UUID partnerUuid,
Integer memberNumber) { Integer memberNumber) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = ( memberNumber != null) final var entities = ( memberNumber != null)
? List.of(membershipRepo.findMembershipByMemberNumber(memberNumber)) ? List.of(membershipRepo.findMembershipByMemberNumber(memberNumber))
@ -50,11 +50,11 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeMembershipResource> addMembership( public ResponseEntity<HsOfficeMembershipResource> addMembership(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final HsOfficeMembershipInsertResource body) { final HsOfficeMembershipInsertResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entityToSave = mapper.map(body, HsOfficeMembershipEntity.class); final var entityToSave = mapper.map(body, HsOfficeMembershipEntity.class);
@ -73,11 +73,11 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficeMembershipResource> getMembershipByUuid( public ResponseEntity<HsOfficeMembershipResource> getMembershipByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID membershipUuid) { final UUID membershipUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = membershipRepo.findByUuid(membershipUuid); final var result = membershipRepo.findByUuid(membershipUuid);
if (result.isEmpty()) { if (result.isEmpty()) {
@ -90,10 +90,10 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deleteMembershipByUuid( public ResponseEntity<Void> deleteMembershipByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID membershipUuid) { final UUID membershipUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = membershipRepo.deleteByUuid(membershipUuid); final var result = membershipRepo.deleteByUuid(membershipUuid);
if (result == 0) { if (result == 0) {
@ -106,12 +106,12 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeMembershipResource> patchMembership( public ResponseEntity<HsOfficeMembershipResource> patchMembership(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID membershipUuid, final UUID membershipUuid,
final HsOfficeMembershipPatchResource body) { final HsOfficeMembershipPatchResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var current = membershipRepo.findByUuid(membershipUuid).orElseThrow(); final var current = membershipRepo.findByUuid(membershipUuid).orElseThrow();

View File

@ -9,10 +9,10 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity; import net.hostsharing.hsadminng.hs.office.partner.HsOfficePartnerEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
@ -38,25 +38,24 @@ import static io.hypersistence.utils.hibernate.type.range.Range.emptyRange;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(schema = "hs_office", name = "membership_rv") @Table(name = "hs_office_membership_rv")
@Getter @Getter
@Setter @Setter
@Builder @Builder
@ -160,8 +159,8 @@ public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEn
.withIdentityView(SQL.query(""" .withIdentityView(SQL.query("""
SELECT m.uuid AS uuid, SELECT m.uuid AS uuid,
'M-' || p.partnerNumber || m.memberNumberSuffix as idName 'M-' || p.partnerNumber || m.memberNumberSuffix as idName
FROM hs_office.membership AS m FROM hs_office_membership AS m
JOIN hs_office.partner AS p ON p.uuid = m.partnerUuid JOIN hs_office_partner AS p ON p.uuid = m.partnerUuid
""")) """))
.withRestrictedViewOrderBy(SQL.projection("validity")) .withRestrictedViewOrderBy(SQL.projection("validity"))
.withUpdatableColumns("validity", "membershipFeeBillable", "status") .withUpdatableColumns("validity", "membershipFeeBillable", "status")
@ -170,12 +169,12 @@ public class HsOfficeMembershipEntity implements BaseEntity<HsOfficeMembershipEn
dependsOnColumn("partnerUuid"), dependsOnColumn("partnerUuid"),
fetchedBySql(""" fetchedBySql("""
SELECT ${columns} SELECT ${columns}
FROM hs_office.partner AS partner FROM hs_office_partner AS partner
JOIN hs_office.relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid JOIN hs_office_relation AS partnerRel ON partnerRel.uuid = partner.partnerRelUuid
WHERE partner.uuid = ${REF}.partnerUuid WHERE partner.uuid = ${REF}.partnerUuid
"""), """),
NOT_NULL) NOT_NULL)
.toRole(GLOBAL, ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.owningUser(CREATOR); with.owningUser(CREATOR);

View File

@ -13,7 +13,7 @@ import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealRepository;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -50,10 +50,10 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficePartnerResource>> listPartners( public ResponseEntity<List<HsOfficePartnerResource>> listPartners(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final String name) { final String name) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = partnerRepo.findPartnerByOptionalNameLike(name); final var entities = partnerRepo.findPartnerByOptionalNameLike(name);
@ -64,11 +64,11 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficePartnerResource> addPartner( public ResponseEntity<HsOfficePartnerResource> addPartner(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final HsOfficePartnerInsertResource body) { final HsOfficePartnerInsertResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entityToSave = createPartnerEntity(body); final var entityToSave = createPartnerEntity(body);
@ -86,11 +86,11 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficePartnerResource> getPartnerByUuid( public ResponseEntity<HsOfficePartnerResource> getPartnerByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID partnerUuid) { final UUID partnerUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = partnerRepo.findByUuid(partnerUuid); final var result = partnerRepo.findByUuid(partnerUuid);
if (result.isEmpty()) { if (result.isEmpty()) {
@ -102,10 +102,10 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deletePartnerByUuid( public ResponseEntity<Void> deletePartnerByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID partnerUuid) { final UUID partnerUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var partnerToDelete = partnerRepo.findByUuid(partnerUuid); final var partnerToDelete = partnerRepo.findByUuid(partnerUuid);
if (partnerToDelete.isEmpty()) { if (partnerToDelete.isEmpty()) {
@ -122,12 +122,12 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficePartnerResource> patchPartner( public ResponseEntity<HsOfficePartnerResource> patchPartner(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID partnerUuid, final UUID partnerUuid,
final HsOfficePartnerPatchResource body) { final HsOfficePartnerPatchResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var current = partnerRepo.findByUuid(partnerUuid).orElseThrow(); final var current = partnerRepo.findByUuid(partnerUuid).orElseThrow();
final var previousPartnerRel = current.getPartnerRel(); final var previousPartnerRel = current.getPartnerRel();

View File

@ -2,9 +2,9 @@ package net.hostsharing.hsadminng.hs.office.partner;
import lombok.*; import lombok.*;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
@ -13,14 +13,13 @@ import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(schema = "hs_office", name = "partner_details_rv") @Table(name = "hs_office_partner_details_rv")
@Getter @Getter
@Setter @Setter
@Builder @Builder
@ -71,9 +70,9 @@ public class HsOfficePartnerDetailsEntity implements BaseEntity<HsOfficePartnerD
return rbacViewFor("partnerDetails", HsOfficePartnerDetailsEntity.class) return rbacViewFor("partnerDetails", HsOfficePartnerDetailsEntity.class)
.withIdentityView(SQL.query(""" .withIdentityView(SQL.query("""
SELECT partnerDetails.uuid as uuid, partner_iv.idName as idName SELECT partnerDetails.uuid as uuid, partner_iv.idName as idName
FROM hs_office.partner_details AS partnerDetails FROM hs_office_partner_details AS partnerDetails
JOIN hs_office.partner partner ON partner.detailsUuid = partnerDetails.uuid JOIN hs_office_partner partner ON partner.detailsUuid = partnerDetails.uuid
JOIN hs_office.partner_iv partner_iv ON partner_iv.uuid = partner.uuid JOIN hs_office_partner_iv partner_iv ON partner_iv.uuid = partner.uuid
""")) """))
.withRestrictedViewOrderBy(SQL.expression("uuid")) .withRestrictedViewOrderBy(SQL.expression("uuid"))
.withUpdatableColumns( .withUpdatableColumns(
@ -83,7 +82,7 @@ public class HsOfficePartnerDetailsEntity implements BaseEntity<HsOfficePartnerD
"birthName", "birthName",
"birthday", "birthday",
"dateOfDeath") "dateOfDeath")
.toRole(GLOBAL, ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
// The grants are defined in HsOfficePartnerEntity.rbac() // The grants are defined in HsOfficePartnerEntity.rbac()
// because they have to be changed when its partnerRel changes, // because they have to be changed when its partnerRel changes,

View File

@ -10,10 +10,10 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContact;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelation;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFound;
@ -24,19 +24,18 @@ import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static jakarta.persistence.CascadeType.*; import static jakarta.persistence.CascadeType.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(schema = "hs_office", name = "partner_rv") @Table(name = "hs_office_partner_rv")
@Getter @Getter
@Setter @Setter
@Builder @Builder
@ -104,7 +103,7 @@ public class HsOfficePartnerEntity implements Stringifyable, BaseEntity<HsOffice
return rbacViewFor("partner", HsOfficePartnerEntity.class) return rbacViewFor("partner", HsOfficePartnerEntity.class)
.withIdentityView(SQL.projection("'P-' || partnerNumber")) .withIdentityView(SQL.projection("'P-' || partnerNumber"))
.withUpdatableColumns("partnerRelUuid") .withUpdatableColumns("partnerRelUuid")
.toRole(GLOBAL, ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.importRootEntityAliasProxy("partnerRel", HsOfficeRelationRbacEntity.class, .importRootEntityAliasProxy("partnerRel", HsOfficeRelationRbacEntity.class,
usingDefaultCase(), usingDefaultCase(),

View File

@ -31,10 +31,10 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficePersonResource>> listPersons( public ResponseEntity<List<HsOfficePersonResource>> listPersons(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final String caption) { final String caption) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = personRepo.findPersonByOptionalNameLike(caption); final var entities = personRepo.findPersonByOptionalNameLike(caption);
@ -45,11 +45,11 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficePersonResource> addPerson( public ResponseEntity<HsOfficePersonResource> addPerson(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final HsOfficePersonInsertResource body) { final HsOfficePersonInsertResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entityToSave = mapper.map(body, HsOfficePersonEntity.class); final var entityToSave = mapper.map(body, HsOfficePersonEntity.class);
@ -67,11 +67,11 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficePersonResource> getPersonByUuid( public ResponseEntity<HsOfficePersonResource> getPersonByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID personUuid) { final UUID personUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = personRepo.findByUuid(personUuid); final var result = personRepo.findByUuid(personUuid);
if (result.isEmpty()) { if (result.isEmpty()) {
@ -83,10 +83,10 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deletePersonByUuid( public ResponseEntity<Void> deletePersonByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID personUuid) { final UUID personUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = personRepo.deleteByUuid(personUuid); final var result = personRepo.deleteByUuid(personUuid);
if (result == 0) { if (result == 0) {
@ -99,12 +99,12 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficePersonResource> patchPerson( public ResponseEntity<HsOfficePersonResource> patchPerson(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID personUuid, final UUID personUuid,
final HsOfficePersonPatchResource body) { final HsOfficePersonPatchResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var current = personRepo.findByUuid(personUuid).orElseThrow(); final var current = personRepo.findByUuid(personUuid).orElseThrow();

View File

@ -3,9 +3,9 @@ package net.hostsharing.hsadminng.hs.office.person;
import lombok.*; import lombok.*;
import lombok.experimental.FieldNameConstants; import lombok.experimental.FieldNameConstants;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -14,15 +14,15 @@ import jakarta.persistence.*;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(schema = "hs_office", name = "person_rv") @Table(name = "hs_office_person_rv")
@Getter @Getter
@Setter @Setter
@Builder @Builder
@ -80,7 +80,7 @@ public class HsOfficePersonEntity implements BaseEntity<HsOfficePersonEntity>, S
return rbacViewFor("person", HsOfficePersonEntity.class) return rbacViewFor("person", HsOfficePersonEntity.class)
.withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)")) .withIdentityView(SQL.projection("concat(tradeName, familyName, givenName)"))
.withUpdatableColumns("personType", "title", "salutation", "tradeName", "givenName", "familyName") .withUpdatableColumns("personType", "title", "salutation", "tradeName", "givenName", "familyName")
.toRole(GLOBAL, GUEST).grantPermission(INSERT) .toRole("global", GUEST).grantPermission(INSERT)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.permission(DELETE); with.permission(DELETE);

View File

@ -5,7 +5,7 @@ import lombok.experimental.FieldNameConstants;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;

View File

@ -45,11 +45,11 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficeRelationResource>> listRelations( public ResponseEntity<List<HsOfficeRelationResource>> listRelations(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID personUuid, final UUID personUuid,
final HsOfficeRelationTypeResource relationType) { final HsOfficeRelationTypeResource relationType) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = relationRbacRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid, final var entities = relationRbacRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid,
mapper.map(relationType, HsOfficeRelationType.class)); mapper.map(relationType, HsOfficeRelationType.class));
@ -62,11 +62,11 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeRelationResource> addRelation( public ResponseEntity<HsOfficeRelationResource> addRelation(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final HsOfficeRelationInsertResource body) { final HsOfficeRelationInsertResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entityToSave = new HsOfficeRelationRbacEntity(); final var entityToSave = new HsOfficeRelationRbacEntity();
entityToSave.setType(HsOfficeRelationType.valueOf(body.getType())); entityToSave.setType(HsOfficeRelationType.valueOf(body.getType()));
@ -96,11 +96,11 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficeRelationResource> getRelationByUuid( public ResponseEntity<HsOfficeRelationResource> getRelationByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID relationUuid) { final UUID relationUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = relationRbacRepo.findByUuid(relationUuid); final var result = relationRbacRepo.findByUuid(relationUuid);
if (result.isEmpty()) { if (result.isEmpty()) {
@ -112,10 +112,10 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deleteRelationByUuid( public ResponseEntity<Void> deleteRelationByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID relationUuid) { final UUID relationUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = relationRbacRepo.deleteByUuid(relationUuid); final var result = relationRbacRepo.deleteByUuid(relationUuid);
if (result == 0) { if (result == 0) {
@ -128,12 +128,12 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeRelationResource> patchRelation( public ResponseEntity<HsOfficeRelationResource> patchRelation(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID relationUuid, final UUID relationUuid,
final HsOfficeRelationPatchResource body) { final HsOfficeRelationPatchResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var current = relationRbacRepo.findByUuid(relationUuid).orElseThrow(); final var current = relationRbacRepo.findByUuid(relationUuid).orElseThrow();

View File

@ -7,34 +7,34 @@ import lombok.experimental.SuperBuilder;
import net.hostsharing.hsadminng.errors.DisplayAs; import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity; import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRbacEntity;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.io.IOException; import java.io.IOException;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef.inCaseOf; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inCaseOf;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef.inOtherCases; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef.inOtherCases;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.DELETE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.OWNER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.REFERRER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.REFERRER;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.TENANT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Entity @Entity
@Table(schema = "hs_office", name = "relation_rv") @Table(name = "hs_office_relation_rv")
@NoArgsConstructor @NoArgsConstructor
@Getter @Getter
@Setter @Setter
@ -45,12 +45,12 @@ public class HsOfficeRelationRbacEntity extends HsOfficeRelation {
public static RbacView rbac() { public static RbacView rbac() {
return rbacViewFor("relation", HsOfficeRelationRbacEntity.class) return rbacViewFor("relation", HsOfficeRelationRbacEntity.class)
.withIdentityView(SQL.projection(""" .withIdentityView(SQL.projection("""
(select idName from hs_office.person_iv p where p.uuid = anchorUuid) (select idName from hs_office_person_iv p where p.uuid = anchorUuid)
|| '-with-' || target.type || '-' || '-with-' || target.type || '-'
|| (select idName from hs_office.person_iv p where p.uuid = holderUuid) || (select idName from hs_office_person_iv p where p.uuid = holderUuid)
""")) """))
.withRestrictedViewOrderBy(SQL.expression( .withRestrictedViewOrderBy(SQL.expression(
"(select idName from hs_office.person_iv p where p.uuid = target.holderUuid)")) "(select idName from hs_office_person_iv p where p.uuid = target.holderUuid)"))
.withUpdatableColumns("contactUuid") .withUpdatableColumns("contactUuid")
.importEntityAlias("anchorPerson", HsOfficePersonEntity.class, usingDefaultCase(), .importEntityAlias("anchorPerson", HsOfficePersonEntity.class, usingDefaultCase(),
dependsOnColumn("anchorUuid"), dependsOnColumn("anchorUuid"),

View File

@ -17,13 +17,13 @@ public interface HsOfficeRelationRbacRepository extends Repository<HsOfficeRelat
} }
@Query(value = """ @Query(value = """
SELECT p.* FROM hs_office.relation_rv AS p SELECT p.* FROM hs_office_relation_rv AS p
WHERE p.anchorUuid = :personUuid OR p.holderUuid = :personUuid WHERE p.anchorUuid = :personUuid OR p.holderUuid = :personUuid
""", nativeQuery = true) """, nativeQuery = true)
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuid(@NotNull UUID personUuid); List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuid(@NotNull UUID personUuid);
@Query(value = """ @Query(value = """
SELECT p.* FROM hs_office.relation_rv AS p SELECT p.* FROM hs_office_relation_rv AS p
WHERE (:relationType IS NULL OR p.type = cast(:relationType AS HsOfficeRelationType)) WHERE (:relationType IS NULL OR p.type = cast(:relationType AS HsOfficeRelationType))
AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid) AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid)
""", nativeQuery = true) """, nativeQuery = true)

View File

@ -11,7 +11,7 @@ import jakarta.persistence.Table;
@Entity @Entity
@Table(schema = "hs_office", name = "relation") @Table(name = "hs_office_relation")
@NoArgsConstructor @NoArgsConstructor
@Getter @Getter
@Setter @Setter

View File

@ -17,13 +17,13 @@ public interface HsOfficeRelationRealRepository extends Repository<HsOfficeRelat
} }
@Query(value = """ @Query(value = """
SELECT p.* FROM hs_office.relation AS p SELECT p.* FROM hs_office_relation AS p
WHERE p.anchorUuid = :personUuid OR p.holderUuid = :personUuid WHERE p.anchorUuid = :personUuid OR p.holderUuid = :personUuid
""", nativeQuery = true) """, nativeQuery = true)
List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuid(@NotNull UUID personUuid); List<HsOfficeRelationRealEntity> findRelationRelatedToPersonUuid(@NotNull UUID personUuid);
@Query(value = """ @Query(value = """
SELECT p.* FROM hs_office.relation AS p SELECT p.* FROM hs_office_relation AS p
WHERE (:relationType IS NULL OR p.type = cast(:relationType AS HsOfficeRelationType)) WHERE (:relationType IS NULL OR p.type = cast(:relationType AS HsOfficeRelationType))
AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid) AND ( p.anchorUuid = :personUuid OR p.holderUuid = :personUuid)
""", nativeQuery = true) """, nativeQuery = true)

View File

@ -39,10 +39,10 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<HsOfficeSepaMandateResource>> listSepaMandatesByIban( public ResponseEntity<List<HsOfficeSepaMandateResource>> listSepaMandatesByIban(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final String iban) { final String iban) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entities = sepaMandateRepo.findSepaMandateByOptionalIban(iban); final var entities = sepaMandateRepo.findSepaMandateByOptionalIban(iban);
@ -54,11 +54,11 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeSepaMandateResource> addSepaMandate( public ResponseEntity<HsOfficeSepaMandateResource> addSepaMandate(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final HsOfficeSepaMandateInsertResource body) { final HsOfficeSepaMandateInsertResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var entityToSave = mapper.map(body, HsOfficeSepaMandateEntity.class, SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER); final var entityToSave = mapper.map(body, HsOfficeSepaMandateEntity.class, SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER);
@ -77,11 +77,11 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<HsOfficeSepaMandateResource> getSepaMandateByUuid( public ResponseEntity<HsOfficeSepaMandateResource> getSepaMandateByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID sepaMandateUuid) { final UUID sepaMandateUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = sepaMandateRepo.findByUuid(sepaMandateUuid); final var result = sepaMandateRepo.findByUuid(sepaMandateUuid);
if (result.isEmpty()) { if (result.isEmpty()) {
@ -94,10 +94,10 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deleteSepaMandateByUuid( public ResponseEntity<Void> deleteSepaMandateByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID sepaMandateUuid) { final UUID sepaMandateUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = sepaMandateRepo.deleteByUuid(sepaMandateUuid); final var result = sepaMandateRepo.deleteByUuid(sepaMandateUuid);
if (result == 0) { if (result == 0) {
@ -110,12 +110,12 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<HsOfficeSepaMandateResource> patchSepaMandate( public ResponseEntity<HsOfficeSepaMandateResource> patchSepaMandate(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID sepaMandateUuid, final UUID sepaMandateUuid,
final HsOfficeSepaMandatePatchResource body) { final HsOfficeSepaMandatePatchResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var current = sepaMandateRepo.findByUuid(sepaMandateUuid).orElseThrow(); final var current = sepaMandateRepo.findByUuid(sepaMandateUuid).orElseThrow();

View File

@ -7,8 +7,8 @@ import net.hostsharing.hsadminng.errors.DisplayAs;
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity; import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountEntity;
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity;
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity; import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRbacEntity;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.stringify.Stringify; import net.hostsharing.hsadminng.stringify.Stringify;
import net.hostsharing.hsadminng.stringify.Stringifyable; import net.hostsharing.hsadminng.stringify.Stringifyable;
import org.hibernate.annotations.Type; import org.hibernate.annotations.Type;
@ -20,20 +20,20 @@ import java.util.UUID;
import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR; import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.DEBITOR;
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*; import static net.hostsharing.hsadminng.mapper.PostgresDateRange.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
import static net.hostsharing.hsadminng.stringify.Stringify.stringify; import static net.hostsharing.hsadminng.stringify.Stringify.stringify;
@Entity @Entity
@Table(schema = "hs_office", name = "sepamandate_rv") @Table(name = "hs_office_sepamandate_rv")
@Getter @Getter
@Setter @Setter
@Builder @Builder
@ -104,8 +104,8 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, BaseEntity<HsOf
return rbacViewFor("sepaMandate", HsOfficeSepaMandateEntity.class) return rbacViewFor("sepaMandate", HsOfficeSepaMandateEntity.class)
.withIdentityView(query(""" .withIdentityView(query("""
select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName select sm.uuid as uuid, ba.iban || '-' || sm.validity as idName
from hs_office.sepamandate sm from hs_office_sepamandate sm
join hs_office.bankaccount ba on ba.uuid = sm.bankAccountUuid join hs_office_bankaccount ba on ba.uuid = sm.bankAccountUuid
""")) """))
.withRestrictedViewOrderBy(expression("validity")) .withRestrictedViewOrderBy(expression("validity"))
.withUpdatableColumns("reference", "agreement", "validity") .withUpdatableColumns("reference", "agreement", "validity")
@ -114,8 +114,8 @@ public class HsOfficeSepaMandateEntity implements Stringifyable, BaseEntity<HsOf
dependsOnColumn("debitorUuid"), dependsOnColumn("debitorUuid"),
fetchedBySql(""" fetchedBySql("""
SELECT ${columns} SELECT ${columns}
FROM hs_office.relation debitorRel FROM hs_office_relation debitorRel
JOIN hs_office.debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid JOIN hs_office_debitor debitor ON debitor.debitorRelUuid = debitorRel.uuid
WHERE debitor.uuid = ${REF}.debitorUuid WHERE debitor.uuid = ${REF}.debitorUuid
"""), """),
NOT_NULL) NOT_NULL)

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -6,12 +6,12 @@ import java.util.function.BinaryOperator;
import java.util.stream.Stream; import java.util.stream.Stream;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.rbac.generator.PostgresTriggerReference.NEW; import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.ADMIN; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.GUEST; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.GUEST;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
import static org.apache.commons.lang3.StringUtils.capitalize; import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.apache.commons.lang3.StringUtils.uncapitalize; import static org.apache.commons.lang3.StringUtils.uncapitalize;
@ -46,7 +46,7 @@ public class InsertTriggerGenerator {
private void generateInsertPermissionGrants(final StringWriter plPgSql) { private void generateInsertPermissionGrants(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- ============================================================================ -- ============================================================================
--changeset InsertTriggerGenerator:${liquibaseTagPrefix}-rbac-GRANTING-INSERT-PERMISSION endDelimiter:--// --changeset ${liquibaseTagPrefix}-rbac-GRANTING-INSERT-PERMISSION:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
""", """,
with("liquibaseTagPrefix", liquibaseTagPrefix)); with("liquibaseTagPrefix", liquibaseTagPrefix));
@ -55,7 +55,7 @@ public class InsertTriggerGenerator {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- granting INSERT permission to ${rawSubTable} ---------------------------- -- granting INSERT permission to ${rawSubTable} ----------------------------
""", """,
with("rawSubTable", g.getSuperRoleDef().getEntityAlias().getRawTableNameWithSchema())); with("rawSubTable", g.getSuperRoleDef().getEntityAlias().getRawTableName()));
if (isGrantToADifferentTable(g)) { if (isGrantToADifferentTable(g)) {
plPgSql.writeLn( plPgSql.writeLn(
@ -67,13 +67,13 @@ public class InsertTriggerGenerator {
declare declare
row ${rawSuperTable}; row ${rawSuperTable};
begin begin
call base.defineContext('create INSERT INTO ${rawSubTable} permissions for pre-exising ${rawSuperTable} rows'); call defineContext('create INSERT INTO ${rawSubTable} permissions for pre-exising ${rawSuperTable} rows');
FOR row IN SELECT * FROM ${rawSuperTable} FOR row IN SELECT * FROM ${rawSuperTable}
${whenCondition} ${whenCondition}
LOOP LOOP
call rbac.grantPermissionToRole( call grantPermissionToRole(
rbac.createPermission(row.uuid, 'INSERT', '${rawSubTable}'), createPermission(row.uuid, 'INSERT', '${rawSubTable}'),
${superRoleRef}); ${superRoleRef});
END LOOP; END LOOP;
end; end;
@ -84,40 +84,40 @@ public class InsertTriggerGenerator {
? "WHERE type = '${value}'" ? "WHERE type = '${value}'"
.replace("${value}", g.getSuperRoleDef().getEntityAlias().usingCase().value) .replace("${value}", g.getSuperRoleDef().getEntityAlias().usingCase().value)
: "-- unconditional for all rows in that table"), : "-- unconditional for all rows in that table"),
with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableNameWithSchema()), with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableName()),
with("rawSubTable", g.getPermDef().getEntityAlias().getRawTableNameWithSchema()), with("rawSubTable", g.getPermDef().getEntityAlias().getRawTableName()),
with("superRoleRef", toRoleDescriptor(g.getSuperRoleDef(), "row"))); with("superRoleRef", toRoleDescriptor(g.getSuperRoleDef(), "row")));
} else { } else {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- Granting INSERT INTO hs_hosting_asset permissions to specified role of pre-existing hs_hosting_asset rows slipped, -- Granting INSERT INTO hs_hosting_asset permissions to specified role of pre-existing hs_hosting_asset rows slipped,
-- because there cannot yet be any pre-existing rows in the same table yet. -- because there cannot yet be any pre-existing rows in the same table yet.
""", """,
with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableNameWithSchema()), with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableName()),
with("rawSubTable", g.getPermDef().getEntityAlias().getRawTableNameWithSchema())); with("rawSubTable", g.getPermDef().getEntityAlias().getRawTableName()));
} }
plPgSql.writeLn(""" plPgSql.writeLn("""
/** /**
Grants ${rawSubTable} INSERT permission to specified role of new ${rawSuperTable} rows. Grants ${rawSubTable} INSERT permission to specified role of new ${rawSuperTable} rows.
*/ */
create or replace function ${rawSubTableSchemaPrefix}new_${rawSubTableShortName}_grants_insert_to_${rawSuperTableShortName}_tf() create or replace function new_${rawSubTable}_grants_insert_to_${rawSuperTable}_tf()
returns trigger returns trigger
language plpgsql language plpgsql
strict as $$ strict as $$
begin begin
${ifConditionThen} ${ifConditionThen}
call rbac.grantPermissionToRole( call grantPermissionToRole(
rbac.createPermission(NEW.uuid, 'INSERT', '${rawSubTable}'), createPermission(NEW.uuid, 'INSERT', '${rawSubTable}'),
${superRoleRef}); ${superRoleRef});
${ifConditionEnd} ${ifConditionEnd}
return NEW; return NEW;
end; $$; end; $$;
-- z_... is to put it at the end of after insert triggers, to make sure the roles exist -- z_... is to put it at the end of after insert triggers, to make sure the roles exist
create trigger z_new_${rawSubTableName}_grants_after_insert_tg create trigger z_new_${rawSubTable}_grants_insert_to_${rawSuperTable}_tg
after insert on ${rawSuperTableWithSchema} after insert on ${rawSuperTable}
for each row for each row
execute procedure ${rawSubTableSchemaPrefix}new_${rawSubTableShortName}_grants_insert_to_${rawSuperTableShortName}_tf(); execute procedure new_${rawSubTable}_grants_insert_to_${rawSuperTable}_tf();
""", """,
with("ifConditionThen", g.getSuperRoleDef().getEntityAlias().isCaseDependent() with("ifConditionThen", g.getSuperRoleDef().getEntityAlias().isCaseDependent()
// TODO.impl: .type needs to be dynamically generated // TODO.impl: .type needs to be dynamically generated
@ -127,13 +127,8 @@ public class InsertTriggerGenerator {
? "end if;" ? "end if;"
: "-- end."), : "-- end."),
with("superRoleRef", toRoleDescriptor(g.getSuperRoleDef(), NEW.name())), with("superRoleRef", toRoleDescriptor(g.getSuperRoleDef(), NEW.name())),
with("rawSuperTableWithSchema", g.getSuperRoleDef().getEntityAlias().getRawTableNameWithSchema()),
with("rawSuperTableShortName", g.getSuperRoleDef().getEntityAlias().getRawTableShortName()),
with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableName()), with("rawSuperTable", g.getSuperRoleDef().getEntityAlias().getRawTableName()),
with("rawSubTable", g.getPermDef().getEntityAlias().getRawTableNameWithSchema()), with("rawSubTable", g.getPermDef().getEntityAlias().getRawTableName()));
with("rawSubTableSchemaPrefix", g.getPermDef().getEntityAlias().getRawTableSchemaPrefix()),
with("rawSubTableName", g.getPermDef().getEntityAlias().getRawTableName()),
with("rawSubTableShortName", g.getPermDef().getEntityAlias().getRawTableShortName()));
}); });
} }
@ -141,7 +136,7 @@ public class InsertTriggerGenerator {
private void generateInsertPermissionTriggerAlwaysDisallow(final StringWriter plPgSql) { private void generateInsertPermissionTriggerAlwaysDisallow(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- ============================================================================ -- ============================================================================
--changeset InsertTriggerGenerator:${liquibaseTagPrefix}-rbac-ALWAYS-DISALLOW-INSERT endDelimiter:--// --changeset ${liquibaseTagPrefix}-rbac-ALWAYS-DISALLOW-INSERT:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
""", """,
with("liquibaseTagPrefix", liquibaseTagPrefix)); with("liquibaseTagPrefix", liquibaseTagPrefix));
@ -155,15 +150,14 @@ public class InsertTriggerGenerator {
returns trigger returns trigger
language plpgsql as $$ language plpgsql as $$
begin begin
raise exception '[403] insert into ${rawSubTableWithSchema} values(%) not allowed regardless of current subject, no insert permissions granted at all', NEW; raise exception '[403] insert into ${rawSubTable} values(%) not allowed regardless of current subject, no insert permissions granted at all', NEW;
end; $$; end; $$;
create trigger ${rawSubTable}_insert_permission_check_tg create trigger ${rawSubTable}_insert_permission_check_tg
before insert on ${rawSubTable} before insert on ${rawSubTable}
for each row for each row
execute procedure ${rawSubTableWithSchema}_insert_permission_missing_tf(); execute procedure ${rawSubTable}_insert_permission_missing_tf();
""", """,
with("rawSubTableWithSchema", rbacDef.getRootEntityAlias().getRawTableNameWithSchema()),
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
plPgSql.writeLn("--//"); plPgSql.writeLn("--//");
@ -185,7 +179,7 @@ public class InsertTriggerGenerator {
private void generateInsertPermissionsCheckHeader(final StringWriter plPgSql) { private void generateInsertPermissionsCheckHeader(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- ============================================================================ -- ============================================================================
--changeset InsertTriggerGenerator:${liquibaseTagPrefix}-rbac-CHECKING-INSERT-PERMISSION endDelimiter:--// --changeset ${rawSubTable}-rbac-CHECKING-INSERT-PERMISSION:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/** /**
@ -198,8 +192,7 @@ public class InsertTriggerGenerator {
superObjectUuid uuid; superObjectUuid uuid;
begin begin
""", """,
with("liquibaseTagPrefix", liquibaseTagPrefix), with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableNameWithSchema()));
plPgSql.chopEmptyLines(); plPgSql.chopEmptyLines();
} }
@ -213,7 +206,7 @@ public class InsertTriggerGenerator {
if (g.getSuperRoleDef().isGlobal(GUEST)) { if (g.getSuperRoleDef().isGlobal(GUEST)) {
plPgSql.writeLn( plPgSql.writeLn(
""" """
-- check INSERT permission for rbac.global anyone -- check INSERT INSERT permission for global anyone
if ${caseCondition}true then if ${caseCondition}true then
return NEW; return NEW;
end if; end if;
@ -222,8 +215,8 @@ public class InsertTriggerGenerator {
} else if (g.getSuperRoleDef().isGlobal(ADMIN)) { } else if (g.getSuperRoleDef().isGlobal(ADMIN)) {
plPgSql.writeLn( plPgSql.writeLn(
""" """
-- check INSERT permission if rbac.global ADMIN -- check INSERT INSERT if global ADMIN
if ${caseCondition}rbac.isGlobalAdmin() then if ${caseCondition}isGlobalAdmin() then
return NEW; return NEW;
end if; end if;
""", """,
@ -232,25 +225,25 @@ public class InsertTriggerGenerator {
plPgSql.writeLn( plPgSql.writeLn(
""" """
-- check INSERT permission via direct foreign key: NEW.${refColumn} -- check INSERT permission via direct foreign key: NEW.${refColumn}
if ${caseCondition}rbac.hasInsertPermission(NEW.${refColumn}, '${rawSubTable}') then if ${caseCondition}hasInsertPermission(NEW.${refColumn}, '${rawSubTable}') then
return NEW; return NEW;
end if; end if;
""", """,
with("caseCondition", caseCondition), with("caseCondition", caseCondition),
with("refColumn", superRoleEntityAlias.dependsOnColumName()), with("refColumn", superRoleEntityAlias.dependsOnColumName()),
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableNameWithSchema())); with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
} else { } else {
plPgSql.writeLn( plPgSql.writeLn(
""" """
-- check INSERT permission via indirect foreign key: NEW.${refColumn} -- check INSERT permission via indirect foreign key: NEW.${refColumn}
superObjectUuid := (${fetchSql}); superObjectUuid := (${fetchSql});
assert superObjectUuid is not null, 'object uuid fetched depending on ${rawSubTable}.${refColumn} must not be null, also check fetchSql in RBAC DSL'; assert superObjectUuid is not null, 'object uuid fetched depending on ${rawSubTable}.${refColumn} must not be null, also check fetchSql in RBAC DSL';
if ${caseCondition}rbac.hasInsertPermission(superObjectUuid, '${rawSubTable}') then if ${caseCondition}hasInsertPermission(superObjectUuid, '${rawSubTable}') then
return NEW; return NEW;
end if; end if;
""", """,
with("caseCondition", caseCondition), with("caseCondition", caseCondition),
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableNameWithSchema()), with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()),
with("refColumn", superRoleEntityAlias.dependsOnColumName()), with("refColumn", superRoleEntityAlias.dependsOnColumName()),
with("fetchSql", g.getSuperRoleDef().getEntityAlias().fetchSql().sql), with("fetchSql", g.getSuperRoleDef().getEntityAlias().fetchSql().sql),
with("columns", g.getSuperRoleDef().getEntityAlias().aliasName() + ".uuid"), with("columns", g.getSuperRoleDef().getEntityAlias().aliasName() + ".uuid"),
@ -261,17 +254,16 @@ public class InsertTriggerGenerator {
private void generateInsertPermissionsChecksFooter(final StringWriter plPgSql) { private void generateInsertPermissionsChecksFooter(final StringWriter plPgSql) {
plPgSql.writeLn(); plPgSql.writeLn();
plPgSql.writeLn(""" plPgSql.writeLn("""
raise exception '[403] insert into ${rawSubTableWithSchema} values(%) not allowed for current subjects % (%)', raise exception '[403] insert into ${rawSubTable} values(%) not allowed for current subjects % (%)',
NEW, base.currentSubjects(), rbac.currentSubjectOrAssumedRolesUuids(); NEW, currentSubjects(), currentSubjectsUuids();
end; $$; end; $$;
create trigger ${rawSubTable}_insert_permission_check_tg create trigger ${rawSubTable}_insert_permission_check_tg
before insert on ${rawSubTableWithSchema} before insert on ${rawSubTable}
for each row for each row
execute procedure ${rawSubTableWithSchema}_insert_permission_check_tf(); execute procedure ${rawSubTable}_insert_permission_check_tf();
--// --//
""", """,
with("rawSubTableWithSchema", rbacDef.getRootEntityAlias().getRawTableNameWithSchema()),
with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName())); with("rawSubTable", rbacDef.getRootEntityAlias().getRawTableName()));
} }
@ -280,7 +272,7 @@ public class InsertTriggerGenerator {
} }
private boolean isGrantToADifferentTable(final RbacView.RbacGrantDefinition g) { private boolean isGrantToADifferentTable(final RbacView.RbacGrantDefinition g) {
return !rbacDef.getRootEntityAlias().getRawTableNameWithSchema().equals(g.getSuperRoleDef().getEntityAlias().getRawTableNameWithSchema()); return !rbacDef.getRootEntityAlias().getRawTableName().equals(g.getSuperRoleDef().getEntityAlias().getRawTableName());
} }
private Stream<RbacView.RbacGrantDefinition> getInsertGrants() { private Stream<RbacView.RbacGrantDefinition> getInsertGrants() {

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
public enum PostgresTriggerReference { public enum PostgresTriggerReference {
NEW, OLD NEW, OLD

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
public class RbacIdentityViewGenerator { public class RbacIdentityViewGenerator {
private final RbacView rbacDef; private final RbacView rbacDef;
@ -12,13 +12,13 @@ public class RbacIdentityViewGenerator {
this.rbacDef = rbacDef; this.rbacDef = rbacDef;
this.liquibaseTagPrefix = liquibaseTagPrefix; this.liquibaseTagPrefix = liquibaseTagPrefix;
this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName(); this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
this.rawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema(); this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
} }
void generateTo(final StringWriter plPgSql) { void generateTo(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- ============================================================================ -- ============================================================================
--changeset RbacIdentityViewGenerator:${liquibaseTagPrefix}-rbac-IDENTITY-VIEW endDelimiter:--// --changeset ${liquibaseTagPrefix}-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
""", """,
with("liquibaseTagPrefix", liquibaseTagPrefix)); with("liquibaseTagPrefix", liquibaseTagPrefix));
@ -26,13 +26,13 @@ public class RbacIdentityViewGenerator {
plPgSql.writeLn( plPgSql.writeLn(
switch (rbacDef.getIdentityViewSqlQuery().part) { switch (rbacDef.getIdentityViewSqlQuery().part) {
case SQL_PROJECTION -> """ case SQL_PROJECTION -> """
call rbac.generateRbacIdentityViewFromProjection('${rawTableName}', call generateRbacIdentityViewFromProjection('${rawTableName}',
$idName$ $idName$
${identityViewSqlPart} ${identityViewSqlPart}
$idName$); $idName$);
"""; """;
case SQL_QUERY -> """ case SQL_QUERY -> """
call rbac.generateRbacIdentityViewFromQuery('${rawTableName}', call generateRbacIdentityViewFromQuery('${rawTableName}',
$idName$ $idName$
${identityViewSqlPart} ${identityViewSqlPart}
$idName$); $idName$);

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
public class RbacObjectGenerator { public class RbacObjectGenerator {
@ -9,15 +9,15 @@ public class RbacObjectGenerator {
public RbacObjectGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { public RbacObjectGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
this.liquibaseTagPrefix = liquibaseTagPrefix; this.liquibaseTagPrefix = liquibaseTagPrefix;
this.rawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema(); this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
} }
void generateTo(final StringWriter plPgSql) { void generateTo(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- ============================================================================ -- ============================================================================
--changeset RbacObjectGenerator:${liquibaseTagPrefix}-rbac-OBJECT endDelimiter:--// --changeset ${liquibaseTagPrefix}-rbac-OBJECT:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call rbac.generateRelatedRbacObject('${rawTableName}'); call generateRelatedRbacObject('${rawTableName}');
--// --//
""", """,

View File

@ -1,9 +1,9 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.indented; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.indented;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
public class RbacRestrictedViewGenerator { public class RbacRestrictedViewGenerator {
private final RbacView rbacDef; private final RbacView rbacDef;
@ -13,15 +13,15 @@ public class RbacRestrictedViewGenerator {
public RbacRestrictedViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { public RbacRestrictedViewGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
this.rbacDef = rbacDef; this.rbacDef = rbacDef;
this.liquibaseTagPrefix = liquibaseTagPrefix; this.liquibaseTagPrefix = liquibaseTagPrefix;
this.rawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema(); this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
} }
void generateTo(final StringWriter plPgSql) { void generateTo(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- ============================================================================ -- ============================================================================
--changeset RbacRestrictedViewGenerator:${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW endDelimiter:--// --changeset ${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call rbac.generateRbacRestrictedView('${rawTableName}', call generateRbacRestrictedView('${rawTableName}',
$orderBy$ $orderBy$
${orderBy} ${orderBy}
$orderBy$, $orderBy$,

View File

@ -1,6 +1,6 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
public class RbacRoleDescriptorsGenerator { public class RbacRoleDescriptorsGenerator {
@ -11,15 +11,15 @@ public class RbacRoleDescriptorsGenerator {
public RbacRoleDescriptorsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { public RbacRoleDescriptorsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
this.liquibaseTagPrefix = liquibaseTagPrefix; this.liquibaseTagPrefix = liquibaseTagPrefix;
this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName(); this.simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
this.rawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema(); this.rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
} }
void generateTo(final StringWriter plPgSql) { void generateTo(final StringWriter plPgSql) {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- ============================================================================ -- ============================================================================
--changeset RbacRoleDescriptorsGenerator:${liquibaseTagPrefix}-rbac-ROLE-DESCRIPTORS endDelimiter:--// --changeset ${liquibaseTagPrefix}-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
call rbac.generateRbacRoleDescriptors('${simpleEntityVarName}', '${rawTableName}'); call generateRbacRoleDescriptors('${simpleEntityVarName}', '${rawTableName}');
--// --//
""", """,

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter; import lombok.Getter;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import org.reflections.Reflections; import org.reflections.Reflections;
import org.reflections.scanners.TypeAnnotationsScanner; import org.reflections.scanners.TypeAnnotationsScanner;
@ -12,6 +12,7 @@ import jakarta.persistence.Version;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -22,12 +23,12 @@ import static java.util.Arrays.asList;
import static java.util.Arrays.stream; import static java.util.Arrays.stream;
import static java.util.Collections.max; import static java.util.Collections.max;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.PERM_TO_ROLE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition.GrantType.ROLE_TO_ROLE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.ROLE_TO_ROLE;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.Part.AUTO_FETCH; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.Part.AUTO_FETCH;
import static org.apache.commons.collections4.SetUtils.hashSet; import static org.apache.commons.collections4.SetUtils.hashSet;
import static org.apache.commons.lang3.StringUtils.uncapitalize; import static org.apache.commons.lang3.StringUtils.uncapitalize;
@ -35,12 +36,12 @@ import static org.apache.commons.lang3.StringUtils.uncapitalize;
// TODO.refa: rename to RbacDSL // TODO.refa: rename to RbacDSL
public class RbacView { public class RbacView {
public static final String GLOBAL = "rbac.global"; public static final String GLOBAL = "global";
public static final String OUTPUT_BASEDIR = "src/main/resources/db/changelog"; public static final String OUTPUT_BASEDIR = "src/main/resources/db/changelog";
private final EntityAlias rootEntityAlias; private final EntityAlias rootEntityAlias;
private final Set<RbacSubjectReference> userDefs = new LinkedHashSet<>(); private final Set<RbacUserReference> userDefs = new LinkedHashSet<>();
private final Set<RbacRoleDefinition> roleDefs = new LinkedHashSet<>(); private final Set<RbacRoleDefinition> roleDefs = new LinkedHashSet<>();
private final Set<RbacPermissionDefinition> permDefs = new LinkedHashSet<>(); private final Set<RbacPermissionDefinition> permDefs = new LinkedHashSet<>();
private final Map<String, EntityAlias> entityAliases = new HashMap<>() { private final Map<String, EntityAlias> entityAliases = new HashMap<>() {
@ -89,15 +90,15 @@ public class RbacView {
* @param <E> * @param <E>
* a JPA entity class extending RbacObject * a JPA entity class extending RbacObject
*/ */
public static <E extends BaseEntity<?>> RbacView rbacViewFor(final String alias, final Class<E> entityClass) { public static <E extends BaseEntity> RbacView rbacViewFor(final String alias, final Class<E> entityClass) {
return new RbacView(alias, entityClass); return new RbacView(alias, entityClass);
} }
RbacView(final String alias, final Class<? extends BaseEntity<?>> entityClass) { RbacView(final String alias, final Class<? extends BaseEntity> entityClass) {
rootEntityAlias = new EntityAlias(alias, entityClass); rootEntityAlias = new EntityAlias(alias, entityClass);
entityAliases.put(alias, rootEntityAlias); entityAliases.put(alias, rootEntityAlias);
new RbacSubjectReference(CREATOR); new RbacUserReference(CREATOR);
entityAliases.put("rbac.global", new EntityAlias("rbac.global")); entityAliases.put("global", new EntityAlias("global"));
} }
/** /**
@ -120,7 +121,7 @@ public class RbacView {
* <p>An identity view is a view which maps an objectUuid to an idName. * <p>An identity view is a view which maps an objectUuid to an idName.
* The idName should be a human-readable representation of the row, but as short as possible. * The idName should be a human-readable representation of the row, but as short as possible.
* The idName must only consist of letters (A-Z, a-z), digits (0-9), dash (-), dot (.) and unserscore '_'. * The idName must only consist of letters (A-Z, a-z), digits (0-9), dash (-), dot (.) and unserscore '_'.
* It's used to create the object-specific-role-names like rbactest.customer#abc:ADMIN - here 'abc' is the idName. * It's used to create the object-specific-role-names like test_customer#abc:ADMIN - here 'abc' is the idName.
* The idName not necessarily unique in a table, but it should be avoided. * The idName not necessarily unique in a table, but it should be avoided.
* </p> * </p>
* *
@ -286,9 +287,9 @@ public class RbacView {
* @param <EC> * @param <EC>
* a JPA entity class extending RbacObject * a JPA entity class extending RbacObject
*/ */
public <EC extends BaseEntity<?>> RbacView importRootEntityAliasProxy( public <EC extends BaseEntity> RbacView importRootEntityAliasProxy(
final String aliasName, final String aliasName,
final Class<? extends BaseEntity<?>> entityClass, final Class<? extends BaseEntity> entityClass,
final ColumnValue forCase, final ColumnValue forCase,
final SQL fetchSql, final SQL fetchSql,
final Column dependsOnColum) { final Column dependsOnColum) {
@ -312,7 +313,7 @@ public class RbacView {
* a JPA entity class extending RbacObject * a JPA entity class extending RbacObject
*/ */
public RbacView importSubEntityAlias( public RbacView importSubEntityAlias(
final String aliasName, final Class<? extends BaseEntity<?>> entityClass, final String aliasName, final Class<? extends BaseEntity> entityClass,
final SQL fetchSql, final Column dependsOnColum) { final SQL fetchSql, final Column dependsOnColum) {
importEntityAliasImpl(aliasName, entityClass, usingDefaultCase(), fetchSql, dependsOnColum, true, NOT_NULL); importEntityAliasImpl(aliasName, entityClass, usingDefaultCase(), fetchSql, dependsOnColum, true, NOT_NULL);
return this; return this;
@ -349,14 +350,14 @@ public class RbacView {
* a JPA entity class extending RbacObject * a JPA entity class extending RbacObject
*/ */
public RbacView importEntityAlias( public RbacView importEntityAlias(
final String aliasName, final Class<? extends BaseEntity<?>> entityClass, final ColumnValue usingCase, final String aliasName, final Class<? extends BaseEntity> entityClass, final ColumnValue usingCase,
final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) { final Column dependsOnColum, final SQL fetchSql, final Nullable nullable) {
importEntityAliasImpl(aliasName, entityClass, usingCase, fetchSql, dependsOnColum, false, nullable); importEntityAliasImpl(aliasName, entityClass, usingCase, fetchSql, dependsOnColum, false, nullable);
return this; return this;
} }
private EntityAlias importEntityAliasImpl( private EntityAlias importEntityAliasImpl(
final String aliasName, final Class<? extends BaseEntity<?>> entityClass, final ColumnValue usingCase, final String aliasName, final Class<? extends BaseEntity> entityClass, final ColumnValue usingCase,
final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) { final SQL fetchSql, final Column dependsOnColum, boolean asSubEntity, final Nullable nullable) {
final var entityAlias = ofNullable(entityAliases.get(aliasName)) final var entityAlias = ofNullable(entityAliases.get(aliasName))
@ -466,7 +467,7 @@ public class RbacView {
return new RbacExampleRole(entityAlias, role); return new RbacExampleRole(entityAlias, role);
} }
private RbacGrantDefinition grantRoleToSubject(final RbacRoleDefinition roleDefinition, final RbacSubjectReference user) { private RbacGrantDefinition grantRoleToUser(final RbacRoleDefinition roleDefinition, final RbacUserReference user) {
return findOrCreateGrantDef(roleDefinition, user).toCreate(); return findOrCreateGrantDef(roleDefinition, user).toCreate();
} }
@ -547,7 +548,7 @@ public class RbacView {
} }
public RbacView grantPermission(final Permission perm) { public RbacView grantPermission(final Permission perm) {
final var forTable = rootEntityAlias.getRawTableNameWithSchema(); final var forTable = rootEntityAlias.getRawTableName();
findOrCreateGrantDef(findRbacPerm(rootEntityAlias, perm, forTable), superRoleDef).toCreate(); findOrCreateGrantDef(findRbacPerm(rootEntityAlias, perm, forTable), superRoleDef).toCreate();
return RbacView.this; return RbacView.this;
} }
@ -563,7 +564,7 @@ public class RbacView {
@EqualsAndHashCode @EqualsAndHashCode
public class RbacGrantDefinition { public class RbacGrantDefinition {
private final RbacSubjectReference userDef; private final RbacUserReference userDef;
private final RbacRoleDefinition superRoleDef; private final RbacRoleDefinition superRoleDef;
private final RbacRoleDefinition subRoleDef; private final RbacRoleDefinition subRoleDef;
private final RbacPermissionDefinition permDef; private final RbacPermissionDefinition permDef;
@ -604,7 +605,7 @@ public class RbacView {
register(this); register(this);
} }
public RbacGrantDefinition(final RbacRoleDefinition roleDef, final RbacSubjectReference userDef) { public RbacGrantDefinition(final RbacRoleDefinition roleDef, final RbacUserReference userDef) {
this.userDef = userDef; this.userDef = userDef;
this.subRoleDef = roleDef; this.subRoleDef = roleDef;
this.superRoleDef = null; this.superRoleDef = null;
@ -769,8 +770,8 @@ public class RbacView {
* @return * @return
* The grant definition for further chained calls. * The grant definition for further chained calls.
*/ */
public RbacGrantDefinition owningUser(final RbacSubjectReference.UserRole userRole) { public RbacGrantDefinition owningUser(final RbacUserReference.UserRole userRole) {
return grantRoleToSubject(this, findUserRef(userRole)); return grantRoleToUser(this, findUserRef(userRole));
} }
/** /**
@ -832,12 +833,12 @@ public class RbacView {
} }
} }
public RbacSubjectReference findUserRef(final RbacSubjectReference.UserRole userRole) { public RbacUserReference findUserRef(final RbacUserReference.UserRole userRole) {
return userDefs.stream().filter(u -> u.role == userRole).findFirst().orElseThrow(); return userDefs.stream().filter(u -> u.role == userRole).findFirst().orElseThrow();
} }
@EqualsAndHashCode @EqualsAndHashCode
public class RbacSubjectReference { public class RbacUserReference {
public enum UserRole { public enum UserRole {
GLOBAL_ADMIN, GLOBAL_ADMIN,
@ -846,7 +847,7 @@ public class RbacView {
final UserRole role; final UserRole role;
public RbacSubjectReference(final UserRole creator) { public RbacUserReference(final UserRole creator) {
this.role = creator; this.role = creator;
userDefs.add(this); userDefs.add(this);
} }
@ -884,7 +885,7 @@ public class RbacView {
.orElseGet(() -> new RbacPermissionDefinition(entityAlias, perm, tableName, true)); // TODO: true => toCreate .orElseGet(() -> new RbacPermissionDefinition(entityAlias, perm, tableName, true)); // TODO: true => toCreate
} }
private RbacGrantDefinition findOrCreateGrantDef(final RbacRoleDefinition roleDefinition, final RbacSubjectReference user) { private RbacGrantDefinition findOrCreateGrantDef(final RbacRoleDefinition roleDefinition, final RbacUserReference user) {
return grantDefs.stream() return grantDefs.stream()
.filter(g -> g.subRoleDef == roleDefinition && g.userDef == user) .filter(g -> g.subRoleDef == roleDefinition && g.userDef == user)
.findFirst() .findFirst()
@ -910,18 +911,18 @@ public class RbacView {
return distinctGrantDef; return distinctGrantDef;
} }
record EntityAlias(String aliasName, Class<? extends BaseEntity<?>> entityClass, ColumnValue usingCase, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) { record EntityAlias(String aliasName, Class<? extends BaseEntity> entityClass, ColumnValue usingCase, SQL fetchSql, Column dependsOnColum, boolean isSubEntity, Nullable nullable) {
public EntityAlias(final String aliasName) { public EntityAlias(final String aliasName) {
this(aliasName, null, null, null, null, false, null); this(aliasName, null, null, null, null, false, null);
} }
public EntityAlias(final String aliasName, final Class<? extends BaseEntity<?>> entityClass) { public EntityAlias(final String aliasName, final Class<? extends BaseEntity> entityClass) {
this(aliasName, entityClass, null, null, null, false, null); this(aliasName, entityClass, null, null, null, false, null);
} }
boolean isGlobal() { boolean isGlobal() {
return aliasName().equals("rbac.global"); return aliasName().equals("global");
} }
boolean isPlaceholder() { boolean isPlaceholder() {
@ -936,7 +937,7 @@ public class RbacView {
return switch (fetchSql.part) { return switch (fetchSql.part) {
case SQL_QUERY -> fetchSql; case SQL_QUERY -> fetchSql;
case AUTO_FETCH -> case AUTO_FETCH ->
SQL.query("SELECT * FROM " + getRawTableNameWithSchema() + " WHERE uuid = ${ref}." + dependsOnColum.column); SQL.query("SELECT * FROM " + getRawTableName() + " WHERE uuid = ${ref}." + dependsOnColum.column);
default -> throw new IllegalStateException("unexpected SQL definition: " + fetchSql); default -> throw new IllegalStateException("unexpected SQL definition: " + fetchSql);
}; };
} }
@ -959,37 +960,11 @@ public class RbacView {
: uncapitalize(withoutEntitySuffix(entityClass.getSimpleName())); : uncapitalize(withoutEntitySuffix(entityClass.getSimpleName()));
} }
String getRawTableNameWithSchema() {
if ( aliasName.equals("rbac.global")) {
return "rbac.global"; // TODO: maybe we should introduce a GlobalEntity class?
}
return qualifiedRealTableName(entityClass);
}
String getRawTableSchemaPrefix() {
final var rawTableNameWithSchema = getRawTableNameWithSchema();
final var parts = rawTableNameWithSchema.split("\\.");
final var rawTableSchemaPrefix = parts.length > 1 ? parts[0] + "." : "";
return rawTableSchemaPrefix;
}
String getRawTableName() { String getRawTableName() {
final var rawTableNameWithSchema = getRawTableNameWithSchema(); if ( aliasName.equals("global")) {
final var parts = rawTableNameWithSchema.split("\\."); return "global"; // TODO: maybe we should introduce a GlobalEntity class?
final var rawTableName = parts.length > 1 ? parts[1] : rawTableNameWithSchema; }
return rawTableName; return withoutRvSuffix(entityClass.getAnnotation(Table.class).name());
}
String getRawTableShortName() {
// TODO.impl: some combined function and trigger names are too long
// maybe we should shorten the table name e.g. hs_office.coopsharestransaction -> hsof.coopsharetx
// this is just a workaround:
return getRawTableName()
.replace("hs_office.", "hsof.")
.replace("hs_booking_", "hsbk_")
.replace("hs_hosting_", "hsho_")
.replace("coopsharestransaction", "coopsharetx")
.replace("coopassetstransaction", "coopassettx");
} }
String dependsOnColumName() { String dependsOnColumName() {
@ -1009,12 +984,8 @@ public class RbacView {
} }
} }
public static String qualifiedRealTableName(final Class<? extends BaseEntity<?>> entityClass) { public static String withoutRvSuffix(final String tableName) {
final var tableAnnotation = entityClass.getAnnotation(Table.class); return tableName.substring(0, tableName.length() - "_rv".length());
final var schema = tableAnnotation.schema();
final var tableName = tableAnnotation.name();
final var realTableName = tableName.substring(0, tableName.length() - "_rv".length());
return (schema.isEmpty() ? "" : (schema + ".")) + realTableName;
} }
public enum Role { public enum Role {
@ -1195,7 +1166,7 @@ public class RbacView {
} }
String map(final String originalAliasName) { String map(final String originalAliasName) {
if (outerAliasNames.contains(originalAliasName) || originalAliasName.equals("rbac.global")) { if (outerAliasNames.contains(originalAliasName) || originalAliasName.equals("global")) {
return originalAliasName; return originalAliasName;
} }
if (originalAliasName.equals(importedRbacView.rootEntityAlias.aliasName)) { if (originalAliasName.equals(importedRbacView.rootEntityAlias.aliasName)) {
@ -1273,14 +1244,13 @@ public class RbacView {
public static Set<Class<? extends BaseEntity>> findRbacEntityClasses(String packageName) { public static Set<Class<? extends BaseEntity>> findRbacEntityClasses(String packageName) {
final var reflections = new Reflections(packageName, TypeAnnotationsScanner.class); final var reflections = new Reflections(packageName, TypeAnnotationsScanner.class);
final Set<Class<? extends BaseEntity>> rbacEntityClasses = reflections.getTypesAnnotatedWith(Entity.class).stream() return reflections.getTypesAnnotatedWith(Entity.class).stream()
.filter(BaseEntity.class::isAssignableFrom) .filter(c -> stream(c.getInterfaces()).anyMatch(i -> i== BaseEntity.class))
.filter(c -> stream(c.getDeclaredMethods()) .filter(c -> stream(c.getDeclaredMethods())
.anyMatch(m -> m.getName().equals("rbac") && isStatic(m.getModifiers())) .anyMatch(m -> m.getName().equals("rbac") && Modifier.isStatic(m.getModifiers()))
) )
.map(RbacView::castToSubclassOfBaseEntity) .map(RbacView::castToSubclassOfBaseEntity)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
return rbacEntityClasses;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -1,7 +1,7 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.nio.file.*; import java.nio.file.*;
@ -12,7 +12,7 @@ import java.util.stream.Stream;
import static java.util.Comparator.comparing; import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition.GrantType.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
public class RbacViewMermaidFlowchartGenerator { public class RbacViewMermaidFlowchartGenerator {

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import lombok.SneakyThrows; import lombok.SneakyThrows;
@ -6,8 +6,8 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import static net.hostsharing.hsadminng.rbac.generator.PostgresTriggerReference.NEW; import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
public class RbacViewPostgresGenerator { public class RbacViewPostgresGenerator {
@ -17,7 +17,7 @@ public class RbacViewPostgresGenerator {
public RbacViewPostgresGenerator(final RbacView forRbacDef) { public RbacViewPostgresGenerator(final RbacView forRbacDef) {
rbacDef = forRbacDef; rbacDef = forRbacDef;
liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableNameWithSchema().replace("_", "-").replace(".", "-"); liqibaseTagPrefix = rbacDef.getRootEntityAlias().getRawTableName().replace("_", "-");
plPgSql.writeLn(""" plPgSql.writeLn("""
--liquibase formatted sql --liquibase formatted sql
-- This code generated was by ${generator}, do not amend manually. -- This code generated was by ${generator}, do not amend manually.

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import net.hostsharing.hsadminng.rbac.generator.RbacView.CaseDef; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.CaseDef;
import net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition;
import net.hostsharing.hsadminng.rbac.generator.RbacView.RbacPermissionDefinition; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacPermissionDefinition;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -13,12 +13,12 @@ import java.util.stream.Stream;
import static java.util.Optional.ofNullable; import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toSet; import static java.util.stream.Collectors.toSet;
import static net.hostsharing.hsadminng.rbac.generator.PostgresTriggerReference.NEW; import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.NEW;
import static net.hostsharing.hsadminng.rbac.generator.PostgresTriggerReference.OLD; import static net.hostsharing.hsadminng.rbac.rbacdef.PostgresTriggerReference.OLD;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacGrantDefinition.GrantType.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacGrantDefinition.GrantType.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.StringWriter.with; import static net.hostsharing.hsadminng.rbac.rbacdef.StringWriter.with;
import static org.apache.commons.lang3.StringUtils.capitalize; import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.apache.commons.lang3.StringUtils.uncapitalize; import static org.apache.commons.lang3.StringUtils.uncapitalize;
@ -29,7 +29,7 @@ class RolesGrantsAndPermissionsGenerator {
private final String liquibaseTagPrefix; private final String liquibaseTagPrefix;
private final String simpleEntityName; private final String simpleEntityName;
private final String simpleEntityVarName; private final String simpleEntityVarName;
private final String qualifiedRawTableName; private final String rawTableName;
RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) { RolesGrantsAndPermissionsGenerator(final RbacView rbacDef, final String liquibaseTagPrefix) {
this.rbacDef = rbacDef; this.rbacDef = rbacDef;
@ -40,7 +40,7 @@ class RolesGrantsAndPermissionsGenerator {
simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName(); simpleEntityVarName = rbacDef.getRootEntityAlias().simpleName();
simpleEntityName = capitalize(simpleEntityVarName); simpleEntityName = capitalize(simpleEntityVarName);
qualifiedRawTableName = rbacDef.getRootEntityAlias().getRawTableNameWithSchema(); rawTableName = rbacDef.getRootEntityAlias().getRawTableName();
} }
void generateTo(final StringWriter plPgSql) { void generateTo(final StringWriter plPgSql) {
@ -53,7 +53,7 @@ class RolesGrantsAndPermissionsGenerator {
private void generateHeader(final StringWriter plPgSql, final String triggerType) { private void generateHeader(final StringWriter plPgSql, final String triggerType) {
plPgSql.writeLn(""" plPgSql.writeLn("""
-- ============================================================================ -- ============================================================================
--changeset RolesGrantsAndPermissionsGenerator:${liquibaseTagPrefix}-rbac-${triggerType}-trigger endDelimiter:--// --changeset ${liquibaseTagPrefix}-rbac-${triggerType}-trigger:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
""", """,
with("liquibaseTagPrefix", liquibaseTagPrefix), with("liquibaseTagPrefix", liquibaseTagPrefix),
@ -66,27 +66,28 @@ class RolesGrantsAndPermissionsGenerator {
Creates the roles, grants and permission for the AFTER INSERT TRIGGER. Creates the roles, grants and permission for the AFTER INSERT TRIGGER.
*/ */
create or replace procedure ${rawTableQualifiedName}_build_rbac_system( create or replace procedure buildRbacSystemFor${simpleEntityName}(
NEW ${rawTableQualifiedName} NEW ${rawTableName}
) )
language plpgsql as $$ language plpgsql as $$
""" """
.replace("${rawTableQualifiedName}", qualifiedRawTableName)); .replace("${simpleEntityName}", simpleEntityName)
.replace("${rawTableName}", rawTableName));
plPgSql.writeLn("declare"); plPgSql.writeLn("declare");
plPgSql.indented(() -> { plPgSql.indented(() -> {
referencedEntityAliases() referencedEntityAliases()
.forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableNameWithSchema() + ";")); .forEach((ea) -> plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";"));
}); });
plPgSql.writeLn(); plPgSql.writeLn();
plPgSql.writeLn("begin"); plPgSql.writeLn("begin");
plPgSql.indented(() -> { plPgSql.indented(() -> {
plPgSql.writeLn("call rbac.enterTriggerForObjectUuid(NEW.uuid);"); plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
plPgSql.writeLn(); plPgSql.writeLn();
generateCreateRolesAndGrantsAfterInsert(plPgSql); generateCreateRolesAndGrantsAfterInsert(plPgSql);
plPgSql.ensureSingleEmptyLine(); plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn("call rbac.leaveTriggerForObjectUuid(NEW.uuid);"); plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
}); });
plPgSql.writeLn("end; $$;"); plPgSql.writeLn("end; $$;");
plPgSql.writeLn(); plPgSql.writeLn();
@ -105,21 +106,21 @@ class RolesGrantsAndPermissionsGenerator {
Called from the AFTER UPDATE TRIGGER to re-wire the grants. Called from the AFTER UPDATE TRIGGER to re-wire the grants.
*/ */
create or replace procedure ${rawTableQualifiedName}_update_rbac_system( create or replace procedure updateRbacRulesFor${simpleEntityName}(
OLD ${rawTableQualifiedName}, OLD ${rawTableName},
NEW ${rawTableQualifiedName} NEW ${rawTableName}
) )
language plpgsql as $$ language plpgsql as $$
begin begin
if ${updateConditions} then if ${updateConditions} then
delete from rbac.grants g where g.grantedbytriggerof = OLD.uuid; delete from rbacgrants g where g.grantedbytriggerof = OLD.uuid;
call ${rawTableQualifiedName}_build_rbac_system(NEW); call buildRbacSystemFor${simpleEntityName}(NEW);
end if; end if;
end; $$; end; $$;
""", """,
with("simpleEntityName", simpleEntityName), with("simpleEntityName", simpleEntityName),
with("rawTableQualifiedName", qualifiedRawTableName), with("rawTableName", rawTableName),
with("updateConditions", updateConditions)); with("updateConditions", updateConditions));
} }
@ -129,33 +130,34 @@ class RolesGrantsAndPermissionsGenerator {
Called from the AFTER UPDATE TRIGGER to re-wire the grants. Called from the AFTER UPDATE TRIGGER to re-wire the grants.
*/ */
create or replace procedure ${rawTableQualifiedName}_update_rbac_system( create or replace procedure updateRbacRulesFor${simpleEntityName}(
OLD ${rawTableQualifiedName}, OLD ${rawTableName},
NEW ${rawTableQualifiedName} NEW ${rawTableName}
) )
language plpgsql as $$ language plpgsql as $$
declare declare
""", """
with("rawTableQualifiedName", qualifiedRawTableName)); .replace("${simpleEntityName}", simpleEntityName)
.replace("${rawTableName}", rawTableName));
plPgSql.chopEmptyLines(); plPgSql.chopEmptyLines();
plPgSql.indented(() -> { plPgSql.indented(() -> {
referencedEntityAliases() referencedEntityAliases()
.forEach((ea) -> { .forEach((ea) -> {
plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableNameWithSchema() + ";"); plPgSql.writeLn(entityRefVar(OLD, ea) + " " + ea.getRawTableName() + ";");
plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableNameWithSchema() + ";"); plPgSql.writeLn(entityRefVar(NEW, ea) + " " + ea.getRawTableName() + ";");
}); });
}); });
plPgSql.writeLn(); plPgSql.writeLn();
plPgSql.writeLn("begin"); plPgSql.writeLn("begin");
plPgSql.indented(() -> { plPgSql.indented(() -> {
plPgSql.writeLn("call rbac.enterTriggerForObjectUuid(NEW.uuid);"); plPgSql.writeLn("call enterTriggerForObjectUuid(NEW.uuid);");
plPgSql.writeLn(); plPgSql.writeLn();
generateUpdateRolesAndGrantsAfterUpdate(plPgSql); generateUpdateRolesAndGrantsAfterUpdate(plPgSql);
plPgSql.ensureSingleEmptyLine(); plPgSql.ensureSingleEmptyLine();
plPgSql.writeLn("call rbac.leaveTriggerForObjectUuid(NEW.uuid);"); plPgSql.writeLn("call leaveTriggerForObjectUuid(NEW.uuid);");
}); });
plPgSql.writeLn("end; $$;"); plPgSql.writeLn("end; $$;");
plPgSql.writeLn(); plPgSql.writeLn();
@ -307,10 +309,10 @@ class RolesGrantsAndPermissionsGenerator {
private String generateRevoke(RbacGrantDefinition grantDef) { private String generateRevoke(RbacGrantDefinition grantDef) {
return switch (grantDef.grantType()) { return switch (grantDef.grantType()) {
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant"); case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
case ROLE_TO_ROLE -> "call rbac.revokeRoleFromRole(${subRoleRef}, ${superRoleRef});" case ROLE_TO_ROLE -> "call revokeRoleFromRole(${subRoleRef}, ${superRoleRef});"
.replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef())) .replace("${subRoleRef}", roleRef(OLD, grantDef.getSubRoleDef()))
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef())); .replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
case PERM_TO_ROLE -> "call rbac.revokePermissionFromRole(${permRef}, ${superRoleRef});" case PERM_TO_ROLE -> "call revokePermissionFromRole(${permRef}, ${superRoleRef});"
.replace("${permRef}", getPerm(OLD, grantDef.getPermDef())) .replace("${permRef}", getPerm(OLD, grantDef.getPermDef()))
.replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef())); .replace("${superRoleRef}", roleRef(OLD, grantDef.getSuperRoleDef()));
}; };
@ -319,13 +321,13 @@ class RolesGrantsAndPermissionsGenerator {
private String generateGrant(RbacGrantDefinition grantDef) { private String generateGrant(RbacGrantDefinition grantDef) {
final var grantSql = switch (grantDef.grantType()) { final var grantSql = switch (grantDef.grantType()) {
case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant"); case ROLE_TO_USER -> throw new IllegalArgumentException("unexpected grant");
case ROLE_TO_ROLE -> "call rbac.grantRoleToRole(${subRoleRef}, ${superRoleRef}${assumed});" case ROLE_TO_ROLE -> "call grantRoleToRole(${subRoleRef}, ${superRoleRef}${assumed});"
.replace("${assumed}", grantDef.isAssumed() ? "" : ", rbac.unassumed()") .replace("${assumed}", grantDef.isAssumed() ? "" : ", unassumed()")
.replace("${subRoleRef}", roleRef(NEW, grantDef.getSubRoleDef())) .replace("${subRoleRef}", roleRef(NEW, grantDef.getSubRoleDef()))
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef())); .replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
case PERM_TO_ROLE -> case PERM_TO_ROLE ->
grantDef.getPermDef().getPermission() == INSERT ? "" grantDef.getPermDef().getPermission() == INSERT ? ""
: "call rbac.grantPermissionToRole(${permRef}, ${superRoleRef});" : "call grantPermissionToRole(${permRef}, ${superRoleRef});"
.replace("${permRef}", createPerm(NEW, grantDef.getPermDef())) .replace("${permRef}", createPerm(NEW, grantDef.getPermDef()))
.replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef())); .replace("${superRoleRef}", roleRef(NEW, grantDef.getSuperRoleDef()));
}; };
@ -333,15 +335,15 @@ class RolesGrantsAndPermissionsGenerator {
} }
private String findPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) { private String findPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
return permRef("rbac.findPermissionId", ref, permDef); return permRef("findPermissionId", ref, permDef);
} }
private String getPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) { private String getPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
return permRef("rbac.getPermissionId", ref, permDef); return permRef("getPermissionId", ref, permDef);
} }
private String createPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) { private String createPerm(final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
return permRef("rbac.createPermission", ref, permDef); return permRef("createPermission", ref, permDef);
} }
private String permRef(final String functionName, final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) { private String permRef(final String functionName, final PostgresTriggerReference ref, final RbacPermissionDefinition permDef) {
@ -362,7 +364,7 @@ class RolesGrantsAndPermissionsGenerator {
System.out.println("null"); System.out.println("null");
} }
if (roleDef.getEntityAlias().isGlobal()) { if (roleDef.getEntityAlias().isGlobal()) {
return "rbac.globalAdmin()"; return "globalAdmin()";
} }
final String entityRefVar = entityRefVar(rootRefVar, roleDef.getEntityAlias()); final String entityRefVar = entityRefVar(rootRefVar, roleDef.getEntityAlias());
return roleDef.getEntityAlias().simpleName() + capitalize(roleDef.getRole().name()) return roleDef.getEntityAlias().simpleName() + capitalize(roleDef.getRole().name())
@ -387,7 +389,7 @@ class RolesGrantsAndPermissionsGenerator {
} }
plPgSql.writeLn(); plPgSql.writeLn();
plPgSql.writeLn("perform rbac.defineRoleWithGrants("); plPgSql.writeLn("perform createRoleWithGrants(");
plPgSql.indented(() -> { plPgSql.indented(() -> {
plPgSql.writeLn("${simpleVarName)${roleSuffix}(NEW)," plPgSql.writeLn("${simpleVarName)${roleSuffix}(NEW),"
.replace("${simpleVarName)", simpleEntityVarName) .replace("${simpleVarName)", simpleEntityVarName)
@ -413,7 +415,7 @@ class RolesGrantsAndPermissionsGenerator {
.map(this::toPlPgSqlReference) .map(this::toPlPgSqlReference)
.toList(); .toList();
plPgSql.indented(() -> plPgSql.indented(() ->
plPgSql.writeLn("subjectUuids => array[" + joinArrayElements(arrayElements, 2) + "],\n")); plPgSql.writeLn("userUuids => array[" + joinArrayElements(arrayElements, 2) + "],\n"));
rbacGrants.removeAll(grantsToUsers); rbacGrants.removeAll(grantsToUsers);
} }
} }
@ -512,25 +514,25 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.writeLn(""" plPgSql.writeLn("""
/* /*
AFTER INSERT TRIGGER to create the role+grant structure for a new ${rawTableQualifiedName} row. AFTER INSERT TRIGGER to create the role+grant structure for a new ${rawTableName} row.
*/ */
create or replace function ${rawTableQualifiedName}_build_rbac_system_after_insert_tf() create or replace function insertTriggerFor${simpleEntityName}_tf()
returns trigger returns trigger
language plpgsql language plpgsql
strict as $$ strict as $$
begin begin
call ${rawTableQualifiedName}_build_rbac_system(NEW); call buildRbacSystemFor${simpleEntityName}(NEW);
return NEW; return NEW;
end; $$; end; $$;
create trigger build_rbac_system_after_insert_tg create trigger insertTriggerFor${simpleEntityName}_tg
after insert on ${rawTableQualifiedName} after insert on ${rawTableName}
for each row for each row
execute procedure ${rawTableQualifiedName}_build_rbac_system_after_insert_tf(); execute procedure insertTriggerFor${simpleEntityName}_tf();
""" """
.replace("${schemaPrefix}", schemaPrefix(qualifiedRawTableName)) .replace("${simpleEntityName}", simpleEntityName)
.replace("${rawTableQualifiedName}", qualifiedRawTableName) .replace("${rawTableName}", rawTableName)
); );
generateFooter(plPgSql); generateFooter(plPgSql);
@ -547,43 +549,38 @@ class RolesGrantsAndPermissionsGenerator {
plPgSql.writeLn(""" plPgSql.writeLn("""
/* /*
AFTER UPDATE TRIGGER to re-wire the grant structure for a new ${rawTableQualifiedName} row. AFTER INSERT TRIGGER to re-wire the grant structure for a new ${rawTableName} row.
*/ */
create or replace function ${rawTableQualifiedName}_update_rbac_system_after_update_tf() create or replace function updateTriggerFor${simpleEntityName}_tf()
returns trigger returns trigger
language plpgsql language plpgsql
strict as $$ strict as $$
begin begin
call ${rawTableQualifiedName}_update_rbac_system(OLD, NEW); call updateRbacRulesFor${simpleEntityName}(OLD, NEW);
return NEW; return NEW;
end; $$; end; $$;
create trigger update_rbac_system_after_update_tg create trigger updateTriggerFor${simpleEntityName}_tg
after update on ${rawTableQualifiedName} after update on ${rawTableName}
for each row for each row
execute procedure ${rawTableQualifiedName}_update_rbac_system_after_update_tf(); execute procedure updateTriggerFor${simpleEntityName}_tf();
""" """
.replace("${rawTableQualifiedName}", qualifiedRawTableName) .replace("${simpleEntityName}", simpleEntityName)
.replace("${rawTableName}", rawTableName)
); );
generateFooter(plPgSql); generateFooter(plPgSql);
} }
private String schemaPrefix(final String qualifiedIdentifier) {
return qualifiedIdentifier.contains(".")
? qualifiedIdentifier.split("\\.")[0] + "."
: "";
}
private static void generateFooter(final StringWriter plPgSql) { private static void generateFooter(final StringWriter plPgSql) {
plPgSql.writeLn("--//"); plPgSql.writeLn("--//");
plPgSql.writeLn(); plPgSql.writeLn();
} }
private String toPlPgSqlReference(final RbacView.RbacSubjectReference userRef) { private String toPlPgSqlReference(final RbacView.RbacUserReference userRef) {
return switch (userRef.role) { return switch (userRef.role) {
case CREATOR -> "rbac.currentSubjectUuid()"; case CREATOR -> "currentUserUuid()";
default -> throw new IllegalArgumentException("unknown user role: " + userRef); default -> throw new IllegalArgumentException("unknown user role: " + userRef);
}; };
} }
@ -592,9 +589,9 @@ class RolesGrantsAndPermissionsGenerator {
final PostgresTriggerReference triggerRef, final PostgresTriggerReference triggerRef,
final RbacView.RbacRoleDefinition roleDef, final RbacView.RbacRoleDefinition roleDef,
final boolean assumed) { final boolean assumed) {
final var assumedArg = assumed ? "" : ", rbac.unassumed()"; final var assumedArg = assumed ? "" : ", unassumed()";
return toRoleRef(roleDef) + return toRoleRef(roleDef) +
(roleDef.getEntityAlias().isGlobal() ? ( assumed ? "()" : "(rbac.unassumed())") (roleDef.getEntityAlias().isGlobal() ? ( assumed ? "()" : "(unassumed())")
: rbacDef.isRootEntityAlias(roleDef.getEntityAlias()) ? ("(" + triggerRef.name() + ")") : rbacDef.isRootEntityAlias(roleDef.getEntityAlias()) ? ("(" + triggerRef.name() + ")")
: "(" + toTriggerReference(triggerRef, roleDef.getEntityAlias()) + assumedArg + ")"); : "(" + toTriggerReference(triggerRef, roleDef.getEntityAlias()) + assumedArg + ")");
} }

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -19,11 +19,9 @@ public class StringWriter {
writeLn(); writeLn();
} }
String writeLn(final String text, final VarDef... varDefs) { void writeLn(final String text, final VarDef... varDefs) {
final var insertText = indented(new VarReplacer(varDefs).apply(text)); string.append( indented( new VarReplacer(varDefs).apply(text) ));
string.append(insertText);
writeLn(); writeLn();
return insertText;
} }
void writeLn() { void writeLn() {

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.generator; package net.hostsharing.hsadminng.rbac.rbacdef;
// TODO: The whole code in this package is more like a quick hack to solve an urgent problem. // TODO: The whole code in this package is more like a quick hack to solve an urgent problem.
// It should be re-written in PostgreSQL pl/pgsql, // It should be re-written in PostgreSQL pl/pgsql,

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.grant; package net.hostsharing.hsadminng.rbac.rbacgrant;
import lombok.*; import lombok.*;
import org.springframework.data.annotation.Immutable; import org.springframework.data.annotation.Immutable;
@ -12,7 +12,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
@Entity @Entity
@Table(schema = "rbac", name = "grants_ev") @Table(name = "rbacgrants_ev")
@Getter @Getter
@Setter @Setter
@Builder @Builder

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.grant; package net.hostsharing.hsadminng.rbac.rbacgrant;
import org.springframework.data.repository.Repository; import org.springframework.data.repository.Repository;

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.grant; package net.hostsharing.hsadminng.rbac.rbacgrant;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
@ -33,14 +33,14 @@ public class RbacGrantController implements RbacGrantsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<RbacGrantResource> getGrantById( public ResponseEntity<RbacGrantResource> getGrantById(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID grantedRoleUuid, final UUID grantedRoleUuid,
final UUID granteeSubjectUuid) { final UUID granteeUserUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var id = new RbacGrantId(granteeSubjectUuid, grantedRoleUuid); final var id = new RbacGrantId(granteeUserUuid, grantedRoleUuid);
final var result = rbacGrantRepository.findById(id); final var result = rbacGrantRepository.findById(id);
if (result == null) { if (result == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
@ -50,23 +50,23 @@ public class RbacGrantController implements RbacGrantsApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<RbacGrantResource>> listSubjectGrants( public ResponseEntity<List<RbacGrantResource>> listUserGrants(
final String currentSubject, final String currentUser,
final String assumedRoles) { final String assumedRoles) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
return ResponseEntity.ok(mapper.mapList(rbacGrantRepository.findAll(), RbacGrantResource.class)); return ResponseEntity.ok(mapper.mapList(rbacGrantRepository.findAll(), RbacGrantResource.class));
} }
@Override @Override
@Transactional @Transactional
public ResponseEntity<RbacGrantResource> grantRoleToSubject( public ResponseEntity<RbacGrantResource> grantRoleToUser(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final RbacGrantResource body) { final RbacGrantResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var granted = rbacGrantRepository.save(mapper.map(body, RbacGrantEntity.class)); final var granted = rbacGrantRepository.save(mapper.map(body, RbacGrantEntity.class));
em.flush(); em.flush();
@ -82,28 +82,28 @@ public class RbacGrantController implements RbacGrantsApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> revokeRoleFromSubject( public ResponseEntity<Void> revokeRoleFromUser(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID grantedRoleUuid, final UUID grantedRoleUuid,
final UUID granteeSubjectUuid) { final UUID granteeUserUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
rbacGrantRepository.deleteByRbacGrantId(new RbacGrantId(granteeSubjectUuid, grantedRoleUuid)); rbacGrantRepository.deleteByRbacGrantId(new RbacGrantId(granteeUserUuid, grantedRoleUuid));
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
// TODO.feat: implement an endpoint to create a Mermaid flowchart with all grants of a given user // TODO: implement an endpoint to create a Mermaid flowchart with all grants of a given user
// @GetMapping( // @GetMapping(
// path = "/api/rbac/subjects/{subjectUuid}/grants", // path = "/api/rbac/users/{userUuid}/grants",
// produces = {"text/vnd.mermaid"}) // produces = {"text/vnd.mermaid"})
// @Transactional(readOnly = true) // @Transactional(readOnly = true)
// public ResponseEntity<String> allGrantsOfUserAsMermaid( // public ResponseEntity<String> allGrantsOfUserAsMermaid(
// @RequestHeader(name = "current-subject") String currentSubject, // @RequestHeader(name = "current-user") String currentUser,
// @RequestHeader(name = "assumed-roles", required = false) String assumedRoles) { // @RequestHeader(name = "assumed-roles", required = false) String assumedRoles) {
// final var graph = RbacGrantsDiagramService.allGrantsToUser(currentSubject); // final var graph = RbacGrantsDiagramService.allGrantsToUser(currentUser);
// return ResponseEntity.ok(graph); // return ResponseEntity.ok(graph);
// } // }

View File

@ -1,14 +1,14 @@
package net.hostsharing.hsadminng.rbac.grant; package net.hostsharing.hsadminng.rbac.rbacgrant;
import lombok.*; import lombok.*;
import net.hostsharing.hsadminng.rbac.role.RbacRoleType; import net.hostsharing.hsadminng.rbac.rbacrole.RbacRoleType;
import org.springframework.data.annotation.Immutable; import org.springframework.data.annotation.Immutable;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.util.UUID; import java.util.UUID;
@Entity @Entity
@Table(schema = "rbac", name = "grants_rv") @Table(name = "rbacgrants_rv")
@IdClass(RbacGrantId.class) @IdClass(RbacGrantId.class)
@Getter @Getter
@Setter @Setter
@ -33,11 +33,11 @@ public class RbacGrantEntity {
private UUID grantedRoleUuid; private UUID grantedRoleUuid;
@Column(name = "username", updatable = false, insertable = false) @Column(name = "username", updatable = false, insertable = false)
private String granteeSubjectName; private String granteeUserName;
@Id @Id
@Column(name = "subjectuuid") @Column(name = "useruuid")
private UUID granteeSubjectUuid; private UUID granteeUserUuid;
private boolean assumed; private boolean assumed;
@ -55,12 +55,12 @@ public class RbacGrantEntity {
private RbacRoleType grantedRoleType; private RbacRoleType grantedRoleType;
RbacGrantId getRbacGrantId() { RbacGrantId getRbacGrantId() {
return new RbacGrantId(granteeSubjectUuid, grantedRoleUuid); return new RbacGrantId(granteeUserUuid, grantedRoleUuid);
} }
public String toDisplay() { public String toDisplay() {
return "{ grant role:" + grantedRoleIdName + return "{ grant role:" + grantedRoleIdName +
" to user:" + granteeSubjectName + " to user:" + granteeUserName +
" by role:" + grantedByRoleIdName + " by role:" + grantedByRoleIdName +
(assumed ? " and assume" : "") + (assumed ? " and assume" : "") +
" }"; " }";

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.grant; package net.hostsharing.hsadminng.rbac.rbacgrant;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -14,6 +14,6 @@ import java.util.UUID;
@AllArgsConstructor @AllArgsConstructor
public class RbacGrantId implements Serializable { public class RbacGrantId implements Serializable {
private UUID granteeSubjectUuid; private UUID granteeUserUuid;
private UUID grantedRoleUuid; private UUID grantedRoleUuid;
} }

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.grant; package net.hostsharing.hsadminng.rbac.rbacgrant;
import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
@ -11,7 +11,7 @@ public interface RbacGrantRepository extends Repository<RbacGrantEntity, RbacGra
@Query(value = """ @Query(value = """
select g from RbacGrantEntity as g select g from RbacGrantEntity as g
where g.grantedRoleUuid=:#{#rbacGrantId.grantedRoleUuid} where g.grantedRoleUuid=:#{#rbacGrantId.grantedRoleUuid}
and g.granteeSubjectUuid=:#{#rbacGrantId.granteeSubjectUuid} and g.granteeUserUuid=:#{#rbacGrantId.granteeUserUuid}
""") """)
RbacGrantEntity findById(RbacGrantId rbacGrantId); RbacGrantEntity findById(RbacGrantId rbacGrantId);
@ -25,7 +25,7 @@ public interface RbacGrantRepository extends Repository<RbacGrantEntity, RbacGra
@Query(value = """ @Query(value = """
delete from RbacGrantEntity as g delete from RbacGrantEntity as g
where g.grantedRoleUuid=:#{#rbacGrantId.grantedRoleUuid} where g.grantedRoleUuid=:#{#rbacGrantId.grantedRoleUuid}
and g.granteeSubjectUuid=:#{#rbacGrantId.granteeSubjectUuid} and g.granteeUserUuid=:#{#rbacGrantId.granteeUserUuid}
""") """)
void deleteByRbacGrantId(RbacGrantId rbacGrantId); void deleteByRbacGrantId(RbacGrantId rbacGrantId);
} }

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.grant; package net.hostsharing.hsadminng.rbac.rbacgrant;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -16,7 +16,7 @@ import java.util.stream.Stream;
import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService.Include.*; import static net.hostsharing.hsadminng.rbac.rbacgrant.RbacGrantsDiagramService.Include.*;
// TODO: cleanup - this code was 'hacked' to quickly fix a specific problem, needs refactoring // TODO: cleanup - this code was 'hacked' to quickly fix a specific problem, needs refactoring
@Service @Service
@ -64,9 +64,9 @@ public class RbacGrantsDiagramService {
private Map<UUID, List<RawRbacGrantEntity>> descendantsByUuid = new HashMap<>(); private Map<UUID, List<RawRbacGrantEntity>> descendantsByUuid = new HashMap<>();
public String allGrantsTocurrentSubject(final EnumSet<Include> includes) { public String allGrantsToCurrentUser(final EnumSet<Include> includes) {
final var graph = new LimitedHashSet<RawRbacGrantEntity>(); final var graph = new LimitedHashSet<RawRbacGrantEntity>();
for ( UUID subjectUuid: context.fetchCurrentSubjectOrAssumedRolesUuids() ) { for ( UUID subjectUuid: context.currentSubjectsUuids() ) {
traverseGrantsTo(graph, subjectUuid, includes); traverseGrantsTo(graph, subjectUuid, includes);
} }
return toMermaidFlowchart(graph, includes); return toMermaidFlowchart(graph, includes);
@ -78,11 +78,11 @@ public class RbacGrantsDiagramService {
if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm:")) { if (!includes.contains(PERMISSIONS) && g.getDescendantIdName().startsWith("perm:")) {
return; return;
} }
if ( !g.getDescendantIdName().startsWith("role:rbac.global")) { if ( !g.getDescendantIdName().startsWith("role:global")) {
if (!includes.contains(TEST_ENTITIES) && g.getDescendantIdName().contains(":rbactest.")) { if (!includes.contains(TEST_ENTITIES) && g.getDescendantIdName().contains(":test_")) {
return; return;
} }
if (!includes.contains(NON_TEST_ENTITIES) && !g.getDescendantIdName().contains(":rbactest.")) { if (!includes.contains(NON_TEST_ENTITIES) && !g.getDescendantIdName().contains(":test_")) {
return; return;
} }
} }
@ -94,7 +94,7 @@ public class RbacGrantsDiagramService {
} }
public String allGrantsFrom(final UUID targetObject, final String op, final EnumSet<Include> includes) { public String allGrantsFrom(final UUID targetObject, final String op, final EnumSet<Include> includes) {
final var refUuid = (UUID) em.createNativeQuery("SELECT uuid FROM rbac.permission WHERE objectuuid=:targetObject AND op=:op") final var refUuid = (UUID) em.createNativeQuery("SELECT uuid FROM rbacpermission WHERE objectuuid=:targetObject AND op=:op")
.setParameter("targetObject", targetObject) .setParameter("targetObject", targetObject)
.setParameter("op", op) .setParameter("op", op)
.getSingleResult(); .getSingleResult();

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.object; package net.hostsharing.hsadminng.rbac.rbacobject;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.role; package net.hostsharing.hsadminng.rbac.rbacrole;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
@ -26,10 +26,10 @@ public class RbacRoleController implements RbacRolesApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<RbacRoleResource>> listRoles( public ResponseEntity<List<RbacRoleResource>> listRoles(
final String currentSubject, final String currentUser,
final String assumedRoles) { final String assumedRoles) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final List<RbacRoleEntity> result = rbacRoleRepository.findAll(); final List<RbacRoleEntity> result = rbacRoleRepository.findAll();

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.role; package net.hostsharing.hsadminng.rbac.rbacrole;
import lombok.*; import lombok.*;
import org.hibernate.annotations.Formula; import org.hibernate.annotations.Formula;
@ -8,7 +8,7 @@ import jakarta.persistence.*;
import java.util.UUID; import java.util.UUID;
@Entity @Entity
@Table(schema = "rbac", name = "role_rv") @Table(name = "rbacrole_rv")
@Getter @Getter
@Setter @Setter
@ToString @ToString

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.role; package net.hostsharing.hsadminng.rbac.rbacrole;
import org.springframework.data.repository.Repository; import org.springframework.data.repository.Repository;

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.role; package net.hostsharing.hsadminng.rbac.rbacrole;
public enum RbacRoleType { public enum RbacRoleType {
OWNER, ADMIN, AGENT, TENANT, GUEST, REFERRER OWNER, ADMIN, AGENT, TENANT, GUEST, REFERRER

View File

@ -1,10 +1,10 @@
package net.hostsharing.hsadminng.rbac.subject; package net.hostsharing.hsadminng.rbac.rbacuser;
import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.mapper.Mapper; import net.hostsharing.hsadminng.mapper.Mapper;
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi; import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacUsersApi;
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectPermissionResource; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacUserPermissionResource;
import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacSubjectResource; import net.hostsharing.hsadminng.rbac.generated.api.v1.model.RbacUserResource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -15,7 +15,7 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
public class RbacSubjectController implements RbacSubjectsApi { public class RbacUserController implements RbacUsersApi {
@Autowired @Autowired
private Context context; private Context context;
@ -24,81 +24,81 @@ public class RbacSubjectController implements RbacSubjectsApi {
private Mapper mapper; private Mapper mapper;
@Autowired @Autowired
private RbacSubjectRepository rbacSubjectRepository; private RbacUserRepository rbacUserRepository;
@Override @Override
@Transactional @Transactional
public ResponseEntity<RbacSubjectResource> createSubject( public ResponseEntity<RbacUserResource> createUser(
final RbacSubjectResource body final RbacUserResource body
) { ) {
context.define(null); context.define(null);
if (body.getUuid() == null) { if (body.getUuid() == null) {
body.setUuid(UUID.randomUUID()); body.setUuid(UUID.randomUUID());
} }
final var saved = mapper.map(body, RbacSubjectEntity.class); final var saved = mapper.map(body, RbacUserEntity.class);
rbacSubjectRepository.create(saved); rbacUserRepository.create(saved);
final var uri = final var uri =
MvcUriComponentsBuilder.fromController(getClass()) MvcUriComponentsBuilder.fromController(getClass())
.path("/api/rbac.yaml/users/{id}") .path("/api/rbac.yaml/users/{id}")
.buildAndExpand(saved.getUuid()) .buildAndExpand(saved.getUuid())
.toUri(); .toUri();
return ResponseEntity.created(uri).body(mapper.map(saved, RbacSubjectResource.class)); return ResponseEntity.created(uri).body(mapper.map(saved, RbacUserResource.class));
} }
@Override @Override
@Transactional @Transactional
public ResponseEntity<Void> deleteSubjectByUuid( public ResponseEntity<Void> deleteUserByUuid(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID subjectUuid final UUID userUuid
) { ) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
rbacSubjectRepository.deleteByUuid(subjectUuid); rbacUserRepository.deleteByUuid(userUuid);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<RbacSubjectResource> getSubjectById( public ResponseEntity<RbacUserResource> getUserById(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID subjectUuid) { final UUID userUuid) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = rbacSubjectRepository.findByUuid(subjectUuid); final var result = rbacUserRepository.findByUuid(userUuid);
if (result == null) { if (result == null) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
return ResponseEntity.ok(mapper.map(result, RbacSubjectResource.class)); return ResponseEntity.ok(mapper.map(result, RbacUserResource.class));
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<RbacSubjectResource>> listSubjects( public ResponseEntity<List<RbacUserResource>> listUsers(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final String userName final String userName
) { ) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
return ResponseEntity.ok(mapper.mapList(rbacSubjectRepository.findByOptionalNameLike(userName), RbacSubjectResource.class)); return ResponseEntity.ok(mapper.mapList(rbacUserRepository.findByOptionalNameLike(userName), RbacUserResource.class));
} }
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<RbacSubjectPermissionResource>> listSubjectPermissions( public ResponseEntity<List<RbacUserPermissionResource>> listUserPermissions(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID subjectUuid final UUID userUuid
) { ) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
return ResponseEntity.ok(mapper.mapList( return ResponseEntity.ok(mapper.mapList(
rbacSubjectRepository.findPermissionsOfUserByUuid(subjectUuid), rbacUserRepository.findPermissionsOfUserByUuid(userUuid),
RbacSubjectPermissionResource.class)); RbacUserPermissionResource.class));
} }
} }

View File

@ -1,4 +1,4 @@
package net.hostsharing.hsadminng.rbac.subject; package net.hostsharing.hsadminng.rbac.rbacuser;
import lombok.*; import lombok.*;
import org.springframework.data.annotation.Immutable; import org.springframework.data.annotation.Immutable;
@ -13,14 +13,14 @@ import java.time.temporal.ChronoUnit;
import java.util.UUID; import java.util.UUID;
@Entity @Entity
@Table(schema = "rbac", name = "subject_rv") @Table(name = "rbacuser_rv")
@Getter @Getter
@Setter @Setter
@ToString @ToString
@Immutable @Immutable
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class RbacSubjectEntity { public class RbacUserEntity {
private static final int MAX_VALIDITY_DAYS = 21; private static final int MAX_VALIDITY_DAYS = 21;
private static DateTimeFormatter DATE_FORMAT_WITH_FULLHOUR = DateTimeFormatter.ofPattern("MM-dd-yyyy HH"); private static DateTimeFormatter DATE_FORMAT_WITH_FULLHOUR = DateTimeFormatter.ofPattern("MM-dd-yyyy HH");

View File

@ -1,8 +1,8 @@
package net.hostsharing.hsadminng.rbac.subject; package net.hostsharing.hsadminng.rbac.rbacuser;
import java.util.UUID; import java.util.UUID;
public interface RbacSubjectPermission { public interface RbacUserPermission {
UUID getRoleUuid(); UUID getRoleUuid();
String getRoleName(); String getRoleName();

View File

@ -0,0 +1,46 @@
package net.hostsharing.hsadminng.rbac.rbacuser;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import java.util.List;
import java.util.UUID;
public interface RbacUserRepository extends Repository<RbacUserEntity, UUID> {
@Query("""
select u from RbacUserEntity u
where :userName is null or u.name like concat(cast(:userName as text), '%')
order by u.name
""")
List<RbacUserEntity> findByOptionalNameLike(String userName);
// bypasses the restricted view, to be able to grant rights to arbitrary user
@Query(value = "select * from rbacuser where name=:userName", nativeQuery = true)
RbacUserEntity findByName(String userName);
RbacUserEntity findByUuid(UUID uuid);
@Query(value = "select * from grantedPermissions(:userUuid)", nativeQuery = true)
List<RbacUserPermission> findPermissionsOfUserByUuid(UUID userUuid);
/*
Can't use save/saveAndFlush from SpringData because the uuid is not generated on the entity level,
but explicitly, and then SpringData check's if it exists using an SQL SELECT.
And SQL SELECT needs a currentUser which we don't yet have in the case of self registration.
*/
@Modifying
@Query(value = "insert into RBacUser_RV (uuid, name) values( :#{#newUser.uuid}, :#{#newUser.name})", nativeQuery = true)
void insert(final RbacUserEntity newUser);
default RbacUserEntity create(final RbacUserEntity rbacUserEntity) {
if (rbacUserEntity.getUuid() == null) {
rbacUserEntity.setUuid(UUID.randomUUID());
}
insert(rbacUserEntity);
return rbacUserEntity;
}
void deleteByUuid(UUID userUuid);
}

View File

@ -1,46 +0,0 @@
package net.hostsharing.hsadminng.rbac.subject;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import java.util.List;
import java.util.UUID;
public interface RbacSubjectRepository extends Repository<RbacSubjectEntity, UUID> {
@Query("""
select u from RbacSubjectEntity u
where :userName is null or u.name like concat(cast(:userName as text), '%')
order by u.name
""")
List<RbacSubjectEntity> findByOptionalNameLike(String userName);
// bypasses the restricted view, to be able to grant rights to arbitrary user
@Query(value = "select * from rbac.subject where name=:userName", nativeQuery = true)
RbacSubjectEntity findByName(String userName);
RbacSubjectEntity findByUuid(UUID uuid);
@Query(value = "select * from rbac.grantedPermissions(:subjectUuid)", nativeQuery = true)
List<RbacSubjectPermission> findPermissionsOfUserByUuid(UUID subjectUuid);
/*
Can't use save/saveAndFlush from SpringData because the uuid is not generated on the entity level,
but explicitly, and then SpringData check's if it exists using an SQL SELECT.
And SQL SELECT needs a currentSubject which we don't yet have in the case of self registration.
*/
@Modifying
@Query(value = "insert into rbac.subject_rv (uuid, name) values( :#{#newUser.uuid}, :#{#newUser.name})", nativeQuery = true)
void insert(final RbacSubjectEntity newUser);
default RbacSubjectEntity create(final RbacSubjectEntity rbacSubjectEntity) {
if (rbacSubjectEntity.getUuid() == null) {
rbacSubjectEntity.setUuid(UUID.randomUUID());
}
insert(rbacSubjectEntity);
return rbacSubjectEntity;
}
void deleteByUuid(UUID subjectUuid);
}

View File

@ -32,11 +32,11 @@ public class TestCustomerController implements TestCustomersApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<TestCustomerResource>> listCustomers( public ResponseEntity<List<TestCustomerResource>> listCustomers(
String currentSubject, String currentUser,
String assumedRoles, String assumedRoles,
String prefix String prefix
) { ) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(prefix); final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(prefix);
@ -46,11 +46,11 @@ public class TestCustomerController implements TestCustomersApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<TestCustomerResource> addCustomer( public ResponseEntity<TestCustomerResource> addCustomer(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final TestCustomerResource customer) { final TestCustomerResource customer) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var saved = testCustomerRepository.save(mapper.map(customer, TestCustomerEntity.class)); final var saved = testCustomerRepository.save(mapper.map(customer, TestCustomerEntity.class));
final var uri = final var uri =

View File

@ -5,22 +5,22 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.GLOBAL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.GLOBAL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.RbacSubjectReference.UserRole.CREATOR; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Entity @Entity
@Table(schema = "rbactest", name = "customer_rv") @Table(name = "test_customer_rv")
@Getter @Getter
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@ -46,7 +46,7 @@ public class TestCustomerEntity implements BaseEntity<TestCustomerEntity> {
.withIdentityView(SQL.projection("prefix")) .withIdentityView(SQL.projection("prefix"))
.withRestrictedViewOrderBy(SQL.expression("reference")) .withRestrictedViewOrderBy(SQL.expression("reference"))
.withUpdatableColumns("reference", "prefix", "adminUserName") .withUpdatableColumns("reference", "prefix", "adminUserName")
.toRole("rbac.global", ADMIN).grantPermission(INSERT) .toRole("global", ADMIN).grantPermission(INSERT)
.createRole(OWNER, (with) -> { .createRole(OWNER, (with) -> {
with.owningUser(CREATOR).unassumed(); with.owningUser(CREATOR).unassumed();
@ -62,6 +62,6 @@ public class TestCustomerEntity implements BaseEntity<TestCustomerEntity> {
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("2-rbactest/201-rbactest-customer/2013-rbactest-customer-rbac"); rbac().generateWithBaseFileName("2-test/201-test-customer/2013-test-customer-rbac");
} }
} }

View File

@ -4,25 +4,25 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.test.pac.TestPackageEntity; import net.hostsharing.hsadminng.rbac.test.pac.TestPackageEntity;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.directlyFetchedByDependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Entity @Entity
@Table(schema = "rbactest", name = "domain_rv") @Table(name = "test_domain_rv")
@Getter @Getter
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@ -68,6 +68,6 @@ public class TestDomainEntity implements BaseEntity<TestDomainEntity> {
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("2-rbactest/203-rbactest-domain/2033-rbactest-domain-rbac"); rbac().generateWithBaseFileName("2-test/203-test-domain/2033-test-domain-rbac");
} }
} }

View File

@ -29,11 +29,11 @@ public class TestPackageController implements TestPackagesApi {
@Override @Override
@Transactional(readOnly = true) @Transactional(readOnly = true)
public ResponseEntity<List<TestPackageResource>> listPackages( public ResponseEntity<List<TestPackageResource>> listPackages(
String currentSubject, String currentUser,
String assumedRoles, String assumedRoles,
String name String name
) { ) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var result = testPackageRepository.findAllByOptionalNameLike(name); final var result = testPackageRepository.findAllByOptionalNameLike(name);
return ResponseEntity.ok(mapper.mapList(result, TestPackageResource.class)); return ResponseEntity.ok(mapper.mapList(result, TestPackageResource.class));
@ -42,12 +42,12 @@ public class TestPackageController implements TestPackagesApi {
@Override @Override
@Transactional @Transactional
public ResponseEntity<TestPackageResource> updatePackage( public ResponseEntity<TestPackageResource> updatePackage(
final String currentSubject, final String currentUser,
final String assumedRoles, final String assumedRoles,
final UUID packageUuid, final UUID packageUuid,
final TestPackageUpdateResource body) { final TestPackageUpdateResource body) {
context.define(currentSubject, assumedRoles); context.define(currentUser, assumedRoles);
final var current = testPackageRepository.findByUuid(packageUuid); final var current = testPackageRepository.findByUuid(packageUuid);
OptionalFromJson.of(body.getDescription()).ifPresent(current::setDescription); OptionalFromJson.of(body.getDescription()).ifPresent(current::setDescription);

View File

@ -4,25 +4,25 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import net.hostsharing.hsadminng.rbac.object.BaseEntity; import net.hostsharing.hsadminng.rbac.rbacobject.BaseEntity;
import net.hostsharing.hsadminng.rbac.generator.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView;
import net.hostsharing.hsadminng.rbac.generator.RbacView.SQL; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL;
import net.hostsharing.hsadminng.rbac.test.cust.TestCustomerEntity; import net.hostsharing.hsadminng.rbac.test.cust.TestCustomerEntity;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.ColumnValue.usingDefaultCase; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.ColumnValue.usingDefaultCase;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Nullable.NOT_NULL; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Nullable.NOT_NULL;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.Role.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.SQL.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.*;
import static net.hostsharing.hsadminng.rbac.generator.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor;
@Entity @Entity
@Table(schema = "rbactest", name = "package_rv") @Table(name = "test_package_rv")
@Getter @Getter
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@ -69,6 +69,6 @@ public class TestPackageEntity implements BaseEntity<TestPackageEntity> {
} }
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
rbac().generateWithBaseFileName("2-rbactest/202-rbactest-package/2023-rbactest-package-rbac"); rbac().generateWithBaseFileName("2-test/202-test-package/2023-test-package-rbac");
} }
} }

View File

@ -3,13 +3,13 @@ components:
parameters: parameters:
currentSubject: currentUser:
name: current-subject name: current-user
in: header in: header
required: true required: true
schema: schema:
type: string type: string
description: Identifying name of the current subject (e.g. user). description: Identifying name of the currently logged in user.
assumedRoles: assumedRoles:
name: assumed-roles name: assumed-roles
@ -17,4 +17,4 @@ components:
required: false required: false
schema: schema:
type: string type: string
description: Semicolon-separated list of roles to assume. The current subject needs to have the right to assume these roles. description: Semicolon-separated list of roles to assume. The current user needs to have the right to assume these roles.

View File

@ -8,13 +8,13 @@ components:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Unauthorized: Unauthorized:
description: The current subject is unknown or not authorized. description: The current user is unknown or not authorized.
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Forbidden: Forbidden:
description: The current subject or none of the assumed or roles is granted access to the resource. description: The current user or none of the assumed or roles is granted access to the resource.
content: content:
application/json: application/json:
schema: schema:

View File

@ -3,13 +3,13 @@ components:
parameters: parameters:
currentSubject: currentUser:
name: current-subject name: current-user
in: header in: header
required: true required: true
schema: schema:
type: string type: string
description: Identifying name of the currently logged in subject. description: Identifying name of the currently logged in user.
assumedRoles: assumedRoles:
name: assumed-roles name: assumed-roles
@ -17,4 +17,4 @@ components:
required: false required: false
schema: schema:
type: string type: string
description: Semicolon-separated list of roles to assume. The current subject needs to have the right to assume these roles. description: Semicolon-separated list of roles to assume. The current user needs to have the right to assume these roles.

View File

@ -8,13 +8,13 @@ components:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Unauthorized: Unauthorized:
description: The current subject is unknown or not authorized. description: The current user is unknown or not authorized.
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Forbidden: Forbidden:
description: The current subject or none of the assumed or roles is granted access to the resource. description: The current user or none of the assumed or roles is granted access to the resource.
content: content:
application/json: application/json:
schema: schema:

View File

@ -4,7 +4,7 @@ get:
description: 'Fetch a single booking item its uuid, if visible for the current subject.' description: 'Fetch a single booking item its uuid, if visible for the current subject.'
operationId: getBookingItemByUuid operationId: getBookingItemByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: bookingItemUuid - name: bookingItemUuid
in: path in: path
@ -32,7 +32,7 @@ patch:
description: 'Updates a single booking item identified by its uuid, if permitted for the current subject.' description: 'Updates a single booking item identified by its uuid, if permitted for the current subject.'
operationId: patchBookingItem operationId: patchBookingItem
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: bookingItemUuid - name: bookingItemUuid
in: path in: path
@ -63,7 +63,7 @@ delete:
description: 'Delete a single booking item identified by its uuid, if permitted for the current subject.' description: 'Delete a single booking item identified by its uuid, if permitted for the current subject.'
operationId: deleteBookingIemByUuid operationId: deleteBookingIemByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: bookingItemUuid - name: bookingItemUuid
in: path in: path

View File

@ -1,11 +1,11 @@
get: get:
summary: Returns a list of all booking items for a specified project. summary: Returns a list of all booking items for a specified project.
description: Returns the list of all booking items for a specified project which are visible to the current subject or any of it's assumed roles. description: Returns the list of all booking items for a specified project which are visible to the current user or any of it's assumed roles.
tags: tags:
- hs-booking-items - hs-booking-items
operationId: listBookingItemsByProjectUuid operationId: listBookingItemsByProjectUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: projectUuid - name: projectUuid
in: query in: query
@ -34,7 +34,7 @@ post:
- hs-booking-items - hs-booking-items
operationId: addBookingItem operationId: addBookingItem
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
requestBody: requestBody:
description: A JSON object describing the new booking item. description: A JSON object describing the new booking item.

View File

@ -4,7 +4,7 @@ get:
description: 'Fetch a single booking project its uuid, if visible for the current subject.' description: 'Fetch a single booking project its uuid, if visible for the current subject.'
operationId: getBookingProjectByUuid operationId: getBookingProjectByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: bookingProjectUuid - name: bookingProjectUuid
in: path in: path
@ -32,7 +32,7 @@ patch:
description: 'Updates a single booking project identified by its uuid, if permitted for the current subject.' description: 'Updates a single booking project identified by its uuid, if permitted for the current subject.'
operationId: patchBookingProject operationId: patchBookingProject
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: bookingProjectUuid - name: bookingProjectUuid
in: path in: path
@ -63,7 +63,7 @@ delete:
description: 'Delete a single booking project identified by its uuid, if permitted for the current subject.' description: 'Delete a single booking project identified by its uuid, if permitted for the current subject.'
operationId: deleteBookingIemByUuid operationId: deleteBookingIemByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: bookingProjectUuid - name: bookingProjectUuid
in: path in: path

View File

@ -1,11 +1,11 @@
get: get:
summary: Returns a list of all booking projects for a specified debitor. summary: Returns a list of all booking projects for a specified debitor.
description: Returns the list of all booking projects for a specified debitor which are visible to the current subject or any of it's assumed roles. description: Returns the list of all booking projects for a specified debitor which are visible to the current user or any of it's assumed roles.
tags: tags:
- hs-booking-projects - hs-booking-projects
operationId: listBookingProjectsByDebitorUuid operationId: listBookingProjectsByDebitorUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: debitorUuid - name: debitorUuid
in: query in: query
@ -34,7 +34,7 @@ post:
- hs-booking-projects - hs-booking-projects
operationId: addBookingProject operationId: addBookingProject
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
requestBody: requestBody:
description: A JSON object describing the new booking project. description: A JSON object describing the new booking project.

View File

@ -3,13 +3,13 @@ components:
parameters: parameters:
currentSubject: currentUser:
name: current-subject name: current-user
in: header in: header
required: true required: true
schema: schema:
type: string type: string
description: Identifying name of the currently logged in subject. description: Identifying name of the currently logged in user.
assumedRoles: assumedRoles:
name: assumed-roles name: assumed-roles
@ -17,4 +17,4 @@ components:
required: false required: false
schema: schema:
type: string type: string
description: Semicolon-separated list of roles to assume. The current subject needs to have the right to assume these roles. description: Semicolon-separated list of roles to assume. The current user needs to have the right to assume these roles.

View File

@ -8,13 +8,13 @@ components:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Unauthorized: Unauthorized:
description: The current subject is unknown or not authorized. description: The current user is unknown or not authorized.
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Error' $ref: '#/components/schemas/Error'
Forbidden: Forbidden:
description: The current subject or none of the assumed or roles is granted access to the resource. description: The current user or none of the assumed or roles is granted access to the resource.
content: content:
application/json: application/json:
schema: schema:

View File

@ -4,7 +4,7 @@ get:
description: 'Fetch a single managed asset by its uuid, if visible for the current subject.' description: 'Fetch a single managed asset by its uuid, if visible for the current subject.'
operationId: getAssetByUuid operationId: getAssetByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: assetUuid - name: assetUuid
in: path in: path
@ -32,7 +32,7 @@ patch:
description: 'Updates a single hosting asset identified by its uuid, if permitted for the current subject.' description: 'Updates a single hosting asset identified by its uuid, if permitted for the current subject.'
operationId: patchAsset operationId: patchAsset
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: assetUuid - name: assetUuid
in: path in: path
@ -63,7 +63,7 @@ delete:
description: 'Delete a single hosting asset identified by its uuid, if permitted for the current subject.' description: 'Delete a single hosting asset identified by its uuid, if permitted for the current subject.'
operationId: deleteAssetUuid operationId: deleteAssetUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: assetUuid - name: assetUuid
in: path in: path

View File

@ -1,11 +1,11 @@
get: get:
summary: Returns a filtered list of all hosting assets. summary: Returns a filtered list of all hosting assets.
description: Returns the list of all hosting assets which match the given filters and are visible to the current subject or any of it's assumed roles. description: Returns the list of all hosting assets which match the given filters and are visible to the current user or any of it's assumed roles.
tags: tags:
- hs-hosting-assets - hs-hosting-assets
operationId: listAssets operationId: listAssets
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: projectUuid - name: projectUuid
in: query in: query
@ -47,7 +47,7 @@ post:
- hs-hosting-assets - hs-hosting-assets
operationId: addAsset operationId: addAsset
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
requestBody: requestBody:
description: A JSON object describing the new hosting asset. description: A JSON object describing the new hosting asset.

View File

@ -4,7 +4,7 @@ get:
description: 'Fetch a single bank account by its uuid, if visible for the current subject.' description: 'Fetch a single bank account by its uuid, if visible for the current subject.'
operationId: getBankAccountByUuid operationId: getBankAccountByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: bankAccountUUID - name: bankAccountUUID
in: path in: path
@ -31,7 +31,7 @@ delete:
description: 'Delete a single bank account by its uuid, if permitted for the current subject.' description: 'Delete a single bank account by its uuid, if permitted for the current subject.'
operationId: deleteBankAccountByUuid operationId: deleteBankAccountByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: bankAccountUUID - name: bankAccountUUID
in: path in: path

View File

@ -1,11 +1,11 @@
get: get:
summary: Returns a list of (optionally filtered) bankaccounts. summary: Returns a list of (optionally filtered) bankaccounts.
description: Returns the list of (optionally filtered) bankaccounts which are visible to the current subject or any of it's assumed roles. description: Returns the list of (optionally filtered) bankaccounts which are visible to the current user or any of it's assumed roles.
tags: tags:
- hs-office-bank-accounts - hs-office-bank-accounts
operationId: listBankAccounts operationId: listBankAccounts
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: holder - name: holder
in: query in: query
@ -33,7 +33,7 @@ post:
- hs-office-bank-accounts - hs-office-bank-accounts
operationId: addBankAccount operationId: addBankAccount
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
requestBody: requestBody:
content: content:

View File

@ -4,7 +4,7 @@ get:
description: 'Fetch a single business contact by its uuid, if visible for the current subject.' description: 'Fetch a single business contact by its uuid, if visible for the current subject.'
operationId: getContactByUuid operationId: getContactByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: contactUUID - name: contactUUID
in: path in: path
@ -32,7 +32,7 @@ patch:
description: 'Updates a single contact by its uuid, if permitted for the current subject.' description: 'Updates a single contact by its uuid, if permitted for the current subject.'
operationId: patchContact operationId: patchContact
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: contactUUID - name: contactUUID
in: path in: path
@ -63,7 +63,7 @@ delete:
description: 'Delete a single business contact by its uuid, if permitted for the current subject.' description: 'Delete a single business contact by its uuid, if permitted for the current subject.'
operationId: deleteContactByUuid operationId: deleteContactByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: contactUUID - name: contactUUID
in: path in: path

View File

@ -1,11 +1,11 @@
get: get:
summary: Returns a list of (optionally filtered) contacts. summary: Returns a list of (optionally filtered) contacts.
description: Returns the list of (optionally filtered) contacts which are visible to the current subject or any of it's assumed roles. description: Returns the list of (optionally filtered) contacts which are visible to the current user or any of it's assumed roles.
tags: tags:
- hs-office-contacts - hs-office-contacts
operationId: listContacts operationId: listContacts
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: name - name: name
in: query in: query
@ -33,7 +33,7 @@ post:
- hs-office-contacts - hs-office-contacts
operationId: addContact operationId: addContact
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
requestBody: requestBody:
content: content:

View File

@ -4,7 +4,7 @@ get:
description: 'Fetch a single asset transaction by its uuid, if visible for the current subject.' description: 'Fetch a single asset transaction by its uuid, if visible for the current subject.'
operationId: getCoopAssetTransactionByUuid operationId: getCoopAssetTransactionByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: assetTransactionUUID - name: assetTransactionUUID
in: path in: path

View File

@ -1,11 +1,11 @@
get: get:
summary: Returns a list of (optionally filtered) cooperative asset transactions. summary: Returns a list of (optionally filtered) cooperative asset transactions.
description: Returns the list of (optionally filtered) cooperative asset transactions which are visible to the current subject or any of it's assumed roles. description: Returns the list of (optionally filtered) cooperative asset transactions which are visible to the current user or any of it's assumed roles.
tags: tags:
- hs-office-coopAssets - hs-office-coopAssets
operationId: listCoopAssets operationId: listCoopAssets
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: membershipUuid - name: membershipUuid
in: query in: query
@ -48,7 +48,7 @@ post:
- hs-office-coopAssets - hs-office-coopAssets
operationId: addCoopAssetsTransaction operationId: addCoopAssetsTransaction
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
requestBody: requestBody:
description: A JSON object describing the new cooperative assets transaction. description: A JSON object describing the new cooperative assets transaction.

View File

@ -4,7 +4,7 @@ get:
description: 'Fetch a single share transaction by its uuid, if visible for the current subject.' description: 'Fetch a single share transaction by its uuid, if visible for the current subject.'
operationId: getCoopShareTransactionByUuid operationId: getCoopShareTransactionByUuid
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: shareTransactionUUID - name: shareTransactionUUID
in: path in: path

View File

@ -1,11 +1,11 @@
get: get:
summary: Returns a list of (optionally filtered) cooperative share transactions. summary: Returns a list of (optionally filtered) cooperative share transactions.
description: Returns the list of (optionally filtered) cooperative share transactions which are visible to the current subject or any of it's assumed roles. description: Returns the list of (optionally filtered) cooperative share transactions which are visible to the current user or any of it's assumed roles.
tags: tags:
- hs-office-coopShares - hs-office-coopShares
operationId: listCoopShares operationId: listCoopShares
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: membershipUuid - name: membershipUuid
in: query in: query
@ -48,7 +48,7 @@ post:
- hs-office-coopShares - hs-office-coopShares
operationId: addCoopSharesTransaction operationId: addCoopSharesTransaction
parameters: parameters:
- $ref: 'auth.yaml#/components/parameters/currentSubject' - $ref: 'auth.yaml#/components/parameters/currentUser'
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
requestBody: requestBody:
description: A JSON object describing the new cooperative shares transaction. description: A JSON object describing the new cooperative shares transaction.

Some files were not shown because too many files have changed in this diff Show More