avoid-recursive-rbac-query-for-global-admins in the _rv generator (#216)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/216 Reviewed-by: Marc Sandlus <hsh-marcsandlus@noreply.dev.hostsharing.net>
This commit is contained in:
@@ -0,0 +1,325 @@
|
|||||||
|
# PR#216: RBAC Performance Optimization for Global Admins
|
||||||
|
|
||||||
|
## The Problem
|
||||||
|
|
||||||
|
We have a severe performance problem in SELECT Queries when executed as global-admin.
|
||||||
|
|
||||||
|
The cause of the performance problem is, that if a global-admin runs SELECT queries,
|
||||||
|
they can see all rows but yet the ReBAC filter is still active.
|
||||||
|
In other words, in the case of a SELECT without a WHERE-condition,
|
||||||
|
the ReBAC access rights are checked for each row in the target table.
|
||||||
|
This is horribly expensive because that's a recursive CTE query.
|
||||||
|
|
||||||
|
There was some shortcut in the code (see `procedure rbac.generateRbacRestrictedView` before this merge commit),
|
||||||
|
but which was not really used by the query-optimizer and still the whole recursive CTE query got exectuted.
|
||||||
|
This can be seen below in [Query-Plan before](#query-plan-before).
|
||||||
|
|
||||||
|
## The Solution
|
||||||
|
|
||||||
|
To find a solution, we need mass test-data, a query-plan analysis and a refactored rekursive CTE query.
|
||||||
|
|
||||||
|
### Test-Data Generation
|
||||||
|
|
||||||
|
To be able to do performance-tests, mass test-data was needed.
|
||||||
|
I estimated the production database contains about 400 partner records and 500 SEPA mandate records, both including old ones.
|
||||||
|
For performance-tests I needed similar test data, or better even a bit more to be future safe, e.g. about twice the quantity.
|
||||||
|
|
||||||
|
The test-data script is a stored procedure `procedure hs_office.contact_create_mass_test_data` which is also part of the test-data Liquibase profile, but just the script, no mass test-data is generated automatically.
|
||||||
|
|
||||||
|
You can actually generate mass test-data by running the following SQL commands:
|
||||||
|
|
||||||
|
```PostgresSQL
|
||||||
|
rollback; -- for the case of any previously failed transaction
|
||||||
|
|
||||||
|
-- generate test data for partner numbers 20xxx with 80% membership
|
||||||
|
call hs_office.partner_create_mass_bundle_test_data(20000, 20999, 80);
|
||||||
|
|
||||||
|
-- show some statistics about what was generated
|
||||||
|
select * from hs_statistics_v;
|
||||||
|
|
||||||
|
-- we mostly care about SEPA mandates:
|
||||||
|
select count(*) from hs_office.sepamandate;
|
||||||
|
```
|
||||||
|
|
||||||
|
The last statement will most likely show 1003, 3 from the normal test-data plus 1000 from the mass test data.
|
||||||
|
|
||||||
|
Find the statistics for the database after mass test-data generation in attachment [Mass-Data-Statistics](#attachment-mass-data-statistics).
|
||||||
|
|
||||||
|
|
||||||
|
### The Performance-Test Script
|
||||||
|
|
||||||
|
Find the performance-script in `procedure hs_office.bench_debitor_sepamandates`,
|
||||||
|
which is now part of the Liquibase-changesets for the test-data profile.
|
||||||
|
|
||||||
|
The test can be run this way:
|
||||||
|
|
||||||
|
```PostgreSQL
|
||||||
|
\o /dev/null
|
||||||
|
rollback;
|
||||||
|
call hs_office.bench_debitor_sepamandates(100); -- 100 is the number of loops
|
||||||
|
```
|
||||||
|
|
||||||
|
### Query Plan Analysis
|
||||||
|
|
||||||
|
To get hints about what's going wrong, I did a query-plan analysis:
|
||||||
|
|
||||||
|
```PostgreSQ
|
||||||
|
rollback;
|
||||||
|
|
||||||
|
begin;
|
||||||
|
call base.defineContext( 'query debitor', null, 'superuser-alex@hostsharing.net' );
|
||||||
|
\timing on
|
||||||
|
explain analyze
|
||||||
|
select count(*)
|
||||||
|
from hs_office.debitor d
|
||||||
|
join hs_office.sepamandate_rv s on (s.debitoruuid = d.uuid)
|
||||||
|
join hs_office.bankaccount b on (b.uuid = s.bankAccountUuid);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Query-Plan before
|
||||||
|
|
||||||
|
The following was the query plan with the shortcut-optimization which was not picked up by the query-optimizer,
|
||||||
|
thus on the commit before the merge-commit for this branch.
|
||||||
|
|
||||||
|
No need to read the resulting query-plan in details, simply put, it does way too much.
|
||||||
|
If curious, you can find it [in the attachment](#attachment-query-plan-before)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### Query-Plan after optimization with isGlobalAdmin-cache
|
||||||
|
|
||||||
|
```
|
||||||
|
Aggregate (cost=90.27..90.28 rows=1 width=8) (actual time=14.961..14.982 rows=1 loops=1)
|
||||||
|
-> Hash Join (cost=72.50..87.77 rows=1000 width=0) (actual time=7.692..13.800 rows=1003 loops=1)
|
||||||
|
Hash Cond: (select_hs_office_sepamandate_rv.bankaccountuuid = b.uuid)
|
||||||
|
-> Hash Join (cost=35.82..48.45 rows=1000 width=16) (actual time=3.945..7.648 rows=1003 loops=1)
|
||||||
|
Hash Cond: (select_hs_office_sepamandate_rv.debitoruuid = d.uuid)
|
||||||
|
-> Function Scan on select_hs_office_sepamandate_rv (cost=0.25..10.25 rows=1000 width=32) (actual time=1.018..2.277 rows=1003 loops=1)
|
||||||
|
-> Hash (cost=23.03..23.03 rows=1003 width=16) (actual time=2.894..2.899 rows=1003 loops=1)
|
||||||
|
Buckets: 1024 Batches: 1 Memory Usage: 56kB
|
||||||
|
-> Seq Scan on debitor d (cost=0.00..23.03 rows=1003 width=16) (actual time=0.020..1.464 rows=1003 loops=1)
|
||||||
|
-> Hash (cost=24.08..24.08 rows=1008 width=16) (actual time=3.734..3.738 rows=1008 loops=1)
|
||||||
|
Buckets: 1024 Batches: 1 Memory Usage: 56kB
|
||||||
|
-> Seq Scan on bankaccount b (cost=0.00..24.08 rows=1008 width=16) (actual time=0.036..1.937 rows=1008 loops=1)
|
||||||
|
|
||||||
|
Planning Time: 0.349 ms
|
||||||
|
Execution Time: 15.063 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
This looks like a sensible tidy query plan for the job.
|
||||||
|
|
||||||
|
### Performance-Comparison
|
||||||
|
|
||||||
|
#### case A) with old optimization, freshly generated schema
|
||||||
|
|
||||||
|
```
|
||||||
|
limit10 min/avg/max: 2545.297 ms / 2796.658 ms / 4433.851 ms
|
||||||
|
count all min/avg/max: 2571.942 ms / 2821.460 ms / 3658.856 ms
|
||||||
|
completed in 9 m 22 s 8 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
This clearly shows the problem; up to almost 3 seconds for a single query is too long.
|
||||||
|
|
||||||
|
#### case B) with new optimization, freshly generated schema
|
||||||
|
|
||||||
|
```
|
||||||
|
limit10 min/avg/max: 2418.032 ms / 2656.825 ms / 4212.158 ms
|
||||||
|
count all min/avg/max: 2443.345 ms / 2680.387 ms / 3475.913 ms
|
||||||
|
completed in 8 m 53 s 908 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
My new shortcut implementation in the recursive CTE query did not show much improvement,
|
||||||
|
probably so even no improvement as the difference is statistically just too little.
|
||||||
|
|
||||||
|
#### case C) without optimization, but cached isGlobalAdmin, freshly generated schema
|
||||||
|
|
||||||
|
Now, I went back to the previous shortcut but store the information
|
||||||
|
if the current subject is a global admin as a session-variable.
|
||||||
|
The caching is done right when `global.defineContext()` is called.
|
||||||
|
|
||||||
|
```
|
||||||
|
limit10 min/avg/max: 383.585 ms / 425.593 ms / 1135.419 ms
|
||||||
|
count all min/avg/max: 374.289 ms / 408.141 ms / 474.026 ms
|
||||||
|
completed in 1 m 23 s 437 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
As we can see, this showed some progress, but not enough.
|
||||||
|
|
||||||
|
#### case D) with new optimization + cache, freshly generated schema
|
||||||
|
|
||||||
|
Now I combined both approaches, the new shortcut and cached the isGlobalAdmin information
|
||||||
|
|
||||||
|
```
|
||||||
|
limit10 min/avg/max: 0.806 ms / 1.212 ms / 2.159 ms
|
||||||
|
count all min/avg/max: 1.305 ms / 1.805 ms / 3.469 ms
|
||||||
|
completed in 620 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
This brought the breakthrough; we are now down from almost 4 seconds to below 4 milliseconds,
|
||||||
|
**faster by a factor of 1000**.
|
||||||
|
|
||||||
|
#### case E) with new optimization + cache, upgraded schema
|
||||||
|
|
||||||
|
So far, I always freshly generated the schema.
|
||||||
|
But for our production database, we need to upgrade the existing schema.
|
||||||
|
Unfortunately, parts of the improved implementation are in code that is generated
|
||||||
|
(by `procedure rbac.generateRbacRestrictedView`), thus,
|
||||||
|
not just the generator had to be updated, but also be called for each table with RBAC support.
|
||||||
|
|
||||||
|
To be on the safe side that it really worked, I ran the performance-tests again:
|
||||||
|
|
||||||
|
```
|
||||||
|
limit10 min/avg/max: 0.515 ms / 1.012 ms / 3.464 ms
|
||||||
|
count all min/avg/max: 1.116 ms / 1.801 ms / 2.614 ms
|
||||||
|
completed in 510 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
Which is quite similar to case D, as expected.
|
||||||
|
|
||||||
|
|
||||||
|
### Epilogue
|
||||||
|
|
||||||
|
This performance optimization only works if the current subject is a global admin,
|
||||||
|
the global-admin role may or may not be assumed, but no lower role.
|
||||||
|
|
||||||
|
If any lower role gets assumed or the subject is not granted the global-admin role,
|
||||||
|
the rekursive CTE query still has to be executed.
|
||||||
|
|
||||||
|
This might still be a performance problem, but not as bad as in the case of a global-admin,
|
||||||
|
because normal users cannot see that many objects, nor do they have that many (indirect) grants.
|
||||||
|
Therefore, both the width and the depth of the recursion are much smaller than for global-admins.
|
||||||
|
|
||||||
|
But for users who can see very many objects, e.g. the admin of a large client,
|
||||||
|
there could still be a severe performance problem.
|
||||||
|
|
||||||
|
There are ideas for optimizing the ReBAC-system, which are described in [RBAC Performance Analysis](rbac-performance-analysis.md#the-problematically-huge-join).
|
||||||
|
But these need major changes in the RBAC system, for which we currently have no financial capacity.
|
||||||
|
|
||||||
|
|
||||||
|
## Attachments
|
||||||
|
|
||||||
|
### Attachment: Mass-Data-Statistics
|
||||||
|
|
||||||
|
|
||||||
|
| count | rbac-table | hs-table | type |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| 218 019 | grants | | |
|
||||||
|
| 168 865 | references | | |
|
||||||
|
| 94 370 | permissions | | |
|
||||||
|
| 69 242 | roles | | |
|
||||||
|
| 29 576 | objects | | |
|
||||||
|
| 7 021 | objects | hs\_booking.item | |
|
||||||
|
| 5 253 | login users | | |
|
||||||
|
| 4 818 | objects | hs\_office.coopassettx | |
|
||||||
|
| 3 212 | objects | hs\_office.coopsharetx | |
|
||||||
|
| 3 015 | objects | hs\_office.relation | |
|
||||||
|
| 2 017 | objects | hs\_office.person | |
|
||||||
|
| 2 006 | objects | hs\_booking.item | MANAGED\_WEBSPACE |
|
||||||
|
| 2 006 | objects | hs\_booking.item | MANAGED\_SERVER |
|
||||||
|
| 2 006 | objects | hs\_booking.item | CLOUD\_SERVER |
|
||||||
|
| 1 620 | objects | hs\_hosting.asset | |
|
||||||
|
| 1 012 | objects | hs\_office.contact | |
|
||||||
|
| 1 008 | objects | hs\_office.bankaccount | |
|
||||||
|
| 1 005 | objects | hs\_office.partner | |
|
||||||
|
| 1 005 | objects | hs\_office.partner\_details | |
|
||||||
|
| 1 003 | objects | hs\_office.debitor | |
|
||||||
|
| 1 003 | objects | hs\_booking.item | PRIVATE\_CLOUD |
|
||||||
|
| 1 003 | objects | hs\_booking.project | |
|
||||||
|
| 1 003 | objects | hs\_office.sepamandate | |
|
||||||
|
| 803 | objects | hs\_office.membership | |
|
||||||
|
| 180 | objects | hs\_hosting.asset | UNIX\_USER |
|
||||||
|
| 90 | objects | hs\_hosting.asset | DOMAIN\_SMTP\_SETUP |
|
||||||
|
| 90 | objects | hs\_hosting.asset | EMAIL\_ADDRESS |
|
||||||
|
| 90 | objects | hs\_hosting.asset | CLOUD\_SERVER |
|
||||||
|
| 90 | objects | hs\_hosting.asset | PGSQL\_DATABASE |
|
||||||
|
| 90 | objects | hs\_hosting.asset | MANAGED\_WEBSPACE |
|
||||||
|
| 90 | objects | hs\_hosting.asset | DOMAIN\_SETUP |
|
||||||
|
| 90 | objects | hs\_hosting.asset | MARIADB\_USER |
|
||||||
|
| 90 | objects | hs\_hosting.asset | PGSQL\_USER |
|
||||||
|
| 90 | objects | hs\_hosting.asset | DOMAIN\_MBOX\_SETUP |
|
||||||
|
| 90 | objects | hs\_hosting.asset | DOMAIN\_HTTP\_SETUP |
|
||||||
|
| 90 | objects | hs\_hosting.asset | DOMAIN\_DNS\_SETUP |
|
||||||
|
| 90 | objects | hs\_hosting.asset | MANAGED\_SERVER |
|
||||||
|
| 90 | objects | hs\_hosting.asset | PGSQL\_INSTANCE |
|
||||||
|
| 90 | objects | hs\_hosting.asset | EMAIL\_ALIAS |
|
||||||
|
| 90 | objects | hs\_hosting.asset | MARIADB\_DATABASE |
|
||||||
|
| 90 | objects | hs\_hosting.asset | MARIADB\_INSTANCE |
|
||||||
|
| 18 | objects | rbactest.domain | |
|
||||||
|
| 9 | objects | rbactest.package | |
|
||||||
|
| 3 | objects | rbactest.customer | |
|
||||||
|
| 1 | objects | rbac.global | |
|
||||||
|
|
||||||
|
|
||||||
|
### Attachment: Query-Plan before
|
||||||
|
|
||||||
|
```
|
||||||
|
Aggregate (cost=1800394.72..1800394.73 rows=1 width=8) (actual time=801.557..801.594 rows=1 loops=1)
|
||||||
|
-> Hash Join (cost=1800381.15..1800393.04 rows=669 width=0) (actual time=794.549..800.418 rows=1003 loops=1)
|
||||||
|
Hash Cond: (target.bankaccountuuid = b.uuid)
|
||||||
|
-> Hash Join (cost=1800344.47..1800354.60 rows=669 width=16) (actual time=60.423..63.940 rows=1003 loops=1)
|
||||||
|
Hash Cond: (target.debitoruuid = d.uuid)
|
||||||
|
-> Sort (cost=1800308.90..1800310.57 rows=669 width=280) (actual time=56.670..57.796 rows=1003 loops=1)
|
||||||
|
Sort Key: target.validity
|
||||||
|
Sort Method: quicksort Memory: 87kB
|
||||||
|
CTE accessible_uuids
|
||||||
|
-> HashAggregate (cost=1799078.92..1799361.78 rows=28286 width=16) (never executed)
|
||||||
|
Group Key: perm.objectuuid
|
||||||
|
CTE recursive_grants
|
||||||
|
-> Recursive Union (cost=4655.41..1575457.36 rows=3199924 width=37) (never executed)
|
||||||
|
-> Subquery Scan on "*SELECT* 1" (cost=4655.41..4720.95 rows=6554 width=37) (never executed)
|
||||||
|
-> HashAggregate (cost=4655.41..4720.95 rows=6554 width=37) (never executed)
|
||||||
|
Group Key: "grant".descendantuuid, "grant".ascendantuuid
|
||||||
|
-> Bitmap Heap Scan on "grant" (cost=136.43..4622.27 rows=6629 width=37) (never executed)
|
||||||
|
Recheck Cond: (ascendantuuid = ANY (rbac.currentsubjectorassumedrolesuuids()))
|
||||||
|
Filter: assumed
|
||||||
|
-> Bitmap Index Scan on grant_ascendantuuid_idx (cost=0.00..134.77 rows=6715 width=0) (never executed)
|
||||||
|
Index Cond: (ascendantuuid = ANY (rbac.currentsubjectorassumedrolesuuids()))
|
||||||
|
-> Unique (cost=149882.00..153873.72 rows=319337 width=37) (never executed)
|
||||||
|
-> Sort (cost=149882.00..150680.35 rows=319337 width=37) (never executed)
|
||||||
|
Sort Key: g.descendantuuid, g.ascendantuuid, ((grants.level + 1)), (base.asserttrue((grants.level < 22), ('too many grant-levels: '::text (grants.level)::text)))
|
||||||
|
-> Merge Join (cost=6554.45..111954.57 rows=319337 width=37) (never executed)
|
||||||
|
Merge Cond: (g.ascendantuuid = grants.descendantuuid)
|
||||||
|
-> Index Scan using grant_ascendantuuid_idx on "grant" g (cost=0.42..16246.48 rows=215214 width=32) (never executed)
|
||||||
|
Filter: assumed
|
||||||
|
-> Sort (cost=6554.03..6717.88 rows=65540 width=20) (never executed)
|
||||||
|
Sort Key: grants.descendantuuid
|
||||||
|
-> WorkTable Scan on recursive_grants grants (cost=0.00..1310.80 rows=65540 width=20) (never executed)
|
||||||
|
CTE count_check
|
||||||
|
-> Result (cost=143996.60..143996.87 rows=1 width=1) (never executed)
|
||||||
|
InitPlan 2
|
||||||
|
-> Aggregate (cost=71998.29..71998.30 rows=1 width=8) (never executed)
|
||||||
|
-> CTE Scan on recursive_grants (cost=0.00..63998.48 rows=3199924 width=0) (never executed)
|
||||||
|
InitPlan 3
|
||||||
|
-> Aggregate (cost=71998.29..71998.30 rows=1 width=8) (never executed)
|
||||||
|
-> CTE Scan on recursive_grants recursive_grants_1 (cost=0.00..63998.48 rows=3199924 width=0) (never executed)
|
||||||
|
-> Hash Join (cost=2270.14..79353.40 rows=108518 width=16) (never executed)
|
||||||
|
Hash Cond: (recursive_grants_2.descendantuuid = perm.uuid)
|
||||||
|
-> CTE Scan on recursive_grants recursive_grants_2 (cost=0.00..63998.48 rows=3199924 width=16) (never executed)
|
||||||
|
-> Hash (cost=2230.14..2230.14 rows=3200 width=32) (never executed)
|
||||||
|
-> Hash Join (cost=80.55..2230.14 rows=3200 width=32) (never executed)
|
||||||
|
Hash Cond: (perm.objectuuid = obj.uuid)
|
||||||
|
-> Seq Scan on permission perm (cost=0.00..1763.70 rows=94370 width=32) (never executed)
|
||||||
|
-> Hash (cost=68.02..68.02 rows=1003 width=16) (never executed)
|
||||||
|
-> Nested Loop (cost=0.41..68.02 rows=1003 width=16) (never executed)
|
||||||
|
-> CTE Scan on count_check cc (cost=0.00..0.02 rows=1 width=0) (never executed)
|
||||||
|
Filter: valid
|
||||||
|
-> Index Only Scan using object_objecttable_uuid_key on object obj (cost=0.41..57.97 rows=1003 width=16) (never executed)
|
||||||
|
Index Cond: (objecttable = 'hs_office.sepamandate'::text)
|
||||||
|
Heap Fetches: 0
|
||||||
|
-> Seq Scan on sepamandate target (cost=636.44..915.72 rows=669 width=280) (actual time=1.189..54.696 rows=1003 loops=1)
|
||||||
|
Filter: (rbac.hasglobaladminrole() OR (ANY (uuid = (hashed SubPlan 6).col1)))
|
||||||
|
SubPlan 6
|
||||||
|
-> CTE Scan on accessible_uuids (cost=0.00..565.72 rows=28286 width=16) (never executed)
|
||||||
|
-> Hash (cost=23.03..23.03 rows=1003 width=16) (actual time=3.680..3.683 rows=1003 loops=1)
|
||||||
|
Buckets: 1024 Batches: 1 Memory Usage: 56kB
|
||||||
|
-> Seq Scan on debitor d (cost=0.00..23.03 rows=1003 width=16) (actual time=0.168..1.824 rows=1003 loops=1)
|
||||||
|
-> Hash (cost=24.08..24.08 rows=1008 width=16) (actual time=734.056..734.059 rows=1008 loops=1)
|
||||||
|
Buckets: 1024 Batches: 1 Memory Usage: 56kB
|
||||||
|
-> Seq Scan on bankaccount b (cost=0.00..24.08 rows=1008 width=16) (actual time=731.222..732.672 rows=1008 loops=1)
|
||||||
|
Planning Time: 3.765 ms
|
||||||
|
JIT:
|
||||||
|
Functions: 81
|
||||||
|
Options: Inlining true, Optimization true, Expressions true, Deforming true
|
||||||
|
Timing: Generation 9.230 ms (Deform 3.886 ms), Inlining 201.353 ms, Optimization 312.388 ms, Emission 217.583 ms, Total 740.554 ms
|
||||||
|
Execution Time: 888.157 ms
|
||||||
|
```
|
||||||
@@ -83,7 +83,7 @@ begin
|
|||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset michael.hoennig:rbac-context-CONTEXT-DEFINED endDelimiter:--//
|
--changeset michael.hoennig:rbac-context-CONTEXT-DEFINED runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
/*
|
||||||
Callback which is called after the context has been (re-) defined.
|
Callback which is called after the context has been (re-) defined.
|
||||||
@@ -98,6 +98,7 @@ create or replace procedure base.contextDefined(
|
|||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
declare
|
declare
|
||||||
currentSubjectUuid uuid;
|
currentSubjectUuid uuid;
|
||||||
|
currentSubjectHasGlobalAdminRole boolean;
|
||||||
begin
|
begin
|
||||||
execute format('set local hsadminng.currentTask to %L', currentTask);
|
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||||
|
|
||||||
@@ -111,6 +112,13 @@ begin
|
|||||||
execute format('set local hsadminng.currentSubjectOrAssumedRolesUuids to %L',
|
execute format('set local hsadminng.currentSubjectOrAssumedRolesUuids to %L',
|
||||||
(select array_to_string(rbac.determineCurrentSubjectOrAssumedRolesUuids(currentSubjectUuid, assumedRoles), ';')));
|
(select array_to_string(rbac.determineCurrentSubjectOrAssumedRolesUuids(currentSubjectUuid, assumedRoles), ';')));
|
||||||
|
|
||||||
|
if currentSubjectUuid is null then
|
||||||
|
currentSubjectHasGlobalAdminRole := false;
|
||||||
|
else
|
||||||
|
currentSubjectHasGlobalAdminRole := rbac.isGranted(array[currentSubjectUuid], rbac.findRoleId(rbac.global_ADMIN()));
|
||||||
|
end if;
|
||||||
|
execute format('set local hsadminng.isGlobalAdmin to %L', currentSubjectHasGlobalAdminRole::text);
|
||||||
|
|
||||||
raise notice 'Context defined as: %, %, %, [%]', currentTask, currentRequest, currentSubject, assumedRoles;
|
raise notice 'Context defined as: %, %, %, [%]', currentTask, currentRequest, currentSubject, assumedRoles;
|
||||||
end; $$;
|
end; $$;
|
||||||
|
|
||||||
@@ -181,4 +189,3 @@ begin
|
|||||||
return string_to_array(currentSubjectOrAssumedRolesUuids, ';');
|
return string_to_array(currentSubjectOrAssumedRolesUuids, ';');
|
||||||
end; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|||||||
@@ -179,8 +179,10 @@ create or replace procedure rbac.generateRbacRestrictedView(targetTable text, or
|
|||||||
declare
|
declare
|
||||||
sql text;
|
sql text;
|
||||||
newColumns text;
|
newColumns text;
|
||||||
|
functionName text;
|
||||||
begin
|
begin
|
||||||
targetTable := lower(targetTable);
|
targetTable := lower(targetTable);
|
||||||
|
functionName := replace(targetTable, '.', '_');
|
||||||
if columnNames = '*' then
|
if columnNames = '*' then
|
||||||
columnNames := base.tableColumnNames(targetTable);
|
columnNames := base.tableColumnNames(targetTable);
|
||||||
end if;
|
end if;
|
||||||
@@ -189,45 +191,62 @@ begin
|
|||||||
Creates a restricted view based on the 'SELECT' permission of the current subject.
|
Creates a restricted view based on the 'SELECT' permission of the current subject.
|
||||||
*/
|
*/
|
||||||
sql := format($sql$
|
sql := format($sql$
|
||||||
|
create or replace function rbac.select_%3$s_rv()
|
||||||
|
returns setof %1$s
|
||||||
|
language plpgsql
|
||||||
|
as $f$
|
||||||
|
begin
|
||||||
|
if rbac.hasGlobalAdminRole() then
|
||||||
|
return query
|
||||||
|
select target.*
|
||||||
|
from %1$s as target
|
||||||
|
order by %2$s;
|
||||||
|
else
|
||||||
|
return query
|
||||||
|
with accessible_uuids as (
|
||||||
|
with recursive
|
||||||
|
recursive_grants as
|
||||||
|
(select distinct rbac.grant.descendantuuid,
|
||||||
|
rbac.grant.ascendantuuid,
|
||||||
|
1 as level,
|
||||||
|
true
|
||||||
|
from rbac.grant
|
||||||
|
where rbac.grant.assumed
|
||||||
|
and (rbac.grant.ascendantuuid = any (rbac.currentSubjectOrAssumedRolesUuids()))
|
||||||
|
union all
|
||||||
|
select distinct g.descendantuuid,
|
||||||
|
g.ascendantuuid,
|
||||||
|
grants.level + 1 as level,
|
||||||
|
base.assertTrue(grants.level < 22, 'too many grant-levels: ' || grants.level)
|
||||||
|
from rbac.grant g
|
||||||
|
join recursive_grants grants on grants.descendantuuid = g.ascendantuuid
|
||||||
|
where g.assumed),
|
||||||
|
grant_count AS (
|
||||||
|
SELECT COUNT(*) AS grant_count FROM recursive_grants
|
||||||
|
),
|
||||||
|
count_check as (select base.assertTrue((select count(*) as grant_count from recursive_grants) < 400000,
|
||||||
|
'too many grants for current subjects: ' || (select count(*) as grant_count from recursive_grants))
|
||||||
|
as valid)
|
||||||
|
select distinct perm.objectuuid
|
||||||
|
from recursive_grants
|
||||||
|
join rbac.permission perm on recursive_grants.descendantuuid = perm.uuid
|
||||||
|
join rbac.object obj on obj.uuid = perm.objectuuid
|
||||||
|
join count_check cc on cc.valid
|
||||||
|
where obj.objectTable = '%1$s' -- 'SELECT' permission is included in all other permissions
|
||||||
|
)
|
||||||
|
select target.*
|
||||||
|
from %1$s as target
|
||||||
|
where target.uuid in (select * from accessible_uuids)
|
||||||
|
order by %2$s;
|
||||||
|
end if;
|
||||||
|
end;
|
||||||
|
$f$;
|
||||||
|
|
||||||
create or replace view %1$s_rv as
|
create or replace view %1$s_rv as
|
||||||
with accessible_uuids as (
|
select * from rbac.select_%3$s_rv();
|
||||||
with recursive
|
|
||||||
recursive_grants as
|
|
||||||
(select distinct rbac.grant.descendantuuid,
|
|
||||||
rbac.grant.ascendantuuid,
|
|
||||||
1 as level,
|
|
||||||
true
|
|
||||||
from rbac.grant
|
|
||||||
where rbac.grant.assumed
|
|
||||||
and (rbac.grant.ascendantuuid = any (rbac.currentSubjectOrAssumedRolesUuids()))
|
|
||||||
union all
|
|
||||||
select distinct g.descendantuuid,
|
|
||||||
g.ascendantuuid,
|
|
||||||
grants.level + 1 as level,
|
|
||||||
base.assertTrue(grants.level < 22, 'too many grant-levels: ' || grants.level)
|
|
||||||
from rbac.grant g
|
|
||||||
join recursive_grants grants on grants.descendantuuid = g.ascendantuuid
|
|
||||||
where g.assumed),
|
|
||||||
grant_count AS (
|
|
||||||
SELECT COUNT(*) AS grant_count FROM recursive_grants
|
|
||||||
),
|
|
||||||
count_check as (select base.assertTrue((select count(*) as grant_count from recursive_grants) < 400000,
|
|
||||||
'too many grants for current subjects: ' || (select count(*) as grant_count from recursive_grants))
|
|
||||||
as valid)
|
|
||||||
select distinct perm.objectuuid
|
|
||||||
from recursive_grants
|
|
||||||
join rbac.permission perm on recursive_grants.descendantuuid = perm.uuid
|
|
||||||
join rbac.object obj on obj.uuid = perm.objectuuid
|
|
||||||
join count_check cc on cc.valid
|
|
||||||
where obj.objectTable = '%1$s' -- 'SELECT' permission is included in all other permissions
|
|
||||||
)
|
|
||||||
select target.*
|
|
||||||
from %1$s as target
|
|
||||||
where rbac.hasGlobalAdminRole() or target.uuid in (select * from accessible_uuids)
|
|
||||||
order by %2$s;
|
|
||||||
|
|
||||||
grant all privileges on %1$s_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
|
grant all privileges on %1$s_rv to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
|
||||||
$sql$, targetTable, orderBy);
|
$sql$, targetTable, orderBy, functionName);
|
||||||
execute sql;
|
execute sql;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -23,23 +23,34 @@ grant select on rbac.global to ${HSADMINNG_POSTGRES_RESTRICTED_USERNAME};
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset michael.hoennig:rbac-global-IS-GLOBAL-ADMIN endDelimiter:--//
|
--changeset michael.hoennig:rbac-global-IS-GLOBAL-ADMIN runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ------------------------------------------------------------------
|
-- ------------------------------------------------------------------
|
||||||
|
|
||||||
create or replace function rbac.isGlobalAdmin()
|
create or replace function rbac.isGlobalAdmin()
|
||||||
returns boolean
|
returns boolean
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
isGlobalAdmin text;
|
||||||
begin
|
begin
|
||||||
return rbac.isGranted(rbac.currentSubjectOrAssumedRolesUuids(), rbac.findRoleId(rbac.global_ADMIN()));
|
isGlobalAdmin := current_setting('hsadminng.isGlobalAdmin', true);
|
||||||
|
if isGlobalAdmin is not null then
|
||||||
|
return isGlobalAdmin::boolean;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
raise exception '`hsadminng.isGlobalAdmin` should have been set by `rbac.defineContext()`';
|
||||||
end; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset michael.hoennig:rbac-global-HAS-GLOBAL-ADMIN-ROLE endDelimiter:--//
|
--changeset michael.hoennig:rbac-global-HAS-GLOBAL-ADMIN-ROLE runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
/*
|
||||||
Returns true if the current user is a global admin and has no assumed role.
|
Returns true if the current user is a global admin and has no assumed role.
|
||||||
|
|
||||||
|
ATTENTION: It's false if the global-admin role is assumed,
|
||||||
|
because the global admin role does not have the global admin role, but it is the global admin role.
|
||||||
|
The differentiation is important for the cases where this function is used.
|
||||||
*/
|
*/
|
||||||
create or replace function rbac.hasGlobalAdminRole()
|
create or replace function rbac.hasGlobalAdminRole()
|
||||||
returns boolean
|
returns boolean
|
||||||
|
|||||||
+1
@@ -167,6 +167,7 @@ call rbac.generateRbacIdentityViewFromProjection('rbactest.customer',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:rbactest-customer-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:rbactest-customer-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('rbactest.customer',
|
call rbac.generateRbacRestrictedView('rbactest.customer',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
reference
|
reference
|
||||||
|
|||||||
+1
@@ -232,6 +232,7 @@ call rbac.generateRbacIdentityViewFromProjection('rbactest.package',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:rbactest-package-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:rbactest-package-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('rbactest.package',
|
call rbac.generateRbacRestrictedView('rbactest.package',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
name
|
name
|
||||||
|
|||||||
+1
@@ -231,6 +231,7 @@ call rbac.generateRbacIdentityViewFromProjection('rbactest.domain',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:rbactest-domain-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:rbactest-domain-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('rbactest.domain',
|
call rbac.generateRbacRestrictedView('rbactest.domain',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
name
|
name
|
||||||
|
|||||||
+1
@@ -90,6 +90,7 @@ call rbac.generateRbacIdentityViewFromProjection('hs_office.contact',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-contact-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-contact-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.contact',
|
call rbac.generateRbacRestrictedView('hs_office.contact',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
caption
|
caption
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ call rbac.generateRbacIdentityViewFromProjection('hs_office.person',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-person-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-person-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.person',
|
call rbac.generateRbacRestrictedView('hs_office.person',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
concat(tradeName, familyName, givenName)
|
concat(tradeName, familyName, givenName)
|
||||||
|
|||||||
+1
@@ -245,6 +245,7 @@ call rbac.generateRbacIdentityViewFromProjection('hs_office.relation',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-relation-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-relation-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.relation',
|
call rbac.generateRbacRestrictedView('hs_office.relation',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
(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)
|
||||||
|
|||||||
+1
@@ -244,6 +244,7 @@ call rbac.generateRbacIdentityViewFromProjection('hs_office.partner',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-partner-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-partner-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.partner',
|
call rbac.generateRbacRestrictedView('hs_office.partner',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
'P-' || partnerNumber
|
'P-' || partnerNumber
|
||||||
|
|||||||
+1
@@ -151,6 +151,7 @@ call rbac.generateRbacIdentityViewFromQuery('hs_office.partner_details',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-partner-details-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-partner-details-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.partner_details',
|
call rbac.generateRbacRestrictedView('hs_office.partner_details',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
uuid
|
uuid
|
||||||
|
|||||||
+1
@@ -90,6 +90,7 @@ call rbac.generateRbacIdentityViewFromProjection('hs_office.bankaccount',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-bankaccount-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-bankaccount-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.bankaccount',
|
call rbac.generateRbacRestrictedView('hs_office.bankaccount',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
iban
|
iban
|
||||||
|
|||||||
+1
@@ -226,6 +226,7 @@ call rbac.generateRbacIdentityViewFromQuery('hs_office.debitor',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-debitor-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-debitor-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.debitor',
|
call rbac.generateRbacRestrictedView('hs_office.debitor',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
defaultPrefix
|
defaultPrefix
|
||||||
|
|||||||
+1
@@ -200,6 +200,7 @@ call rbac.generateRbacIdentityViewFromQuery('hs_office.sepamandate',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-sepamandate-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-sepamandate-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.sepamandate',
|
call rbac.generateRbacRestrictedView('hs_office.sepamandate',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
validity
|
validity
|
||||||
|
|||||||
+1
@@ -182,6 +182,7 @@ call rbac.generateRbacIdentityViewFromQuery('hs_office.membership',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-membership-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-membership-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.membership',
|
call rbac.generateRbacRestrictedView('hs_office.membership',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
validity
|
validity
|
||||||
|
|||||||
+1
@@ -155,6 +155,7 @@ call rbac.generateRbacIdentityViewFromProjection('hs_office.coopsharetx',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-coopsharetx-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-coopsharetx-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.coopsharetx',
|
call rbac.generateRbacRestrictedView('hs_office.coopsharetx',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
reference
|
reference
|
||||||
|
|||||||
+1
@@ -155,6 +155,7 @@ call rbac.generateRbacIdentityViewFromProjection('hs_office.coopassettx',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-office-coopassettx-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-office-coopassettx-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_office.coopassettx',
|
call rbac.generateRbacRestrictedView('hs_office.coopassettx',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
reference
|
reference
|
||||||
|
|||||||
+1
@@ -194,6 +194,7 @@ call rbac.generateRbacIdentityViewFromQuery('hs_booking.project',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-booking-project-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-booking-project-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_booking.project',
|
call rbac.generateRbacRestrictedView('hs_booking.project',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
caption
|
caption
|
||||||
|
|||||||
+1
@@ -263,6 +263,7 @@ call rbac.generateRbacIdentityViewFromProjection('hs_booking.item',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-booking-item-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-booking-item-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_booking.item',
|
call rbac.generateRbacRestrictedView('hs_booking.item',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
validity
|
validity
|
||||||
|
|||||||
+1
@@ -168,6 +168,7 @@ call rbac.generateRbacIdentityViewFromProjection('hs_hosting.asset',
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset RbacRestrictedViewGenerator:hs-hosting-asset-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
--changeset RbacRestrictedViewGenerator:hs-hosting-asset-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
-- trigger change of change in generateRbacRestrictedView regarding #453 optimization for global:ADMIN
|
||||||
call rbac.generateRbacRestrictedView('hs_hosting.asset',
|
call rbac.generateRbacRestrictedView('hs_hosting.asset',
|
||||||
$orderBy$
|
$orderBy$
|
||||||
identifier
|
identifier
|
||||||
|
|||||||
@@ -0,0 +1,586 @@
|
|||||||
|
--liquibase formatted sql
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset michael.hoennig:hs-mass-test-data-GENERATORS context:!without-test-data endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Loop-based mass test-data generators for office/booking/hosting/accounts.
|
||||||
|
*/
|
||||||
|
create or replace procedure hs_office.contact_create_mass_test_data(
|
||||||
|
startCount integer,
|
||||||
|
endCount integer,
|
||||||
|
captionPrefix varchar default 'mass contact '
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
begin
|
||||||
|
for t in startCount..endCount
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass contact test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
call hs_office.contact_create_test_data(captionPrefix || base.intToVarChar(t, 4));
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.person_create_mass_test_data(
|
||||||
|
startCount integer,
|
||||||
|
endCount integer
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
idx varchar;
|
||||||
|
begin
|
||||||
|
for t in startCount..endCount
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass person test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
idx := base.intToVarChar(t, 4);
|
||||||
|
if t % 5 = 0 then
|
||||||
|
call hs_office.person_create_test_data('NP', null, 'MassFamily' || idx, 'MassGiven' || idx, true);
|
||||||
|
else
|
||||||
|
call hs_office.person_create_test_data('LP', 'Mass Partner ' || idx || ' GmbH', null, null, true);
|
||||||
|
end if;
|
||||||
|
call hs_office.person_create_test_data('NP', null, 'MassRep' || idx, 'User' || idx, true);
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.relation_create_mass_test_data(
|
||||||
|
startCount integer,
|
||||||
|
endCount integer,
|
||||||
|
mandantTradeName varchar default 'Hostsharing eG',
|
||||||
|
contactCaptionPrefix varchar default 'mass contact '
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
idx varchar;
|
||||||
|
partnerPersonName varchar;
|
||||||
|
representativeFamilyName varchar;
|
||||||
|
contactCaption varchar;
|
||||||
|
mandantPerson hs_office.person;
|
||||||
|
partnerPerson hs_office.person;
|
||||||
|
representativePerson hs_office.person;
|
||||||
|
contact hs_office.contact;
|
||||||
|
begin
|
||||||
|
select p.* into mandantPerson from hs_office.person p where p.tradeName = mandantTradeName;
|
||||||
|
if mandantPerson is null then
|
||||||
|
raise exception 'mandant "%" not found', mandantTradeName;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
for t in startCount..endCount
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass relation test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
idx := base.intToVarChar(t, 4);
|
||||||
|
partnerPersonName := case when t % 5 = 0 then 'MassFamily' || idx else 'Mass Partner ' || idx || ' GmbH' end;
|
||||||
|
representativeFamilyName := 'MassRep' || idx;
|
||||||
|
contactCaption := contactCaptionPrefix || idx;
|
||||||
|
|
||||||
|
select p.* into partnerPerson
|
||||||
|
from hs_office.person p
|
||||||
|
where p.tradeName = partnerPersonName or p.familyName = partnerPersonName;
|
||||||
|
select p.* into representativePerson from hs_office.person p where p.familyName = representativeFamilyName;
|
||||||
|
select c.* into contact from hs_office.contact c where c.caption = contactCaption;
|
||||||
|
|
||||||
|
if partnerPerson is null or representativePerson is null or contact is null then
|
||||||
|
raise exception 'missing mass test base data for index %', idx;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if not exists (
|
||||||
|
select 1 from hs_office.relation r
|
||||||
|
where r.type = 'PARTNER' and r.anchorUuid = mandantPerson.uuid and r.holderUuid = partnerPerson.uuid
|
||||||
|
) then
|
||||||
|
call hs_office.relation_create_test_data(partnerPersonName, 'PARTNER', mandantTradeName, contactCaption);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if not exists (
|
||||||
|
select 1 from hs_office.relation r
|
||||||
|
where r.type = 'REPRESENTATIVE' and r.anchorUuid = partnerPerson.uuid and r.holderUuid = representativePerson.uuid and r.contactUuid = contact.uuid
|
||||||
|
) then
|
||||||
|
call hs_office.relation_create_test_data(representativeFamilyName, 'REPRESENTATIVE', partnerPersonName, contactCaption);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if not exists (
|
||||||
|
select 1 from hs_office.relation r
|
||||||
|
where r.type = 'DEBITOR' and r.anchorUuid = partnerPerson.uuid and r.holderUuid = partnerPerson.uuid and r.contactUuid = contact.uuid
|
||||||
|
) then
|
||||||
|
call hs_office.relation_create_test_data(partnerPersonName, 'DEBITOR', partnerPersonName, contactCaption);
|
||||||
|
end if;
|
||||||
|
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.partner_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5),
|
||||||
|
mandantTradeName varchar default 'Hostsharing eG',
|
||||||
|
contactCaptionPrefix varchar default 'mass contact '
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
idx varchar;
|
||||||
|
partnerPersonName varchar;
|
||||||
|
contactCaption varchar;
|
||||||
|
begin
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass partner test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
idx := base.intToVarChar(t, 4);
|
||||||
|
partnerPersonName := case when t % 5 = 0 then 'MassFamily' || idx else 'Mass Partner ' || idx || ' GmbH' end;
|
||||||
|
contactCaption := contactCaptionPrefix || idx;
|
||||||
|
|
||||||
|
if not exists (select 1 from hs_office.partner p where p.partnerNumber = t) then
|
||||||
|
call hs_office.partner_create_test_data(mandantTradeName, t, partnerPersonName, contactCaption);
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.bankaccount_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5)
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
idx varchar;
|
||||||
|
v_holder varchar;
|
||||||
|
v_iban varchar;
|
||||||
|
v_bic varchar;
|
||||||
|
begin
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass bankaccount test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
idx := base.intToVarChar(t, 4);
|
||||||
|
v_holder := case when t % 5 = 0 then 'MassFamily' || idx else 'Mass Partner ' || idx || ' GmbH' end;
|
||||||
|
v_iban := 'DE' || lpad(t::text, 20, '0');
|
||||||
|
v_bic := 'MASSDEFF' || lpad((t % 1000)::text, 3, '0');
|
||||||
|
|
||||||
|
if not exists (
|
||||||
|
select 1 from hs_office.bankaccount b where b.holder = v_holder and b.iban = v_iban
|
||||||
|
) then
|
||||||
|
call hs_office.bankaccount_create_test_data(v_holder, v_iban, v_bic);
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.debitor_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5),
|
||||||
|
contactCaptionPrefix varchar default 'mass contact '
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
idx varchar;
|
||||||
|
partnerPersonName varchar;
|
||||||
|
suffixNum integer;
|
||||||
|
suffixText char(2);
|
||||||
|
defaultPrefix char(3);
|
||||||
|
begin
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass debitor test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
idx := base.intToVarChar(t, 4);
|
||||||
|
partnerPersonName := case when t % 5 = 0 then 'MassFamily' || idx else 'Mass Partner ' || idx || ' GmbH' end;
|
||||||
|
suffixNum := 10 + (t % 90);
|
||||||
|
suffixText := lpad(suffixNum::text, 2, '0');
|
||||||
|
defaultPrefix := lower(
|
||||||
|
chr(97 + ((t / 676) % 26)) ||
|
||||||
|
chr(97 + ((t / 26) % 26)) ||
|
||||||
|
chr(97 + (t % 26))
|
||||||
|
);
|
||||||
|
|
||||||
|
if not exists (
|
||||||
|
select 1
|
||||||
|
from hs_office.debitor d
|
||||||
|
join hs_office.relation debitorRel on debitorRel.uuid = d.debitorRelUuid and debitorRel.type = 'DEBITOR'
|
||||||
|
join hs_office.relation partnerRel on partnerRel.holderUuid = debitorRel.anchorUuid and partnerRel.type = 'PARTNER'
|
||||||
|
join hs_office.partner p on p.partnerRelUuid = partnerRel.uuid
|
||||||
|
where p.partnerNumber = t and d.debitorNumberSuffix = suffixText
|
||||||
|
) then
|
||||||
|
call hs_office.debitor_create_test_data(suffixNum, partnerPersonName, contactCaptionPrefix || idx, defaultPrefix);
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.sepamandate_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5)
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
suffixText char(2);
|
||||||
|
iban varchar;
|
||||||
|
begin
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass sepa-mandate test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
suffixText := lpad((10 + (t % 90))::text, 2, '0');
|
||||||
|
iban := 'DE' || lpad(t::text, 20, '0');
|
||||||
|
|
||||||
|
if not exists (
|
||||||
|
select 1
|
||||||
|
from hs_office.sepamandate sm
|
||||||
|
join hs_office.debitor d on d.uuid = sm.debitorUuid
|
||||||
|
join hs_office.relation debitorRel on debitorRel.uuid = d.debitorRelUuid
|
||||||
|
join hs_office.relation partnerRel on partnerRel.holderUuid = debitorRel.anchorUuid
|
||||||
|
join hs_office.partner p on p.partnerRelUuid = partnerRel.uuid
|
||||||
|
where p.partnerNumber = t and d.debitorNumberSuffix = suffixText
|
||||||
|
) then
|
||||||
|
call hs_office.sepamandate_create_test_data(t, suffixText, iban, 'mass-ref-' || t::text || '-' || suffixText);
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.membership_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5),
|
||||||
|
withMembershipPercentage integer default 80
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
memberSuffix char(2);
|
||||||
|
begin
|
||||||
|
if withMembershipPercentage < 0 or withMembershipPercentage > 100 then
|
||||||
|
raise exception 'withMembershipPercentage must be between 0 and 100';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass membership test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
if (t % 100) < withMembershipPercentage then
|
||||||
|
memberSuffix := lpad((10 + (t % 90))::text, 2, '0');
|
||||||
|
call hs_office.membership_create_test_data(t, memberSuffix, daterange('20221001', null, '[]'), 'ACTIVE');
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.coopsharetx_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5),
|
||||||
|
withMembershipPercentage integer default 80
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
memberSuffix char(2);
|
||||||
|
v_membershipUuid uuid;
|
||||||
|
begin
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass coop-sharetx test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
if (t % 100) < withMembershipPercentage then
|
||||||
|
memberSuffix := lpad((10 + (t % 90))::text, 2, '0');
|
||||||
|
select m.uuid into v_membershipUuid
|
||||||
|
from hs_office.membership m
|
||||||
|
join hs_office.partner p on p.uuid = m.partnerUuid
|
||||||
|
where p.partnerNumber = t and m.memberNumberSuffix = memberSuffix;
|
||||||
|
|
||||||
|
if v_membershipUuid is not null and not exists (
|
||||||
|
select 1 from hs_office.coopsharetx tx where tx.membershipUuid = v_membershipUuid
|
||||||
|
) then
|
||||||
|
call hs_office.coopsharetx_create_test_data(t, memberSuffix);
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.coopassettx_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5),
|
||||||
|
withMembershipPercentage integer default 80
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
memberSuffix char(2);
|
||||||
|
v_membershipUuid uuid;
|
||||||
|
begin
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass coop-assettx test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
if (t % 100) < withMembershipPercentage then
|
||||||
|
memberSuffix := lpad((10 + (t % 90))::text, 2, '0');
|
||||||
|
select m.uuid into v_membershipUuid
|
||||||
|
from hs_office.membership m
|
||||||
|
join hs_office.partner p on p.uuid = m.partnerUuid
|
||||||
|
where p.partnerNumber = t and m.memberNumberSuffix = memberSuffix;
|
||||||
|
|
||||||
|
if v_membershipUuid is not null and not exists (
|
||||||
|
select 1 from hs_office.coopassettx tx where tx.membershipUuid = v_membershipUuid
|
||||||
|
) then
|
||||||
|
call hs_office.coopassettx_create_test_data(t, memberSuffix);
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_booking.project_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5)
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
suffixText char(2);
|
||||||
|
begin
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass booking-project test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
suffixText := lpad((10 + (t % 90))::text, 2, '0');
|
||||||
|
if not exists (
|
||||||
|
select 1 from hs_booking.project p where p.caption = 'D-' || t::text || suffixText || ' default project'
|
||||||
|
) then
|
||||||
|
call hs_booking.project_create_test_data(t, suffixText);
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_booking.item_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5)
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
suffixText char(2);
|
||||||
|
v_projectUuid uuid;
|
||||||
|
begin
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass booking-item test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
suffixText := lpad((10 + (t % 90))::text, 2, '0');
|
||||||
|
select p.uuid into v_projectUuid from hs_booking.project p where p.caption = 'D-' || t::text || suffixText || ' default project';
|
||||||
|
if v_projectUuid is not null and not exists (
|
||||||
|
select 1 from hs_booking.item i where i.projectUuid = v_projectUuid
|
||||||
|
) then
|
||||||
|
call hs_booking.item_create_test_data(t, suffixText);
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_hosting.asset_create_mass_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5)
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
suffixText char(2);
|
||||||
|
projectCaption varchar;
|
||||||
|
v_debitorNumberSuffix char(2);
|
||||||
|
v_defaultPrefix char(3);
|
||||||
|
begin
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass hosting-asset test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
suffixText := lpad((10 + (t % 90))::text, 2, '0');
|
||||||
|
projectCaption := 'D-' || t::text || suffixText || ' default project';
|
||||||
|
select d.debitorNumberSuffix, d.defaultPrefix
|
||||||
|
into v_debitorNumberSuffix, v_defaultPrefix
|
||||||
|
from hs_booking.project p
|
||||||
|
join hs_office.debitor d on d.uuid = p.debitorUuid
|
||||||
|
where p.caption = projectCaption;
|
||||||
|
if v_debitorNumberSuffix is not null
|
||||||
|
and not exists (
|
||||||
|
select 1
|
||||||
|
from hs_hosting.asset a
|
||||||
|
join hs_booking.item i on i.uuid = a.bookingItemUuid
|
||||||
|
join hs_booking.project p on p.uuid = i.projectUuid
|
||||||
|
where p.caption = projectCaption
|
||||||
|
)
|
||||||
|
and not exists (
|
||||||
|
select 1 from hs_hosting.asset a
|
||||||
|
where (a.type = 'MANAGED_SERVER' and a.identifier = 'vm10' || v_debitorNumberSuffix)
|
||||||
|
or (a.type = 'CLOUD_SERVER' and a.identifier = 'vm20' || v_debitorNumberSuffix)
|
||||||
|
or (a.type = 'MANAGED_WEBSPACE' and a.identifier = v_defaultPrefix || '01')
|
||||||
|
or (a.type = 'MARIADB_INSTANCE' and a.identifier = 'vm10' || v_debitorNumberSuffix || '.MariaDB.default')
|
||||||
|
or (a.type = 'MARIADB_USER' and a.identifier = v_defaultPrefix || '01_web')
|
||||||
|
or (a.type = 'MARIADB_DATABASE' and a.identifier = v_defaultPrefix || '01_web')
|
||||||
|
or (a.type = 'PGSQL_INSTANCE' and a.identifier = 'vm10' || v_debitorNumberSuffix || '.Postgresql.default')
|
||||||
|
or (a.type = 'PGSQL_USER' and a.identifier = v_defaultPrefix || '01_web')
|
||||||
|
or (a.type = 'PGSQL_DATABASE' and a.identifier = v_defaultPrefix || '01_web')
|
||||||
|
or (a.type = 'EMAIL_ALIAS' and a.identifier = v_defaultPrefix || '01-web')
|
||||||
|
or (a.type = 'UNIX_USER' and a.identifier = v_defaultPrefix || '01-web')
|
||||||
|
or (a.type = 'UNIX_USER' and a.identifier = v_defaultPrefix || '01-mbox')
|
||||||
|
or (a.type = 'DOMAIN_SETUP' and a.identifier = v_defaultPrefix || '.example.org')
|
||||||
|
or (a.type = 'DOMAIN_DNS_SETUP' and a.identifier = v_defaultPrefix || '.example.org|DNS')
|
||||||
|
or (a.type = 'DOMAIN_HTTP_SETUP' and a.identifier = v_defaultPrefix || '.example.org|HTTP')
|
||||||
|
or (a.type = 'DOMAIN_SMTP_SETUP' and a.identifier = v_defaultPrefix || '.example.org|SMTP')
|
||||||
|
or (a.type = 'DOMAIN_MBOX_SETUP' and a.identifier = v_defaultPrefix || '.example.org|MBOX')
|
||||||
|
or (a.type = 'EMAIL_ADDRESS' and a.identifier = 'test@' || v_defaultPrefix || '.example.org')
|
||||||
|
) then
|
||||||
|
call hs_hosting.asset_create_test_data(projectCaption);
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.person_create_mass_test_data_for_accounts(
|
||||||
|
startCount integer,
|
||||||
|
endCount integer
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
idx varchar;
|
||||||
|
begin
|
||||||
|
for t in startCount..endCount
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass account-person test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
idx := base.intToVarChar(t, 4);
|
||||||
|
call hs_office.person_create_test_data('NP', null, 'MassAccountFamily' || idx, 'MassAccountGiven' || idx, true);
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_accounts.account_create_mass_test_data(
|
||||||
|
startCount integer,
|
||||||
|
endCount integer,
|
||||||
|
emailPrefix varchar default 'mass-account-',
|
||||||
|
uidOffset integer default 200000
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
idx varchar;
|
||||||
|
accountEmail varchar;
|
||||||
|
subjectUuid uuid;
|
||||||
|
personUuid uuid;
|
||||||
|
begin
|
||||||
|
for t in startCount..endCount
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass profile test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
idx := base.intToVarChar(t, 4);
|
||||||
|
accountEmail := emailPrefix || idx || '@example.com';
|
||||||
|
select p.uuid into personUuid
|
||||||
|
from hs_office.person p
|
||||||
|
where p.familyName = 'MassAccountFamily' || idx and p.givenName = 'MassAccountGiven' || idx;
|
||||||
|
|
||||||
|
if personUuid is not null and not exists (
|
||||||
|
select 1 from hs_accounts.account pr where pr.person_uuid = personUuid
|
||||||
|
) then
|
||||||
|
perform rbac.create_subject(accountEmail);
|
||||||
|
select s.uuid into subjectUuid from rbac.subject s where s.name = accountEmail;
|
||||||
|
|
||||||
|
insert into hs_accounts.account (
|
||||||
|
uuid, version, person_uuid,
|
||||||
|
global_uid, global_gid
|
||||||
|
) values (
|
||||||
|
subjectUuid, 0, personUuid,
|
||||||
|
uidOffset + t, uidOffset + t
|
||||||
|
);
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
|
||||||
|
create or replace procedure hs_office.partner_create_mass_bundle_test_data(
|
||||||
|
startPartnerNumber numeric(5),
|
||||||
|
endPartnerNumber numeric(5),
|
||||||
|
withMembershipPercentage integer default 80
|
||||||
|
)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
t integer;
|
||||||
|
idx varchar;
|
||||||
|
personUuid uuid;
|
||||||
|
accountEmail varchar;
|
||||||
|
subjectUuid uuid;
|
||||||
|
begin
|
||||||
|
call base.defineContext('creating mass partner bundle test-data', null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
set constraints all deferred;
|
||||||
|
|
||||||
|
call hs_office.contact_create_mass_test_data(startPartnerNumber::integer, endPartnerNumber::integer);
|
||||||
|
call hs_office.person_create_mass_test_data(startPartnerNumber::integer, endPartnerNumber::integer);
|
||||||
|
call hs_office.relation_create_mass_test_data(startPartnerNumber::integer, endPartnerNumber::integer);
|
||||||
|
call hs_office.partner_create_mass_test_data(startPartnerNumber, endPartnerNumber);
|
||||||
|
call hs_office.bankaccount_create_mass_test_data(startPartnerNumber, endPartnerNumber);
|
||||||
|
call hs_office.debitor_create_mass_test_data(startPartnerNumber, endPartnerNumber);
|
||||||
|
call hs_office.sepamandate_create_mass_test_data(startPartnerNumber, endPartnerNumber);
|
||||||
|
|
||||||
|
call hs_office.membership_create_mass_test_data(startPartnerNumber, endPartnerNumber, withMembershipPercentage);
|
||||||
|
call hs_office.coopsharetx_create_mass_test_data(startPartnerNumber, endPartnerNumber, withMembershipPercentage);
|
||||||
|
call hs_office.coopassettx_create_mass_test_data(startPartnerNumber, endPartnerNumber, withMembershipPercentage);
|
||||||
|
|
||||||
|
call hs_booking.project_create_mass_test_data(startPartnerNumber, endPartnerNumber);
|
||||||
|
call hs_booking.item_create_mass_test_data(startPartnerNumber, endPartnerNumber);
|
||||||
|
call hs_hosting.asset_create_mass_test_data(startPartnerNumber, endPartnerNumber);
|
||||||
|
|
||||||
|
for t in startPartnerNumber::integer..endPartnerNumber::integer
|
||||||
|
loop
|
||||||
|
call base.defineContext('mass partner bundle account test-data #' || t, null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||||
|
if t % 5 = 0 then
|
||||||
|
idx := base.intToVarChar(t, 4);
|
||||||
|
select p.uuid into personUuid
|
||||||
|
from hs_office.person p
|
||||||
|
where p.familyName = 'MassFamily' || idx and p.givenName = 'MassGiven' || idx;
|
||||||
|
|
||||||
|
if personUuid is not null and not exists (
|
||||||
|
select 1 from hs_accounts.account pr where pr.person_uuid = personUuid
|
||||||
|
) then
|
||||||
|
accountEmail := 'mass-person-' || idx || '@example.com';
|
||||||
|
perform rbac.create_subject(accountEmail);
|
||||||
|
select s.uuid into subjectUuid from rbac.subject s where s.name = accountEmail;
|
||||||
|
|
||||||
|
insert into hs_accounts.account (
|
||||||
|
uuid, version, person_uuid,
|
||||||
|
global_uid, global_gid
|
||||||
|
) values (
|
||||||
|
subjectUuid, 0, personUuid,
|
||||||
|
300000 + t, 300000 + t
|
||||||
|
);
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
idx := base.intToVarChar(t, 4);
|
||||||
|
select p.uuid into personUuid
|
||||||
|
from hs_office.person p
|
||||||
|
where p.familyName = 'MassRep' || idx and p.givenName = 'User' || idx;
|
||||||
|
|
||||||
|
if personUuid is not null and not exists (
|
||||||
|
select 1 from hs_accounts.account pr where pr.person_uuid = personUuid
|
||||||
|
) then
|
||||||
|
accountEmail := 'mass-rep-' || idx || '@example.com';
|
||||||
|
perform rbac.create_subject(accountEmail);
|
||||||
|
select s.uuid into subjectUuid from rbac.subject s where s.name = accountEmail;
|
||||||
|
|
||||||
|
insert into hs_accounts.account (
|
||||||
|
uuid, version, person_uuid,
|
||||||
|
global_uid, global_gid
|
||||||
|
) values (
|
||||||
|
subjectUuid, 0, personUuid,
|
||||||
|
400000 + t, 400000 + t
|
||||||
|
);
|
||||||
|
end if;
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
--liquibase formatted sql
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset michael.hoennig:hs-mass-test-data-PERFORMANCE context:!without-test-data endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
create or replace procedure hs_office.bench_debitor_sepamandates(iterations int default 10)
|
||||||
|
language plpgsql
|
||||||
|
as $$
|
||||||
|
declare
|
||||||
|
i int;
|
||||||
|
t0 timestamptz;
|
||||||
|
ms numeric;
|
||||||
|
total_limit numeric := 0;
|
||||||
|
total_count numeric := 0;
|
||||||
|
min_limit numeric := null;
|
||||||
|
max_limit numeric := null;
|
||||||
|
min_count numeric := null;
|
||||||
|
max_count numeric := null;
|
||||||
|
rows_read bigint;
|
||||||
|
begin
|
||||||
|
for i in 1..iterations loop
|
||||||
|
call base.defineContext(
|
||||||
|
'query debitor',
|
||||||
|
null,
|
||||||
|
'superuser-alex@hostsharing.net');
|
||||||
|
|
||||||
|
t0 := clock_timestamp();
|
||||||
|
select count(*) into rows_read
|
||||||
|
from (
|
||||||
|
select d.defaultprefix, b.iban, s.validity
|
||||||
|
from hs_office.debitor d
|
||||||
|
join hs_office.sepamandate_rv s on s.debitoruuid = d.uuid
|
||||||
|
join hs_office.bankaccount b on b.uuid = s.bankaccountuuid
|
||||||
|
where d.defaultprefix like 'dq%'
|
||||||
|
limit 10
|
||||||
|
) x;
|
||||||
|
|
||||||
|
ms := extract(epoch from (clock_timestamp() - t0)) * 1000;
|
||||||
|
total_limit := total_limit + ms;
|
||||||
|
if min_limit is null or ms < min_limit then min_limit := ms; end if;
|
||||||
|
if max_limit is null or ms > max_limit then max_limit := ms; end if;
|
||||||
|
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
|
||||||
|
for i in 1..iterations loop
|
||||||
|
call base.defineContext('query debitor',
|
||||||
|
null,
|
||||||
|
'superuser-alex@hostsharing.net');
|
||||||
|
|
||||||
|
t0 := clock_timestamp();
|
||||||
|
select count(*) into rows_read
|
||||||
|
from hs_office.debitor d
|
||||||
|
join hs_office.sepamandate_rv s on s.debitoruuid = d.uuid
|
||||||
|
join hs_office.bankaccount b on b.uuid = s.bankaccountuuid;
|
||||||
|
|
||||||
|
ms := extract(epoch from (clock_timestamp() - t0)) * 1000;
|
||||||
|
total_count := total_count + ms;
|
||||||
|
if min_count is null or ms < min_count then min_count := ms; end if;
|
||||||
|
if max_count is null or ms > max_count then max_count := ms; end if;
|
||||||
|
|
||||||
|
commit;
|
||||||
|
end loop;
|
||||||
|
|
||||||
|
raise notice 'limit10 min/avg/max: % ms / % ms / % ms',
|
||||||
|
round(min_limit, 3), round(total_limit / iterations, 3), round(max_limit, 3);
|
||||||
|
|
||||||
|
raise notice 'count all min/avg/max: % ms / % ms / % ms',
|
||||||
|
round(min_count, 3), round(total_count / iterations, 3), round(max_count, 3);
|
||||||
|
end $$;
|
||||||
|
--//
|
||||||
|
|
||||||
@@ -229,6 +229,12 @@ databaseChangeLog:
|
|||||||
- include:
|
- include:
|
||||||
file: db/changelog/9-hs-global/950-accounts/9519-hs-accounts-test-data.sql
|
file: db/changelog/9-hs-global/950-accounts/9519-hs-accounts-test-data.sql
|
||||||
context: "!only-prod-schema and !without-test-data"
|
context: "!only-prod-schema and !without-test-data"
|
||||||
|
- include:
|
||||||
|
file: db/changelog/9-hs-global/9520-hs-mass-test-data-generators.sql
|
||||||
|
context: "!only-prod-schema and !without-test-data"
|
||||||
|
- include:
|
||||||
|
file: db/changelog/9-hs-global/9521-has-mass-test-data-performance.sql
|
||||||
|
context: "!only-prod-schema and !without-test-data"
|
||||||
|
|
||||||
- include:
|
- include:
|
||||||
file: db/changelog/9-hs-global/960-integrations/9600-hs-integration-schema.sql
|
file: db/changelog/9-hs-global/960-integrations/9600-hs-integration-schema.sql
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ class ContextIntegrationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hasGlobalAdminRoleIsTrueForGlobalAdminWithAssumedRole() {
|
public void hasGlobalAdminRoleIsTrueForGlobalAdminWithAssumedRole() {
|
||||||
final var hsGlobalAdminRole = jpaAttempt.transacted(() -> {
|
final var hasGlobalAdminRole = jpaAttempt.transacted(() -> {
|
||||||
// given
|
// given
|
||||||
context.define("superuser-alex@hostsharing.net", "rbactest.package#yyy00:ADMIN");
|
context.define("superuser-alex@hostsharing.net", "rbactest.package#yyy00:ADMIN");
|
||||||
|
|
||||||
@@ -199,6 +199,20 @@ class ContextIntegrationTests {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertThat(hasGlobalAdminRole.returnedValue()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasGlobalAdminRoleIsFalseForGlobalAdminWithAssumedGlobalAdminRole() {
|
||||||
|
final var hsGlobalAdminRole = jpaAttempt.transacted(() -> {
|
||||||
|
// given
|
||||||
|
context.define("superuser-alex@hostsharing.net", "rbac.global#global:ADMIN");
|
||||||
|
|
||||||
|
// when
|
||||||
|
return (boolean) em.createNativeQuery("select rbac.hasGlobalAdminRole()").getSingleResult();
|
||||||
|
});
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(hsGlobalAdminRole.returnedValue()).isFalse();
|
assertThat(hsGlobalAdminRole.returnedValue()).isFalse();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user