implements table hs_office_relationship to HsOfficeRelationshipController
This commit is contained in:
@ -20,6 +20,10 @@ create or replace function assertReferenceType(argument varchar, referenceId uui
|
||||
declare
|
||||
actualType ReferenceType;
|
||||
begin
|
||||
if referenceId is null then
|
||||
raise exception '% must be a % and not null', argument, expectedType;
|
||||
end if;
|
||||
|
||||
actualType = (select type from RbacReference where uuid = referenceId);
|
||||
if (actualType <> expectedType) then
|
||||
raise exception '% must reference a %, but got a %', argument, expectedType, actualType;
|
||||
@ -608,21 +612,29 @@ begin
|
||||
into RbacGrants (ascendantuuid, descendantUuid, assumed)
|
||||
values (superRoleId, subRoleId, doAssume)
|
||||
on conflict do nothing; -- allow granting multiple times
|
||||
delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId;
|
||||
insert
|
||||
into RbacGrants (ascendantuuid, descendantUuid, assumed)
|
||||
values (superRoleId, subRoleId, doAssume); -- allow granting multiple times
|
||||
end; $$;
|
||||
|
||||
create or replace procedure revokeRoleFromRole(subRoleId uuid, superRoleId uuid)
|
||||
create or replace procedure grantRoleToRoleIfNotNull(subRole RbacRoleDescriptor, superRole RbacRoleDescriptor, doAssume bool = true)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
superRoleId uuid;
|
||||
subRoleId uuid;
|
||||
begin
|
||||
superRoleId := findRoleId(superRole);
|
||||
if ( subRoleId is null ) then return; end if;
|
||||
subRoleId := findRoleId(subRole);
|
||||
|
||||
perform assertReferenceType('superRoleId (ascendant)', superRoleId, 'RbacRole');
|
||||
perform assertReferenceType('subRoleId (descendant)', subRoleId, 'RbacRole');
|
||||
|
||||
if (isGranted(superRoleId, subRoleId)) then
|
||||
delete from RbacGrants where ascendantUuid = superRoleId and descendantUuid = subRoleId;
|
||||
if isGranted(subRoleId, superRoleId) then
|
||||
raise exception '[400] Cyclic role grant detected between % and %', subRoleId, superRoleId;
|
||||
end if;
|
||||
|
||||
insert
|
||||
into RbacGrants (ascendantuuid, descendantUuid, assumed)
|
||||
values (superRoleId, subRoleId, doAssume)
|
||||
on conflict do nothing; -- allow granting multiple times
|
||||
end; $$;
|
||||
|
||||
create or replace procedure revokeRoleFromRole(subRole RbacRoleDescriptor, superRole RbacRoleDescriptor)
|
||||
|
@ -61,7 +61,7 @@ create or replace view rbacgrants_ev as
|
||||
x.descendingIdName as descendantIdName,
|
||||
x.grantedByRoleUuid,
|
||||
x.ascendantUuid as ascendantUuid,
|
||||
x.descendantUuid as descenantUuid,
|
||||
x.descendantUuid as descendantUuid,
|
||||
x.assumed
|
||||
from (
|
||||
select g.uuid as grantUuid,
|
||||
|
@ -51,7 +51,9 @@ declare
|
||||
begin
|
||||
foreach superRoleDescriptor in array roleDescriptors
|
||||
loop
|
||||
superRoleUuids := superRoleUuids || getRoleId(superRoleDescriptor, 'fail');
|
||||
if superRoleDescriptor is not null then
|
||||
superRoleUuids := superRoleUuids || getRoleId(superRoleDescriptor, 'fail');
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
return row (superRoleUuids)::RbacSuperRoles;
|
||||
@ -96,7 +98,6 @@ create type RbacSubRoles as
|
||||
roleUuids uuid[]
|
||||
);
|
||||
|
||||
-- drop FUNCTION beingItselfA(roleUuid uuid)
|
||||
create or replace function beingItselfA(roleUuid uuid)
|
||||
returns RbacSubRoles
|
||||
language plpgsql
|
||||
@ -105,7 +106,6 @@ begin
|
||||
return row (array [roleUuid]::uuid[])::RbacSubRoles;
|
||||
end; $$;
|
||||
|
||||
-- drop FUNCTION beingItselfA(roleDescriptor RbacRoleDescriptor)
|
||||
create or replace function beingItselfA(roleDescriptor RbacRoleDescriptor)
|
||||
returns RbacSubRoles
|
||||
language plpgsql
|
||||
@ -124,7 +124,9 @@ declare
|
||||
begin
|
||||
foreach subRoleDescriptor in array roleDescriptors
|
||||
loop
|
||||
subRoleUuids := subRoleUuids || getRoleId(subRoleDescriptor, 'fail');
|
||||
if subRoleDescriptor is not null then
|
||||
subRoleUuids := subRoleUuids || getRoleId(subRoleDescriptor, 'fail');
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
return row (subRoleUuids)::RbacSubRoles;
|
||||
|
@ -127,6 +127,7 @@ begin
|
||||
/*
|
||||
Creates a restricted view based on the 'view' permission of the current subject.
|
||||
*/
|
||||
-- TODO.refa: hoist `select queryAccessibleObjectUuidsOfSubjectIds(...)` into WITH CTE for performance
|
||||
sql := format($sql$
|
||||
set session session authorization default;
|
||||
create view %1$s_rv as
|
||||
|
@ -17,7 +17,7 @@ begin
|
||||
currentTask = 'creating RBAC test contact ' || contLabel;
|
||||
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||
|
||||
emailAddr = 'customer-admin@' || cleanIdentifier(contLabel) || '.example.com';
|
||||
emailAddr = 'contact-admin@' || cleanIdentifier(contLabel) || '.example.com';
|
||||
call defineContext(currentTask);
|
||||
perform createRbacUser(emailAddr);
|
||||
call defineContext(currentTask, null, emailAddr);
|
||||
@ -64,6 +64,7 @@ do language plpgsql $$
|
||||
call createHsOfficeContactTestData('forth contact');
|
||||
call createHsOfficeContactTestData('fifth contact');
|
||||
call createHsOfficeContactTestData('sixth contact');
|
||||
call createHsOfficeContactTestData('seventh contact');
|
||||
call createHsOfficeContactTestData('eighth contact');
|
||||
call createHsOfficeContactTestData('ninth contact');
|
||||
call createHsOfficeContactTestData('tenth contact');
|
||||
|
@ -60,10 +60,12 @@ end; $$;
|
||||
do language plpgsql $$
|
||||
begin
|
||||
call createHsOfficePersonTestData('LEGAL', 'First Impressions GmbH');
|
||||
call createHsOfficePersonTestData('NATURAL', null, 'Peter', 'Smith');
|
||||
call createHsOfficePersonTestData('NATURAL', null, 'Smith', 'Peter');
|
||||
call createHsOfficePersonTestData('LEGAL', 'Rockshop e.K.', 'Sandra', 'Miller');
|
||||
call createHsOfficePersonTestData('SOLE_REPRESENTATION', 'Ostfriesische Kuhhandel OHG');
|
||||
call createHsOfficePersonTestData('JOINT_REPRESENTATION', 'Erben Bessler', 'Mel', 'Bessler');
|
||||
call createHsOfficePersonTestData('NATURAL', null, 'Bessler', 'Anita');
|
||||
call createHsOfficePersonTestData('NATURAL', null, 'Winkler', 'Paul');
|
||||
end;
|
||||
$$;
|
||||
--//
|
||||
|
@ -0,0 +1,19 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-MAIN-TABLE:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CREATE TYPE HsOfficeRelationshipType AS ENUM ('SOLE_AGENT', 'JOINT_AGENT', 'CO_OWNER', 'ACCOUNTING_CONTACT', 'TECHNICAL_CONTACT');
|
||||
|
||||
CREATE CAST (character varying as HsOfficeRelationshipType) WITH INOUT AS IMPLICIT;
|
||||
|
||||
create table if not exists hs_office_relationship
|
||||
(
|
||||
uuid uuid unique references RbacObject (uuid) initially deferred, -- on delete cascade
|
||||
relAnchorUuid uuid not null references hs_office_person(uuid),
|
||||
relHolderUuid uuid not null references hs_office_person(uuid),
|
||||
contactUuid uuid references hs_office_contact(uuid),
|
||||
relType HsOfficeRelationshipType not null
|
||||
);
|
||||
--//
|
@ -0,0 +1,192 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-rbac-OBJECT:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRelatedRbacObject('hs_office_relationship');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRoleDescriptors('hsOfficeRelationship', 'hs_office_relationship');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates and updates the roles and their assignments for relationship entities.
|
||||
*/
|
||||
|
||||
create or replace function hsOfficeRelationshipRbacRolesTrigger()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
declare
|
||||
hsOfficeRelationshipTenant RbacRoleDescriptor;
|
||||
ownerRole uuid;
|
||||
adminRole uuid;
|
||||
newRelAnchor hs_office_person;
|
||||
newRelHolder hs_office_person;
|
||||
oldContact hs_office_contact;
|
||||
newContact hs_office_contact;
|
||||
begin
|
||||
|
||||
hsOfficeRelationshipTenant := hsOfficeRelationshipTenant(NEW);
|
||||
|
||||
select * from hs_office_person as p where p.uuid = NEW.relAnchorUuid into newRelAnchor;
|
||||
select * from hs_office_person as p where p.uuid = NEW.relHolderUuid into newRelHolder;
|
||||
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
|
||||
-- the owner role with full access for admins of the relAnchor global admins
|
||||
ownerRole = createRole(
|
||||
hsOfficeRelationshipOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRoles(array[
|
||||
globalAdmin(),
|
||||
hsOfficePersonAdmin(newRelAnchor)])
|
||||
);
|
||||
|
||||
-- the admin role with full access for the owner
|
||||
adminRole = createRole(
|
||||
hsOfficeRelationshipAdmin(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
|
||||
beneathRole(ownerRole)
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
perform createRole(
|
||||
hsOfficeRelationshipTenant,
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
|
||||
beneathRoles(array[
|
||||
hsOfficePersonAdmin(newRelAnchor),
|
||||
hsOfficePersonAdmin(newRelHolder),
|
||||
hsOfficeContactAdmin(newContact)]),
|
||||
withSubRoles(array[
|
||||
hsOfficePersonTenant(newRelAnchor),
|
||||
hsOfficePersonTenant(newRelHolder),
|
||||
hsOfficeContactTenant(newContact)])
|
||||
);
|
||||
|
||||
-- anchor and holder admin roles need each others tenant role
|
||||
-- to be able to see the joined relationship
|
||||
call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder));
|
||||
call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor));
|
||||
call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact));
|
||||
|
||||
elsif TG_OP = 'UPDATE' then
|
||||
|
||||
if OLD.contactUuid <> NEW.contactUuid then
|
||||
-- nothing but the contact can be updated,
|
||||
-- in other cases, a new relationship needs to be created and the old updated
|
||||
|
||||
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
|
||||
|
||||
call revokeRoleFromRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(oldContact) );
|
||||
call grantRoleToRole( hsOfficeRelationshipTenant, hsOfficeContactAdmin(newContact) );
|
||||
|
||||
call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficeRelationshipTenant );
|
||||
call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficeRelationshipTenant );
|
||||
end if;
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
/*
|
||||
An AFTER INSERT TRIGGER which creates the role structure for a new customer.
|
||||
*/
|
||||
create trigger createRbacRolesForHsOfficeRelationship_Trigger
|
||||
after insert
|
||||
on hs_office_relationship
|
||||
for each row
|
||||
execute procedure hsOfficeRelationshipRbacRolesTrigger();
|
||||
|
||||
/*
|
||||
An AFTER UPDATE TRIGGER which updates the role structure of a customer.
|
||||
*/
|
||||
create trigger updateRbacRolesForHsOfficeRelationship_Trigger
|
||||
after update
|
||||
on hs_office_relationship
|
||||
for each row
|
||||
execute procedure hsOfficeRelationshipRbacRolesTrigger();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacIdentityView('hs_office_relationship', $idName$
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.relAnchorUuid)
|
||||
|| '-with-' || target.relType || '-' ||
|
||||
(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)
|
||||
$idName$);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call generateRbacRestrictedView('hs_office_relationship',
|
||||
'(select idName from hs_office_person_iv p where p.uuid = target.relHolderUuid)',
|
||||
$updates$
|
||||
contactUuid = new.contactUuid
|
||||
$updates$);
|
||||
--//
|
||||
|
||||
-- TODO: exception if one tries to amend any other column
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-rbac-NEW-RELATHIONSHIP:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Creates a global permission for new-relationship and assigns it to the hostsharing admins role.
|
||||
*/
|
||||
do language plpgsql $$
|
||||
declare
|
||||
addCustomerPermissions uuid[];
|
||||
globalObjectUuid uuid;
|
||||
globalAdminRoleUuid uuid ;
|
||||
begin
|
||||
call defineContext('granting global new-relationship permission to global admin role', null, null, null);
|
||||
|
||||
globalAdminRoleUuid := findRoleId(globalAdmin());
|
||||
globalObjectUuid := (select uuid from global);
|
||||
addCustomerPermissions := createPermissions(globalObjectUuid, array ['new-relationship']);
|
||||
call grantPermissionsToRole(globalAdminRoleUuid, addCustomerPermissions);
|
||||
end;
|
||||
$$;
|
||||
|
||||
/**
|
||||
Used by the trigger to prevent the add-customer to current user respectively assumed roles.
|
||||
*/
|
||||
create or replace function addHsOfficeRelationshipNotAllowedForCurrentSubjects()
|
||||
returns trigger
|
||||
language PLPGSQL
|
||||
as $$
|
||||
begin
|
||||
raise exception '[403] new-relationship 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_relationship_insert_trigger
|
||||
before insert
|
||||
on hs_office_relationship
|
||||
for each row
|
||||
-- TODO.spec: who is allowed to create new relationships
|
||||
when ( not hasAssumedRole() )
|
||||
execute procedure addHsOfficeRelationshipNotAllowedForCurrentSubjects();
|
||||
--//
|
||||
|
@ -0,0 +1,81 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-TEST-DATA-GENERATOR:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Creates a single relationship test record.
|
||||
*/
|
||||
create or replace procedure createHsOfficeRelationshipTestData(
|
||||
anchorPersonTradeName varchar,
|
||||
holderPersonFamilyName varchar,
|
||||
relationshipType HsOfficeRelationshipType,
|
||||
contactLabel varchar)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
currentTask varchar;
|
||||
idName varchar;
|
||||
anchorPerson hs_office_person;
|
||||
holderPerson hs_office_person;
|
||||
contact hs_office_contact;
|
||||
|
||||
begin
|
||||
idName := cleanIdentifier( anchorPersonTradeName|| '-' || holderPersonFamilyName);
|
||||
currentTask := 'creating RBAC test relationship ' || idName;
|
||||
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global.admin');
|
||||
execute format('set local hsadminng.currentTask to %L', currentTask);
|
||||
|
||||
select p.* from hs_office_person p where p.tradeName = anchorPersonTradeName into anchorPerson;
|
||||
select p.* from hs_office_person p where p.familyName = holderPersonFamilyName into holderPerson;
|
||||
select c.* from hs_office_contact c where c.label = contactLabel into contact;
|
||||
|
||||
raise notice 'creating test relationship: %', idName;
|
||||
raise notice '- using anchor person (%): %', anchorPerson.uuid, anchorPerson;
|
||||
raise notice '- using holder person (%): %', holderPerson.uuid, holderPerson;
|
||||
raise notice '- using contact (%): %', contact.uuid, contact;
|
||||
insert
|
||||
into hs_office_relationship (uuid, relanchoruuid, relholderuuid, reltype, contactUuid)
|
||||
values (uuid_generate_v4(), anchorPerson.uuid, holderPerson.uuid, relationshipType, contact.uuid);
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
/*
|
||||
Creates a range of test relationship for mass data generation.
|
||||
*/
|
||||
create or replace procedure createHsOfficeRelationshipTestData(
|
||||
startCount integer, -- count of auto generated rows before the run
|
||||
endCount integer -- count of auto generated rows after the run
|
||||
)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
person hs_office_person;
|
||||
contact hs_office_contact;
|
||||
begin
|
||||
for t in startCount..endCount
|
||||
loop
|
||||
select p.* from hs_office_person p where tradeName = intToVarChar(t, 4) into person;
|
||||
select c.* from hs_office_contact c where c.label = intToVarChar(t, 4) || '#' || t into contact;
|
||||
|
||||
call createHsOfficeRelationshipTestData(person.uuid, contact.uuid, 'SOLE_AGENT');
|
||||
commit;
|
||||
end loop;
|
||||
end; $$;
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset hs-office-relationship-TEST-DATA-GENERATION:1 –context=dev,tc endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
do language plpgsql $$
|
||||
begin
|
||||
call createHsOfficeRelationshipTestData('First Impressions GmbH', 'Smith', 'SOLE_AGENT', 'first contact');
|
||||
|
||||
call createHsOfficeRelationshipTestData('Rockshop e.K.', 'Smith', 'SOLE_AGENT', 'second contact');
|
||||
|
||||
call createHsOfficeRelationshipTestData('Ostfriesische Kuhhandel OHG', 'Smith', 'SOLE_AGENT', 'third contact');
|
||||
end;
|
||||
$$;
|
||||
--//
|
@ -65,3 +65,9 @@ databaseChangeLog:
|
||||
file: db/changelog/223-hs-office-partner-rbac.sql
|
||||
- include:
|
||||
file: db/changelog/228-hs-office-partner-test-data.sql
|
||||
- include:
|
||||
file: db/changelog/230-hs-office-relationship.sql
|
||||
- include:
|
||||
file: db/changelog/233-hs-office-relationship-rbac.sql
|
||||
- include:
|
||||
file: db/changelog/238-hs-office-relationship-test-data.sql
|
||||
|
Reference in New Issue
Block a user