preparation for changing updatable columns #161

Merged
hsh-michaelhoennig merged 7 commits from feature/updatable-relation-holder into master 2025-03-03 12:01:45 +01:00
5 changed files with 76 additions and 16 deletions
Showing only changes of commit 512035b5b4 - Show all commits

View File

@ -51,7 +51,7 @@ public class HsOfficeRelationRbacEntity extends HsOfficeRelation {
""")) """))
.withRestrictedViewOrderBy(SQL.expression( .withRestrictedViewOrderBy(SQL.expression(
"(select idName from hs_office.person_iv p where p.uuid = target.holderUuid)")) "(select idName from hs_office.person_iv p where p.uuid = target.holderUuid)"))
.withUpdatableColumns("contactUuid") .withUpdatableColumns("anchorUuid", "holderUuid", "contactUuid")
.importEntityAlias("anchorPerson", HsOfficePersonRbacEntity.class, usingDefaultCase(), .importEntityAlias("anchorPerson", HsOfficePersonRbacEntity.class, usingDefaultCase(),
dependsOnColumn("anchorUuid"), dependsOnColumn("anchorUuid"),
directlyFetchedByDependsOnColumn(), directlyFetchedByDependsOnColumn(),

View File

@ -235,7 +235,7 @@ begin
*/ */
newColumns := 'new.' || replace(columnNames, ', ', ', new.'); newColumns := 'new.' || replace(columnNames, ', ', ', new.');
sql := format($sql$ sql := format($sql$
create function %1$s_instead_of_insert_tf() create or replace function %1$s_instead_of_insert_tf()
returns trigger returns trigger
language plpgsql as $f$ language plpgsql as $f$
declare declare
@ -254,7 +254,7 @@ begin
Creates an instead of insert trigger for the restricted view. Creates an instead of insert trigger for the restricted view.
*/ */
sql := format($sql$ sql := format($sql$
create trigger instead_of_insert_tg create or replace trigger instead_of_insert_tg
instead of insert instead of insert
on %1$s_rv on %1$s_rv
for each row for each row
@ -266,7 +266,7 @@ begin
Instead of delete trigger function for the restricted view. Instead of delete trigger function for the restricted view.
*/ */
sql := format($sql$ sql := format($sql$
create function %1$s_instead_of_delete_tf() create or replace function %1$s_instead_of_delete_tf()
returns trigger returns trigger
language plpgsql as $f$ language plpgsql as $f$
begin begin
@ -283,7 +283,7 @@ begin
Creates an instead of delete trigger for the restricted view. Creates an instead of delete trigger for the restricted view.
*/ */
sql := format($sql$ sql := format($sql$
create trigger instead_of_delete_tg create or replace trigger instead_of_delete_tg
instead of delete instead of delete
on %1$s_rv on %1$s_rv
for each row for each row
@ -297,7 +297,7 @@ begin
*/ */
if columnUpdates is not null then if columnUpdates is not null then
sql := format($sql$ sql := format($sql$
create function %1$s_instead_of_update_tf() create or replace function %1$s_instead_of_update_tf()
returns trigger returns trigger
language plpgsql as $f$ language plpgsql as $f$
begin begin
@ -316,7 +316,7 @@ begin
Creates an instead of delete trigger for the restricted view. Creates an instead of delete trigger for the restricted view.
*/ */
sql = format($sql$ sql = format($sql$
create trigger instead_of_update_tg create or replace trigger instead_of_update_tg
instead of update instead of update
on %1$s_rv on %1$s_rv
for each row for each row

View File

@ -110,7 +110,7 @@ execute procedure hs_office.relation_build_rbac_system_after_insert_tf();
-- ============================================================================ -- ============================================================================
--changeset RolesGrantsAndPermissionsGenerator:hs-office-relation-rbac-update-trigger endDelimiter:--// --changeset RolesGrantsAndPermissionsGenerator:hs-office-relation-rbac-update-trigger runOnChange:true validCheckSum:ANY endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/* /*
@ -124,7 +124,9 @@ create or replace procedure hs_office.relation_update_rbac_system(
language plpgsql as $$ language plpgsql as $$
begin begin
if NEW.contactUuid is distinct from OLD.contactUuid then if NEW.holderUuid is distinct from OLD.holderUuid
or NEW.anchorUuid is distinct from OLD.anchorUuid
or NEW.contactUuid is distinct from OLD.contactUuid then
delete from rbac.grant g where g.grantedbytriggerof = OLD.uuid; delete from rbac.grant g where g.grantedbytriggerof = OLD.uuid;
call hs_office.relation_build_rbac_system(NEW); call hs_office.relation_build_rbac_system(NEW);
end if; end if;
@ -143,7 +145,7 @@ begin
return NEW; return NEW;
end; $$; end; $$;
create trigger update_rbac_system_after_update_tg create or replace trigger update_rbac_system_after_update_tg
after update on hs_office.relation after update on hs_office.relation
for each row for each row
execute procedure hs_office.relation_update_rbac_system_after_update_tf(); execute procedure hs_office.relation_update_rbac_system_after_update_tf();
@ -241,20 +243,22 @@ call rbac.generateRbacIdentityViewFromProjection('hs_office.relation',
-- ============================================================================ -- ============================================================================
--changeset RbacRestrictedViewGenerator:hs-office-relation-rbac-RESTRICTED-VIEW endDelimiter:--// --changeset RbacRestrictedViewGenerator:hs-office-relation-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
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)
$orderBy$, $orderBy$,
$updates$ $updates$
anchorUuid = new.anchorUuid,
holderUuid = new.holderUuid,
contactUuid = new.contactUuid contactUuid = new.contactUuid
$updates$); $updates$);
--// --//
-- ============================================================================ -- ============================================================================
--changeset RbacRbacSystemRebuildGenerator:hs-office-relation-rbac-rebuild endDelimiter:--// --changeset RbacRbacSystemRebuildGenerator:hs-office-relation-rbac-rebuild runOnChange:true validCheckSum:ANY endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
-- HOWTO: Rebuild RBAC-system for table hs_office.relation after changing its RBAC specification. -- HOWTO: Rebuild RBAC-system for table hs_office.relation after changing its RBAC specification.
@ -305,3 +309,17 @@ END;
$$; $$;
--// --//
-- ============================================================================
--changeset RbacRbacSystemRebuildGenerator:hs-office-relation-rbac-actually-rebuild runOnChange:true validCheckSum:ANY endDelimiter:--//
-- ----------------------------------------------------------------------------
begin transaction;
call base.defineContext(
're-creating RBAC for table hs_office.relation',
null,
'superuser-alex@hostsharing.net' -- FIXME: use env-var
);
call hs_office.relation_rebuild_rbac_system();
commit;
--//

View File

@ -28,6 +28,7 @@ import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.NATU
import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.UNINCORPORATED_FIRM; import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.UNINCORPORATED_FIRM;
import static net.hostsharing.hsadminng.rbac.grant.RawRbacGrantEntity.distinctGrantDisplaysOf; import static net.hostsharing.hsadminng.rbac.grant.RawRbacGrantEntity.distinctGrantDisplaysOf;
import static net.hostsharing.hsadminng.rbac.role.RawRbacRoleEntity.distinctRoleNamesOf; import static net.hostsharing.hsadminng.rbac.role.RawRbacRoleEntity.distinctRoleNamesOf;
import static net.hostsharing.hsadminng.rbac.role.RbacRoleType.ADMIN;
import static net.hostsharing.hsadminng.rbac.test.JpaAttempt.attempt; import static net.hostsharing.hsadminng.rbac.test.JpaAttempt.attempt;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -283,7 +284,44 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
result.returnedValue(), result.returnedValue(),
"hs_office.contact#fifthcontact:ADMIN"); "hs_office.contact#fifthcontact:ADMIN");
relationRbacRepo.deleteByUuid(givenRelation.getUuid()); // FIXME relationRbacRepo.deleteByUuid(givenRelation.getUuid());
}
@Test
public void hostsharingAdmin_withoutAssumedRole_canUpdateHolderOfArbitraryRelation() {
// given
context("superuser-alex@hostsharing.net");
final var givenRelation = givenSomeTemporaryRelationBessler(
"Bert", "fifth contact");
final var oldHolderPerson = givenRelation.getHolder();
final var newHolderPerson = personRepo.findPersonByOptionalNameLike("Paul").getFirst();
assertThatRelationActuallyInDatabase(givenRelation);
assertThatRelationIsVisibleForUserWithRole(
givenRelation,
givenRelation.getHolder().roleId(ADMIN));
// when
final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net");
givenRelation.setHolder(newHolderPerson);
return toCleanup(relationRbacRepo.save(givenRelation).load());
});
// then
result.assertSuccessful();
assertThat(result.returnedValue().getHolder().getGivenName()).isEqualTo("Paul");
assertThatRelationIsVisibleForUserWithRole(
result.returnedValue(),
"rbac.global#global:ADMIN");
assertThatRelationIsVisibleForUserWithRole(
result.returnedValue(),
newHolderPerson.roleId(ADMIN));
assertThatRelationIsNotVisibleForUserWithRole(
result.returnedValue(),
oldHolderPerson.roleId(ADMIN));
// FIXME: relationRbacRepo.deleteByUuid(givenRelation.getUuid());
} }
@Test @Test
@ -296,13 +334,17 @@ class HsOfficeRelationRepositoryIntegrationTest extends ContextBasedTestWithClea
givenRelation, givenRelation,
"hs_office.relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerAnita:AGENT"); "hs_office.relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerAnita:AGENT");
assertThatRelationActuallyInDatabase(givenRelation); assertThatRelationActuallyInDatabase(givenRelation);
final var givenContact = contactRealRepo.findContactByOptionalCaptionLike("sixth contact")
.stream()
.findFirst()
.orElseThrow();
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context( context(
"superuser-alex@hostsharing.net", "superuser-alex@hostsharing.net",
"hs_office.relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerAnita:AGENT"); "hs_office.relation#ErbenBesslerMelBessler-with-REPRESENTATIVE-BesslerAnita:AGENT");
givenRelation.setContact(null); givenRelation.setContact(givenContact);
return relationRbacRepo.save(givenRelation); return relationRbacRepo.save(givenRelation);
}); });

View File

@ -12129,8 +12129,8 @@ INSERT INTO hs_office.debitor (uuid, version, debitornumbersuffix, debitorreluui
-- Data for Name: membership; Type: TABLE DATA; Schema: hs_office; Owner: postgres -- Data for Name: membership; Type: TABLE DATA; Schema: hs_office; Owner: postgres
-- --
INSERT INTO hs_office.membership (uuid, version, partneruuid, membernumbersuffix, validity, status, membershipfeebillable) VALUES ('4330e211-e36c-45ec-9332-f7593ff42811', 0, 'c27d1b0c-7e43-4b64-ae69-4317f51023ba', '01', '[2022-10-01,)', 'ACTIVE', true); INSERT INTO hs_office.membership (uuid, version, partneruuid, membernumbersuffix, validity, status, membershipfeebillable) VALUES ('4330e211-e36c-45ec-9332-f7593ff42811', 0, 'c27d1b0c-7e43-4b64-ae69-4317f51023ba', '01', '[2022-10-01,2025-01-01)', 'ACTIVE', true);
INSERT INTO hs_office.membership (uuid, version, partneruuid, membernumbersuffix, validity, status, membershipfeebillable) VALUES ('bed3c145-aa55-425f-9211-be9f5e9f4ebe', 0, '11583dae-da71-4786-a61d-d70f51ce988e', '02', '[2022-10-01,)', 'ACTIVE', true); INSERT INTO hs_office.membership (uuid, version, partneruuid, membernumbersuffix, validity, status, membershipfeebillable) VALUES ('bed3c145-aa55-425f-9211-be9f5e9f4ebe', 0, '11583dae-da71-4786-a61d-d70f51ce988e', '02', '[2022-10-01,2026-01-01)', 'ACTIVE', true);
INSERT INTO hs_office.membership (uuid, version, partneruuid, membernumbersuffix, validity, status, membershipfeebillable) VALUES ('a42d61c5-7dad-4379-9dd9-39a8d21ddc32', 0, '7fe704c0-2e54-463e-891e-533f0274da76', '03', '[2022-10-01,)', 'ACTIVE', true); INSERT INTO hs_office.membership (uuid, version, partneruuid, membernumbersuffix, validity, status, membershipfeebillable) VALUES ('a42d61c5-7dad-4379-9dd9-39a8d21ddc32', 0, '7fe704c0-2e54-463e-891e-533f0274da76', '03', '[2022-10-01,)', 'ACTIVE', true);