diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java index 1dc8e39f..f8409ffd 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountEntity.java @@ -59,7 +59,7 @@ public class HsOfficeBankAccountEntity implements HasUuid, Stringifyable { public static RbacView rbac() { return rbacViewFor("bankAccount", HsOfficeBankAccountEntity.class) - .withIdentityView(SQL.projection("concat(iban, ':', holder)")) + .withIdentityView(SQL.projection("iban")) .withUpdatableColumns("holder", "iban", "bic") .toRole("global", GUEST).grantPermission("bankAccount", INSERT) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java index 406b232c..e2ff6b3d 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactEntity.java @@ -75,10 +75,11 @@ public class HsOfficeContactEntity implements Stringifyable, HasUuid { }) .createSubRole(REFERRER, (with) -> { with.permission(SELECT); - }); + }) + .toRole(GLOBAL, GUEST).grantPermission("contact", INSERT); } public static void main(String[] args) throws IOException { - rbac().generateWithBaseFileName("203-hs-office-contact-rbac-generated"); + rbac().generateWithBaseFileName("203-hs-office-contact-rbac"); } } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java index 6a7b6fd2..6376e7db 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/office/partner/HsOfficePartnerDetailsEntity.java @@ -2,7 +2,6 @@ package net.hostsharing.hsadminng.hs.office.partner; import lombok.*; import net.hostsharing.hsadminng.errors.DisplayName; -import net.hostsharing.hsadminng.hs.office.relationship.HsOfficeRelationshipEntity; import net.hostsharing.hsadminng.persistence.HasUuid; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView; import net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL; @@ -14,10 +13,8 @@ import java.io.IOException; import java.time.LocalDate; import java.util.UUID; -import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Column.dependsOnColumn; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.*; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.*; -import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.fetchedBySql; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.rbacViewFor; import static net.hostsharing.hsadminng.stringify.Stringify.stringify; diff --git a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java index d6845901..6f68ba88 100644 --- a/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java +++ b/src/main/java/net/hostsharing/hsadminng/rbac/rbacdef/RolesGrantsAndPermissionsGenerator.java @@ -318,12 +318,12 @@ class RolesGrantsAndPermissionsGenerator { generatePermissionsForRole(plPgSql, role); - generateUserGrantsForRole(plPgSql, role); - generateIncomingSuperRolesForRole(plPgSql, role); generateOutgoingSubRolesForRole(plPgSql, role); + generateUserGrantsForRole(plPgSql, role); + plPgSql.chopTail(",\n"); plPgSql.writeLn(); }); diff --git a/src/main/resources/db/changelog/057-rbac-role-builder.sql b/src/main/resources/db/changelog/057-rbac-role-builder.sql index 1a7da953..a428e67f 100644 --- a/src/main/resources/db/changelog/057-rbac-role-builder.sql +++ b/src/main/resources/db/changelog/057-rbac-role-builder.sql @@ -37,7 +37,7 @@ declare subRoleUuid uuid; superRoleUuid uuid; userUuid uuid; - grantedByRoleUuid uuid; + grantedByRoleUuid uuid; -- FIXME: rename to userGrantsByRoleUuid begin roleUuid := createRole(roleDescriptor); @@ -58,8 +58,9 @@ begin end loop; if cardinality(userUuids) > 0 then + -- direct grants to users need a grantedByRole which can revoke the grant if grantedByRole is null then - grantedByRoleUuid := roleUuid; + grantedByRoleUuid := roleUuid; -- FIXME: or do we want to require an explicit grantedByRoleUuid? else grantedByRoleUuid := getRoleId(grantedByRole); end if; diff --git a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql index d225cdd8..66994dd8 100644 --- a/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql +++ b/src/main/resources/db/changelog/203-hs-office-contact-rbac.sql @@ -1,4 +1,6 @@ --liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator at 2024-03-14T09:00:15.769718298. + -- ============================================================================ --changeset hs-office-contact-rbac-OBJECT:1 endDelimiter:--// @@ -15,122 +17,141 @@ call generateRbacRoleDescriptors('hsOfficeContact', 'hs_office_contact'); -- ============================================================================ ---changeset hs-office-contact-rbac-ROLES-CREATION:1 endDelimiter:--// +--changeset hs-office-contact-rbac-insert-trigger:1 endDelimiter:--// -- ---------------------------------------------------------------------------- /* - Creates the roles and their assignments for a new contact for the AFTER INSERT TRIGGER. + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. */ -create or replace function createRbacRolesForHsOfficeContact() +create or replace procedure buildRbacSystemForHsOfficeContact( + NEW hs_office_contact +) + language plpgsql as $$ + +declare + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + perform createRoleWithGrants( + hsOfficeContactOwner(NEW), + permissions => array['DELETE'], + incomingSuperRoles => array[globalAdmin()], + userUuids => array[currentUserUuid()] + ); + + perform createRoleWithGrants( + hsOfficeContactAdmin(NEW), + permissions => array['UPDATE'], + incomingSuperRoles => array[hsOfficeContactOwner(NEW)] + ); + + perform createRoleWithGrants( + hsOfficeContactReferrer(NEW), + permissions => array['SELECT'], + incomingSuperRoles => array[hsOfficeContactAdmin(NEW)] + ); + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_office_contact row. + */ + +create or replace function insertTriggerForHsOfficeContact_tf() returns trigger language plpgsql strict as $$ begin - if TG_OP <> 'INSERT' then - raise exception 'invalid usage of TRIGGER AFTER INSERT'; - end if; - - perform createRoleWithGrants( - hsOfficeContactOwner(NEW), - permissions => array['DELETE'], - incomingSuperRoles => array[globalAdmin()], - userUuids => array[currentUserUuid()], - grantedByRole => globalAdmin() - ); - - perform createRoleWithGrants( - hsOfficeContactAdmin(NEW), - permissions => array['UPDATE'], - incomingSuperRoles => array[hsOfficeContactOwner(NEW)] - ); - - perform createRoleWithGrants( - hsOfficeContactReferrer(NEW), - permissions => array['SELECT'], - incomingSuperRoles => array[hsOfficeContactAdmin(NEW)] - ); - + call buildRbacSystemForHsOfficeContact(NEW); return NEW; end; $$; -/* - An AFTER INSERT TRIGGER which creates the role structure for a new customer. - */ - -create trigger createRbacRolesForHsOfficeContact_Trigger - after insert - on hs_office_contact +create trigger insertTriggerForHsOfficeContact_tg + after insert on hs_office_contact for each row -execute procedure createRbacRolesForHsOfficeContact(); +execute procedure insertTriggerForHsOfficeContact_tf(); --// +-- ============================================================================ +--changeset hs-office-contact-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates INSERT INTO hs_office_contact permissions for the related global rows. + */ +do language plpgsql $$ + declare + row global; + permissionUuid uuid; + roleUuid uuid; + begin + call defineContext('create INSERT INTO hs_office_contact permissions for the related global rows'); + + FOR row IN SELECT * FROM global + LOOP + roleUuid := findRoleId(globalGuest()); + permissionUuid := createPermission(row.uuid, 'INSERT', 'hs_office_contact'); + call grantPermissionToRole(permissionUuid, roleUuid); + END LOOP; + END; +$$; + +/** + Adds hs_office_contact INSERT permission to specified role of new global rows. +*/ +create or replace function hs_office_contact_global_insert_tf() + returns trigger + language plpgsql + strict as $$ +begin + call grantPermissionToRole( + globalGuest(), + createPermission(NEW.uuid, 'INSERT', 'hs_office_contact')); + return NEW; +end; $$; + +create trigger hs_office_contact_global_insert_tg + after insert on global + for each row +execute procedure hs_office_contact_global_insert_tf(); + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_office_contact. +*/ +create or replace function hs_office_contact_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_office_contact not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; +--// + -- ============================================================================ --changeset hs-office-contact-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityViewFromProjection('hs_office_contact', $idName$ - target.label + label $idName$); --// - -- ============================================================================ --changeset hs-office-contact-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- -call generateRbacRestrictedView('hs_office_contact', 'target.label', +call generateRbacRestrictedView('hs_office_contact', + $orderBy$ + label + $orderBy$, $updates$ - label = new.label, + label = new.label, postalAddress = new.postalAddress, emailAddresses = new.emailAddresses, phoneNumbers = new.phoneNumbers $updates$); ---/ - - --- ============================================================================ ---changeset hs-office-contact-rbac-NEW-CONTACT:1 endDelimiter:--// --- ---------------------------------------------------------------------------- -/* - Creates a global permission for new-contact and assigns it to the hostsharing admins role. - */ -do language plpgsql $$ - declare - addCustomerPermissions uuid[]; - globalObjectUuid uuid; - globalAdminRoleUuid uuid; - begin - call defineContext('granting global new-contact permission to global admin role', null, null, null); - - globalAdminRoleUuid := findRoleId(globalAdmin()); - globalObjectUuid := (select uuid from global); - addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-contact']); - call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions); - end; -$$; - -/** - Used by the trigger to prevent the add-customer to current user respectively assumed roles. - */ -create or replace function addHsOfficeContactNotAllowedForCurrentSubjects() - returns trigger - language PLPGSQL -as $$ -begin - raise exception '[403] new-contact not permitted for %', - array_to_string(currentSubjects(), ';', 'null'); -end; $$; - -/** - Checks if the user or assumed roles are allowed to create a new customer. - */ -create trigger hs_office_contact_insert_trigger - before insert - on hs_office_contact - for each row - -- TODO.spec: who is allowed to create new contacts - when ( not hasAssumedRole() ) -execute procedure addHsOfficeContactNotAllowedForCurrentSubjects(); --// diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md index e4c25d7c..c41ea375 100644 --- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md +++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.md @@ -1,6 +1,6 @@ ### rbac bankAccount -This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-11T19:09:38.350576842. +This code generated was by RbacViewMermaidFlowchartGenerator at 2024-03-14T08:55:11.118624882. ```mermaid %%{init:{'flowchart':{'htmlLabels':false}}}%% diff --git a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql index 19cf5a75..a13c131d 100644 --- a/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql +++ b/src/main/resources/db/changelog/243-hs-office-bankaccount-rbac.sql @@ -1,5 +1,6 @@ --liquibase formatted sql --- This code generated was by RbacViewPostgresGenerator at 2024-03-11T19:09:38.359318650. +-- This code generated was by RbacViewPostgresGenerator at 2024-03-14T08:55:11.127959896. + -- ============================================================================ --changeset hs-office-bankaccount-rbac-OBJECT:1 endDelimiter:--// @@ -36,8 +37,8 @@ begin perform createRoleWithGrants( hsOfficeBankAccountOwner(NEW), permissions => array['DELETE'], - userUuids => array[currentUserUuid()], - incomingSuperRoles => array[globalAdmin()] + incomingSuperRoles => array[globalAdmin()], + userUuids => array[currentUserUuid()] ); perform createRoleWithGrants( @@ -72,9 +73,9 @@ create trigger insertTriggerForHsOfficeBankAccount_tg after insert on hs_office_bankaccount for each row execute procedure insertTriggerForHsOfficeBankAccount_tf(); - --// + -- ============================================================================ --changeset hs-office-bankaccount-rbac-INSERT:1 endDelimiter:--// -- ---------------------------------------------------------------------------- @@ -108,8 +109,8 @@ create or replace function hs_office_bankaccount_global_insert_tf() strict as $$ begin call grantPermissionToRole( - createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount'), - globalGuest()); + globalGuest(), + createPermission(NEW.uuid, 'INSERT', 'hs_office_bankaccount')); return NEW; end; $$; @@ -128,23 +129,23 @@ begin raise exception '[403] insert into hs_office_bankaccount not allowed for current subjects % (%)', currentSubjects(), currentSubjectsUuids(); end; $$; - --// + -- ============================================================================ --changeset hs-office-bankaccount-rbac-IDENTITY-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacIdentityViewFromProjection('hs_office_bankaccount', $idName$ - concat(iban, ':', holder) + iban $idName$); - --// + -- ============================================================================ --changeset hs-office-bankaccount-rbac-RESTRICTED-VIEW:1 endDelimiter:--// -- ---------------------------------------------------------------------------- call generateRbacRestrictedView('hs_office_bankaccount', $orderBy$ - concat(iban, ':', holder) + iban $orderBy$, $updates$ holder = new.holder, @@ -153,4 +154,3 @@ call generateRbacRestrictedView('hs_office_bankaccount', $updates$); --// - diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java index eb14e634..fd484c4c 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountRepositoryIntegrationTest.java @@ -102,23 +102,21 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC final var roles = rawRoleRepo.findAll(); assertThat(distinctRoleNamesOf(roles)).containsExactlyInAnyOrder(Array.from( initialRoleNames, - "hs_office_bankaccount#sometempaccC.owner", - "hs_office_bankaccount#sometempaccC.admin", - "hs_office_bankaccount#sometempaccC.tenant", - "hs_office_bankaccount#sometempaccC.guest" + "hs_office_bankaccount#DE25500105176934832579.owner", + "hs_office_bankaccount#DE25500105176934832579.admin", + "hs_office_bankaccount#DE25500105176934832579.referrer" )); assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted( initialGrantNames, - "{ grant perm DELETE on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.owner by system and assume }", - "{ grant role hs_office_bankaccount#sometempaccC.owner to role global#global.admin by system and assume }", - "{ grant role hs_office_bankaccount#sometempaccC.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }", + "{ grant perm DELETE on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.owner by system and assume }", + "{ grant role hs_office_bankaccount#DE25500105176934832579.owner to role global#global.admin by system and assume }", + "{ grant role hs_office_bankaccount#DE25500105176934832579.owner to user selfregistered-user-drew@hostsharing.org by hs_office_bankaccount#DE25500105176934832579.owner and assume }", - "{ grant role hs_office_bankaccount#sometempaccC.admin to role hs_office_bankaccount#sometempaccC.owner by system and assume }", + "{ grant role hs_office_bankaccount#DE25500105176934832579.admin to role hs_office_bankaccount#DE25500105176934832579.owner by system and assume }", + "{ grant perm UPDATE on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.admin by system and assume }", - "{ grant role hs_office_bankaccount#sometempaccC.tenant to role hs_office_bankaccount#sometempaccC.admin by system and assume }", - - "{ grant perm SELECT on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.guest by system and assume }", - "{ grant role hs_office_bankaccount#sometempaccC.guest to role hs_office_bankaccount#sometempaccC.tenant by system and assume }", + "{ grant perm SELECT on hs_office_bankaccount#DE25500105176934832579 to role hs_office_bankaccount#DE25500105176934832579.referrer by system and assume }", + "{ grant role hs_office_bankaccount#DE25500105176934832579.referrer to role hs_office_bankaccount#DE25500105176934832579.admin by system and assume }", null )); } @@ -241,10 +239,6 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTestWithC final var initialRoleNames = distinctRoleNamesOf(rawRoleRepo.findAll()); final var initialGrantNames = distinctGrantDisplaysOf(rawGrantRepo.findAll()); final var givenBankAccount = givenSomeTemporaryBankAccount("selfregistered-user-drew@hostsharing.org"); - assertThat(distinctRoleNamesOf(rawRoleRepo.findAll()).size()).as("unexpected number of roles created") - .isEqualTo(initialRoleNames.size() + 4); - assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll()).size()).as("unexpected number of grants created") - .isEqualTo(initialGrantNames.size() + 7); // when final var result = jpaAttempt.transacted(() -> { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java index 7b520d93..6acf12f6 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/contact/HsOfficeContactRepositoryIntegrationTest.java @@ -107,17 +107,16 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTestWithClean "hs_office_contact#anothernewcontact.admin", "hs_office_contact#anothernewcontact.referrer" )); - assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.from( + assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted( initialGrantNames, - "{ grant perm * on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }", - "{ grant role hs_office_contact#anothernewcontact.owner to role global#global.admin by system and assume }", - "{ grant perm UPDATE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin by system and assume }", - "{ grant role hs_office_contact#anothernewcontact.tenant to role hs_office_contact#anothernewcontact.admin by system and assume }", - "{ grant perm DELETE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }", - "{ grant role hs_office_contact#anothernewcontact.admin to role hs_office_contact#anothernewcontact.owner by system and assume }", + "{ grant role hs_office_contact#anothernewcontact.owner to role global#global.admin by system and assume }", + "{ grant perm UPDATE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.admin by system and assume }", + "{ grant role hs_office_contact#anothernewcontact.owner to user selfregistered-user-drew@hostsharing.org by system and assume }", + "{ grant perm DELETE on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }", + "{ grant role hs_office_contact#anothernewcontact.admin to role hs_office_contact#anothernewcontact.owner by system and assume }", "{ grant perm SELECT on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.referrer by system and assume }", - "{ grant role hs_office_contact#anothernewcontact.referrer to role hs_office_contact#anothernewcontact.admin by system and assume }" + "{ grant role hs_office_contact#anothernewcontact.referrer to role hs_office_contact#anothernewcontact.admin by system and assume }" )); }