introduces generateRbacRestrictedView to generate restricted view + triggers
This commit is contained in:
parent
2cae17a045
commit
44eb59c918
@ -18,4 +18,6 @@ public interface TestCustomerRepository extends Repository<TestCustomerEntity, U
|
|||||||
TestCustomerEntity save(final TestCustomerEntity entity);
|
TestCustomerEntity save(final TestCustomerEntity entity);
|
||||||
|
|
||||||
long count();
|
long count();
|
||||||
|
|
||||||
|
int deleteByUuid(UUID uuid);
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ begin
|
|||||||
|
|
||||||
createDeleteTriggerSQL = format($sql$
|
createDeleteTriggerSQL = format($sql$
|
||||||
create trigger deleteRbacRulesFor_%s_Trigger
|
create trigger deleteRbacRulesFor_%s_Trigger
|
||||||
before delete
|
after delete
|
||||||
on %s
|
on %s
|
||||||
for each row
|
for each row
|
||||||
execute procedure deleteRelatedRbacObject();
|
execute procedure deleteRelatedRbacObject();
|
||||||
@ -113,3 +113,121 @@ begin
|
|||||||
execute sql;
|
execute sql;
|
||||||
end; $$;
|
end; $$;
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
--changeset rbac-generators-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
create or replace procedure generateRbacRestrictedView(targetTable text, orderBy text, columnUpdates text)
|
||||||
|
language plpgsql as $$
|
||||||
|
declare
|
||||||
|
sql text;
|
||||||
|
begin
|
||||||
|
/*
|
||||||
|
Creates a restricted view based on the 'view' permission of the current subject.
|
||||||
|
*/
|
||||||
|
sql := format($sql$
|
||||||
|
set session session authorization default;
|
||||||
|
create view %1$s_rv as
|
||||||
|
select target.*
|
||||||
|
from %1$s as target
|
||||||
|
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', '%1$s', currentSubjectsUuids()))
|
||||||
|
order by %2$s;
|
||||||
|
grant all privileges on %1$s_rv to restricted;
|
||||||
|
$sql$, targetTable, orderBy);
|
||||||
|
execute sql;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Instead of insert trigger function for the restricted view.
|
||||||
|
*/
|
||||||
|
sql := format($sql$
|
||||||
|
create or replace function %1$sInsert()
|
||||||
|
returns trigger
|
||||||
|
language plpgsql as $f$
|
||||||
|
declare
|
||||||
|
newTargetRow %1$s;
|
||||||
|
begin
|
||||||
|
insert
|
||||||
|
into %1$s
|
||||||
|
values (new.*)
|
||||||
|
returning * into newTargetRow;
|
||||||
|
return newTargetRow;
|
||||||
|
end; $f$;
|
||||||
|
$sql$, targetTable);
|
||||||
|
execute sql;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates an instead of insert trigger for the restricted view.
|
||||||
|
*/
|
||||||
|
sql := format($sql$
|
||||||
|
create trigger %1$sInsert_tg
|
||||||
|
instead of insert
|
||||||
|
on %1$s_rv
|
||||||
|
for each row
|
||||||
|
execute function %1$sInsert();
|
||||||
|
$sql$, targetTable);
|
||||||
|
execute sql;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Instead of delete trigger function for the restricted view.
|
||||||
|
*/
|
||||||
|
sql := format($sql$
|
||||||
|
create or replace function %1$sDelete()
|
||||||
|
returns trigger
|
||||||
|
language plpgsql as $f$
|
||||||
|
begin
|
||||||
|
if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('delete', '%1$s', currentSubjectsUuids())) then
|
||||||
|
delete from %1$s p where p.uuid = old.uuid;
|
||||||
|
return old;
|
||||||
|
end if;
|
||||||
|
raise exception '[403] Subject %% is not allowed to delete %1$s uuid %%', currentSubjectsUuids(), old.uuid;
|
||||||
|
end; $f$;
|
||||||
|
$sql$, targetTable);
|
||||||
|
execute sql;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates an instead of delete trigger for the restricted view.
|
||||||
|
*/
|
||||||
|
sql := format($sql$
|
||||||
|
create trigger %1$sDelete_tg
|
||||||
|
instead of delete
|
||||||
|
on %1$s_rv
|
||||||
|
for each row
|
||||||
|
execute function %1$sDelete();
|
||||||
|
$sql$, targetTable);
|
||||||
|
execute sql;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Instead of update trigger function for the restricted view
|
||||||
|
based on the 'edit' permission of the current subject.
|
||||||
|
*/
|
||||||
|
sql := format($sql$
|
||||||
|
create or replace function %1$sUpdate()
|
||||||
|
returns trigger
|
||||||
|
language plpgsql as $f$
|
||||||
|
begin
|
||||||
|
if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('edit', '%1$s', currentSubjectsUuids())) then
|
||||||
|
update %1$s
|
||||||
|
set %2$s
|
||||||
|
where uuid = old.uuid;
|
||||||
|
return old;
|
||||||
|
end if;
|
||||||
|
raise exception '[403] Subject %% is not allowed to update %1$s uuid %%', currentSubjectsUuids(), old.uuid;
|
||||||
|
end; $f$;
|
||||||
|
$sql$, targetTable, columnUpdates);
|
||||||
|
execute sql;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates an instead of delete trigger for the restricted view.
|
||||||
|
*/
|
||||||
|
sql = format($sql$
|
||||||
|
create trigger %1$sUpdate_tg
|
||||||
|
instead of update
|
||||||
|
on %1$s_rv
|
||||||
|
for each row
|
||||||
|
execute function %1$sUpdate();
|
||||||
|
$sql$, targetTable);
|
||||||
|
execute sql;
|
||||||
|
end; $$;
|
||||||
|
--//
|
||||||
|
@ -87,17 +87,12 @@ call generateRbacIdentityView('test_customer', $idName$
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-customer-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
call generateRbacRestrictedView('test_customer', 'target.prefix',
|
||||||
Creates a view to the customer main table with row-level limitation
|
$updates$
|
||||||
based on the 'view' permission of the current user or assumed roles.
|
reference = new.reference,
|
||||||
*/
|
prefix = new.prefix,
|
||||||
set session session authorization default;
|
adminUserName = new.adminUserName
|
||||||
drop view if exists test_customer_rv;
|
$updates$);
|
||||||
create or replace view test_customer_rv as
|
|
||||||
select target.*
|
|
||||||
from test_customer as target
|
|
||||||
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'test_customer', currentSubjectsUuids()));
|
|
||||||
grant all privileges on test_customer_rv to restricted;
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ begin
|
|||||||
-- an owner role is created and assigned to the customer's admin role
|
-- an owner role is created and assigned to the customer's admin role
|
||||||
packageOwnerRoleUuid = createRole(
|
packageOwnerRoleUuid = createRole(
|
||||||
testPackageOwner(NEW),
|
testPackageOwner(NEW),
|
||||||
withoutPermissions(),
|
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||||
beneathRole(testCustomerAdmin(parentCustomer))
|
beneathRole(testCustomerAdmin(parentCustomer))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -64,7 +64,6 @@ end; $$;
|
|||||||
An AFTER INSERT TRIGGER which creates the role structure for a new package.
|
An AFTER INSERT TRIGGER which creates the role structure for a new package.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
drop trigger if exists createRbacRolesForTestPackage_Trigger on test_package;
|
|
||||||
create trigger createRbacRolesForTestPackage_Trigger
|
create trigger createRbacRolesForTestPackage_Trigger
|
||||||
after insert
|
after insert
|
||||||
on test_package
|
on test_package
|
||||||
@ -76,9 +75,7 @@ execute procedure createRbacRolesForTestPackage();
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
--changeset test-package-rbac-IDENTITY-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
call generateRbacIdentityView('test_package', $idName$
|
call generateRbacIdentityView('test_package', 'target.name');
|
||||||
target.name
|
|
||||||
$idName$);
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
@ -90,11 +87,22 @@ call generateRbacIdentityView('test_package', $idName$
|
|||||||
Creates a view to the customer main table which maps the identifying name
|
Creates a view to the customer main table which maps the identifying name
|
||||||
(in this case, the prefix) to the objectUuid.
|
(in this case, the prefix) to the objectUuid.
|
||||||
*/
|
*/
|
||||||
drop view if exists test_package_rv;
|
-- drop view if exists test_package_rv;
|
||||||
create or replace view test_package_rv as
|
-- create or replace view test_package_rv as
|
||||||
select target.*
|
-- select target.*
|
||||||
from test_package as target
|
-- from test_package as target
|
||||||
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'test_package', currentSubjectsUuids()))
|
-- where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'test_package', currentSubjectsUuids()))
|
||||||
order by target.name;
|
-- order by target.name;
|
||||||
grant all privileges on test_package_rv to restricted;
|
-- grant all privileges on test_package_rv to restricted;
|
||||||
|
|
||||||
|
call generateRbacRestrictedView('test_package', 'target.name',
|
||||||
|
$updates$
|
||||||
|
version = new.version,
|
||||||
|
customerUuid = new.customerUuid,
|
||||||
|
name = new.name,
|
||||||
|
description = new.description
|
||||||
|
$updates$);
|
||||||
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ begin
|
|||||||
|
|
||||||
insert
|
insert
|
||||||
into test_package (customerUuid, name, description)
|
into test_package (customerUuid, name, description)
|
||||||
values (cust.uuid, pacName, 'Here can add your own description of package ' || pacName || '.')
|
values (cust.uuid, pacName, 'Here you can add your own description of package ' || pacName || '.')
|
||||||
returning * into pac;
|
returning * into pac;
|
||||||
|
|
||||||
call grantRoleToUser(
|
call grantRoleToUser(
|
||||||
|
@ -100,7 +100,7 @@ call generateRbacIdentityView('test_domain', $idName$
|
|||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset test-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset test-domain-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -86,82 +86,16 @@ call generateRbacIdentityView('hs_office_contact', $idName$
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-contact-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset hs-office-contact-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
call generateRbacRestrictedView('hs_office_contact', 'target.label',
|
||||||
Creates a view to the contact main table with row-level limitation
|
$updates$
|
||||||
based on the 'view' permission of the current user or assumed roles.
|
label = new.label,
|
||||||
*/
|
postalAddress = new.postalAddress,
|
||||||
set session session authorization default;
|
emailAddresses = new.emailAddresses,
|
||||||
drop view if exists hs_office_contact_rv;
|
phoneNumbers = new.phoneNumbers
|
||||||
create or replace view hs_office_contact_rv as
|
$updates$);
|
||||||
select target.*
|
|
||||||
from hs_office_contact as target
|
|
||||||
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'hs_office_contact', currentSubjectsUuids()));
|
|
||||||
grant all privileges on hs_office_contact_rv to restricted;
|
|
||||||
--//
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-contact-rbac-INSTEAD-OF-INSERT-TRIGGER:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
Instead of insert trigger function for hs_office_contact_rv.
|
|
||||||
*/
|
|
||||||
create or replace function insertHsOfficeContact()
|
|
||||||
returns trigger
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
newUser hs_office_contact;
|
|
||||||
begin
|
|
||||||
insert
|
|
||||||
into hs_office_contact
|
|
||||||
values (new.*)
|
|
||||||
returning * into newUser;
|
|
||||||
return newUser;
|
|
||||||
end;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates an instead of insert trigger for the hs_office_contact_rv view.
|
|
||||||
*/
|
|
||||||
create trigger insertHsOfficeContact_Trigger
|
|
||||||
instead of insert
|
|
||||||
on hs_office_contact_rv
|
|
||||||
for each row
|
|
||||||
execute function insertHsOfficeContact();
|
|
||||||
--//
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-contact-rbac-INSTEAD-OF-DELETE-TRIGGER:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
Instead of delete trigger function for hs_office_contact_rv.
|
|
||||||
|
|
||||||
Checks if the current subject (user / assumed role) has the permission to delete the row.
|
|
||||||
*/
|
|
||||||
create or replace function deleteHsOfficeContact()
|
|
||||||
returns trigger
|
|
||||||
language plpgsql as $$
|
|
||||||
begin
|
|
||||||
if hasGlobalRoleGranted(currentUserUuid()) or
|
|
||||||
old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('delete', 'hs_office_contact', currentSubjectsUuids())) then
|
|
||||||
delete from hs_office_contact c where c.uuid = old.uuid;
|
|
||||||
return old;
|
|
||||||
end if;
|
|
||||||
raise exception '[403] User % not allowed to delete contact uuid %', currentUser(), old.uuid;
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates an instead of delete trigger for the hs_office_contact_rv view.
|
|
||||||
*/
|
|
||||||
create trigger deleteHsOfficeContact_Trigger
|
|
||||||
instead of delete
|
|
||||||
on hs_office_contact_rv
|
|
||||||
for each row
|
|
||||||
execute function deleteHsOfficeContact();
|
|
||||||
--/
|
--/
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-contact-rbac-NEW-CONTACT:1 endDelimiter:--//
|
--changeset hs-office-contact-rbac-NEW-CONTACT:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
@ -17,11 +17,9 @@ call generateRbacRoleDescriptors('hsOfficePerson', 'hs_office_person');
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-person-rbac-ROLES-CREATION:1 endDelimiter:--//
|
--changeset hs-office-person-rbac-ROLES-CREATION:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Creates the roles and their assignments for a new person for the AFTER INSERT TRIGGER.
|
Creates the roles and their assignments for a new person for the AFTER INSERT TRIGGER.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
create or replace function createRbacRolesForHsOfficePerson()
|
create or replace function createRbacRolesForHsOfficePerson()
|
||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql
|
language plpgsql
|
||||||
@ -85,82 +83,16 @@ call generateRbacIdentityView('hs_office_person', $idName$
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset hs-office-person-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
call generateRbacRestrictedView('hs_office_person', 'concat(target.tradeName, target.familyName, target.givenName)',
|
||||||
Creates a view to the person main table with row-level limitation
|
$updates$
|
||||||
based on the 'view' permission of the current user or assumed roles.
|
personType = new.personType,
|
||||||
*/
|
tradeName = new.tradeName,
|
||||||
set session session authorization default;
|
givenName = new.givenName,
|
||||||
drop view if exists hs_office_person_rv;
|
familyName = new.familyName
|
||||||
create or replace view hs_office_person_rv as
|
$updates$);
|
||||||
select target.*
|
|
||||||
from hs_office_person as target
|
|
||||||
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'hs_office_person', currentSubjectsUuids()));
|
|
||||||
grant all privileges on hs_office_person_rv to restricted;
|
|
||||||
--//
|
--//
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-person-rbac-INSTEAD-OF-INSERT-TRIGGER:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
Instead of insert trigger function for hs_office_person_rv.
|
|
||||||
*/
|
|
||||||
create or replace function insertHsOfficePerson()
|
|
||||||
returns trigger
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
newUser hs_office_person;
|
|
||||||
begin
|
|
||||||
insert
|
|
||||||
into hs_office_person
|
|
||||||
values (new.*)
|
|
||||||
returning * into newUser;
|
|
||||||
return newUser;
|
|
||||||
end;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates an instead of insert trigger for the hs_office_person_rv view.
|
|
||||||
*/
|
|
||||||
create trigger insertHsOfficePerson_Trigger
|
|
||||||
instead of insert
|
|
||||||
on hs_office_person_rv
|
|
||||||
for each row
|
|
||||||
execute function insertHsOfficePerson();
|
|
||||||
--//
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-person-rbac-INSTEAD-OF-DELETE-TRIGGER:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
Instead of delete trigger function for hs_office_person_rv.
|
|
||||||
|
|
||||||
Checks if the current subject (user / assumed role) has the permission to delete the row.
|
|
||||||
*/
|
|
||||||
create or replace function deleteHsOfficePerson()
|
|
||||||
returns trigger
|
|
||||||
language plpgsql as $$
|
|
||||||
begin
|
|
||||||
if hasGlobalRoleGranted(currentUserUuid()) or
|
|
||||||
old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('delete', 'hs_office_person', currentSubjectsUuids())) then
|
|
||||||
delete from hs_office_person c where c.uuid = old.uuid;
|
|
||||||
return old;
|
|
||||||
end if;
|
|
||||||
raise exception '[403] User % not allowed to delete person uuid %', currentUser(), old.uuid;
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates an instead of delete trigger for the hs_office_person_rv view.
|
|
||||||
*/
|
|
||||||
create trigger deleteHsOfficePerson_Trigger
|
|
||||||
instead of delete
|
|
||||||
on hs_office_person_rv
|
|
||||||
for each row
|
|
||||||
execute function deleteHsOfficePerson();
|
|
||||||
--/
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-person-rbac-NEW-PERSON:1 endDelimiter:--//
|
--changeset hs-office-person-rbac-NEW-PERSON:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
@ -127,120 +127,18 @@ call generateRbacIdentityView('hs_office_partner', $idName$
|
|||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
--changeset hs-office-partner-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
--changeset hs-office-partner-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
/*
|
call generateRbacRestrictedView('hs_office_partner',
|
||||||
Creates a view to the partner main table with row-level limitation
|
'(select idName from hs_office_person_iv p where p.uuid = target.personUuid)',
|
||||||
based on the 'view' permission of the current user or assumed roles.
|
$updates$
|
||||||
*/
|
personUuid = new.personUuid,
|
||||||
set session session authorization default;
|
|
||||||
drop view if exists hs_office_partner_rv;
|
|
||||||
create or replace view hs_office_partner_rv as
|
|
||||||
select target.*
|
|
||||||
from hs_office_partner as target
|
|
||||||
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'hs_office_partner', currentSubjectsUuids()));
|
|
||||||
grant all privileges on hs_office_partner_rv to restricted;
|
|
||||||
--//
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-partner-rbac-INSTEAD-OF-INSERT-TRIGGER:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
Instead of insert trigger function for hs_office_partner_rv.
|
|
||||||
*/
|
|
||||||
create or replace function insertHsOfficePartner()
|
|
||||||
returns trigger
|
|
||||||
language plpgsql as $$
|
|
||||||
declare
|
|
||||||
newUser hs_office_partner;
|
|
||||||
begin
|
|
||||||
insert
|
|
||||||
into hs_office_partner
|
|
||||||
values (new.*)
|
|
||||||
returning * into newUser;
|
|
||||||
return newUser;
|
|
||||||
end;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates an instead of insert trigger for the hs_office_partner_rv view.
|
|
||||||
*/
|
|
||||||
create trigger insertHsOfficePartner_Trigger
|
|
||||||
instead of insert
|
|
||||||
on hs_office_partner_rv
|
|
||||||
for each row
|
|
||||||
execute function insertHsOfficePartner();
|
|
||||||
--//
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-partner-rbac-INSTEAD-OF-DELETE-TRIGGER:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
Instead of delete trigger function for hs_office_partner_rv.
|
|
||||||
|
|
||||||
Checks if the current subject (user / assumed role) has the permission to delete the row.
|
|
||||||
*/
|
|
||||||
create or replace function deleteHsOfficePartner()
|
|
||||||
returns trigger
|
|
||||||
language plpgsql as $$
|
|
||||||
begin
|
|
||||||
if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('delete', 'hs_office_partner', currentSubjectsUuids())) then
|
|
||||||
delete from hs_office_partner p where p.uuid = old.uuid;
|
|
||||||
return old;
|
|
||||||
end if;
|
|
||||||
raise exception '[403] Subject % is not allowed to delete partner uuid %', currentSubjectsUuids(), old.uuid;
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates an instead of delete trigger for the hs_office_partner_rv view.
|
|
||||||
*/
|
|
||||||
create trigger deleteHsOfficePartner_Trigger
|
|
||||||
instead of delete
|
|
||||||
on hs_office_partner_rv
|
|
||||||
for each row
|
|
||||||
execute function deleteHsOfficePartner();
|
|
||||||
--/
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
|
||||||
--changeset hs-office-partner-rbac-INSTEAD-OF-UPDATE-TRIGGER:1 endDelimiter:--//
|
|
||||||
-- ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
Instead of update trigger function for hs_office_partner_rv.
|
|
||||||
|
|
||||||
Checks if the current subject (user / assumed role) has the permission to update the row.
|
|
||||||
*/
|
|
||||||
create or replace function updateHsOfficePartner()
|
|
||||||
returns trigger
|
|
||||||
language plpgsql as $$
|
|
||||||
begin
|
|
||||||
if old.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('edit', 'hs_office_partner', currentSubjectsUuids())) then
|
|
||||||
update hs_office_partner
|
|
||||||
set personUuid = new.personUuid,
|
|
||||||
contactUuid = new.contactUuid,
|
contactUuid = new.contactUuid,
|
||||||
registrationOffice = new.registrationOffice,
|
registrationOffice = new.registrationOffice,
|
||||||
registrationNumber = new.registrationNumber,
|
registrationNumber = new.registrationNumber,
|
||||||
birthday = new.birthday,
|
birthday = new.birthday,
|
||||||
birthName = new.birthName,
|
birthName = new.birthName,
|
||||||
dateOfDeath = new.dateOfDeath
|
dateOfDeath = new.dateOfDeath
|
||||||
where uuid = old.uuid;
|
$updates$);
|
||||||
return old;
|
--//
|
||||||
end if;
|
|
||||||
raise exception '[403] Subject % is not allowed to update partner uuid %', currentSubjectsUuids(), old.uuid;
|
|
||||||
end; $$;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates an instead of delete trigger for the hs_office_partner_rv view.
|
|
||||||
*/
|
|
||||||
create trigger updateHsOfficePartner_Trigger
|
|
||||||
instead of update
|
|
||||||
on hs_office_partner_rv
|
|
||||||
for each row
|
|
||||||
execute function updateHsOfficePartner();
|
|
||||||
--/
|
|
||||||
|
|
||||||
|
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
|
@ -422,11 +422,6 @@ class HsOfficePartnerControllerAcceptanceTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UUID toCleanup(final UUID tempPartnerUuid) {
|
|
||||||
tempPartnerUuids.add(tempPartnerUuid);
|
|
||||||
return tempPartnerUuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler() {
|
private HsOfficePartnerEntity givenSomeTemporaryPartnerBessler() {
|
||||||
return jpaAttempt.transacted(() -> {
|
return jpaAttempt.transacted(() -> {
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
@ -444,6 +439,11 @@ class HsOfficePartnerControllerAcceptanceTest {
|
|||||||
}).assertSuccessful().returnedValue();
|
}).assertSuccessful().returnedValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UUID toCleanup(final UUID tempPartnerUuid) {
|
||||||
|
tempPartnerUuids.add(tempPartnerUuid);
|
||||||
|
return tempPartnerUuid;
|
||||||
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
tempPartnerUuids.forEach(uuid -> {
|
tempPartnerUuids.forEach(uuid -> {
|
||||||
|
@ -243,7 +243,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
|
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
|
||||||
"[403] Subject ", " is not allowed to update partner uuid");
|
"[403] Subject ", " is not allowed to update hs_office_partner uuid");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -265,7 +265,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
|
result.assertExceptionWithRootCauseMessage(JpaSystemException.class,
|
||||||
"[403] Subject ", " is not allowed to update partner uuid");
|
"[403] Subject ", " is not allowed to update hs_office_partner uuid");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertThatPartnerActuallyInDatabase(final HsOfficePartnerEntity saved) {
|
private void assertThatPartnerActuallyInDatabase(final HsOfficePartnerEntity saved) {
|
||||||
@ -333,7 +333,7 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
|
|||||||
// then
|
// then
|
||||||
result.assertExceptionWithRootCauseMessage(
|
result.assertExceptionWithRootCauseMessage(
|
||||||
JpaSystemException.class,
|
JpaSystemException.class,
|
||||||
"[403] Subject ", " not allowed to delete partner");
|
"[403] Subject ", " not allowed to delete hs_office_partner");
|
||||||
assertThat(jpaAttempt.transacted(() -> {
|
assertThat(jpaAttempt.transacted(() -> {
|
||||||
context("superuser-alex@hostsharing.net");
|
context("superuser-alex@hostsharing.net");
|
||||||
return partnerRepo.findByUuid(givenPartner.getUuid());
|
return partnerRepo.findByUuid(givenPartner.getUuid());
|
||||||
|
@ -4,6 +4,8 @@ import io.restassured.RestAssured;
|
|||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||||
import net.hostsharing.hsadminng.context.Context;
|
import net.hostsharing.hsadminng.context.Context;
|
||||||
|
import net.hostsharing.test.JpaAttempt;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -11,6 +13,8 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||||||
import org.springframework.boot.test.web.server.LocalServerPort;
|
import org.springframework.boot.test.web.server.LocalServerPort;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@ -19,7 +23,7 @@ import static org.hamcrest.Matchers.*;
|
|||||||
|
|
||||||
@SpringBootTest(
|
@SpringBootTest(
|
||||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
classes = HsadminNgApplication.class
|
classes = { HsadminNgApplication.class, JpaAttempt.class }
|
||||||
)
|
)
|
||||||
@Transactional
|
@Transactional
|
||||||
class TestCustomerControllerAcceptanceTest {
|
class TestCustomerControllerAcceptanceTest {
|
||||||
@ -32,9 +36,15 @@ class TestCustomerControllerAcceptanceTest {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
Context contextMock;
|
Context contextMock;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
TestCustomerRepository testCustomerRepository;
|
TestCustomerRepository testCustomerRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
JpaAttempt jpaAttempt;
|
||||||
|
|
||||||
|
Set<UUID> tempPartnerUuids = new HashSet<>();
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
class ListCustomers {
|
class ListCustomers {
|
||||||
|
|
||||||
@ -46,7 +56,7 @@ class TestCustomerControllerAcceptanceTest {
|
|||||||
.port(port)
|
.port(port)
|
||||||
.when()
|
.when()
|
||||||
.get("http://localhost/api/test/customers")
|
.get("http://localhost/api/test/customers")
|
||||||
.then().assertThat()
|
.then().log().all().assertThat()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.body("[0].prefix", is("xxx"))
|
.body("[0].prefix", is("xxx"))
|
||||||
@ -119,8 +129,8 @@ class TestCustomerControllerAcceptanceTest {
|
|||||||
.body("""
|
.body("""
|
||||||
{
|
{
|
||||||
"reference": 90020,
|
"reference": 90020,
|
||||||
"prefix": "ttt",
|
"prefix": "uuu",
|
||||||
"adminUserName": "customer-admin@ttt.example.com"
|
"adminUserName": "customer-admin@uuu.example.com"
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
.port(port)
|
.port(port)
|
||||||
@ -129,22 +139,22 @@ class TestCustomerControllerAcceptanceTest {
|
|||||||
.then().assertThat()
|
.then().assertThat()
|
||||||
.statusCode(201)
|
.statusCode(201)
|
||||||
.contentType(ContentType.JSON)
|
.contentType(ContentType.JSON)
|
||||||
.body("prefix", is("ttt"))
|
.body("prefix", is("uuu"))
|
||||||
.header("Location", startsWith("http://localhost"))
|
.header("Location", startsWith("http://localhost"))
|
||||||
.extract().header("Location"); // @formatter:on
|
.extract().header("Location"); // @formatter:on
|
||||||
|
|
||||||
// finally, the new customer can be viewed by its own admin
|
// finally, the new customer can be viewed by its own admin
|
||||||
final var newUserUuid = UUID.fromString(
|
final var newUserUuid = toCleanup(UUID.fromString(
|
||||||
location.substring(location.lastIndexOf('/') + 1));
|
location.substring(location.lastIndexOf('/') + 1)));
|
||||||
context.define("customer-admin@ttt.example.com");
|
context.define("customer-admin@uuu.example.com");
|
||||||
assertThat(testCustomerRepository.findByUuid(newUserUuid))
|
assertThat(testCustomerRepository.findByUuid(newUserUuid))
|
||||||
.hasValueSatisfying(c -> assertThat(c.getPrefix()).isEqualTo("ttt"));
|
.hasValueSatisfying(c -> assertThat(c.getPrefix()).isEqualTo("uuu"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void globalAdmin_withoutAssumedRole_canAddCustomerWithGivenUuid() {
|
void globalAdmin_withoutAssumedRole_canAddCustomerWithGivenUuid() {
|
||||||
|
|
||||||
final var givenUuid = UUID.randomUUID();
|
final var givenUuid = toCleanup(UUID.randomUUID());
|
||||||
|
|
||||||
final var location = RestAssured // @formatter:off
|
final var location = RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -238,4 +248,22 @@ class TestCustomerControllerAcceptanceTest {
|
|||||||
assertThat(testCustomerRepository.findCustomerByOptionalPrefixLike("uuu")).hasSize(0);
|
assertThat(testCustomerRepository.findCustomerByOptionalPrefixLike("uuu")).hasSize(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UUID toCleanup(final UUID tempPartnerUuid) {
|
||||||
|
tempPartnerUuids.add(tempPartnerUuid);
|
||||||
|
return tempPartnerUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void cleanup() {
|
||||||
|
tempPartnerUuids.forEach(uuid -> {
|
||||||
|
jpaAttempt.transacted(() -> {
|
||||||
|
context.define("superuser-alex@hostsharing.net", null);
|
||||||
|
System.out.println("DELETING temporary partner: " + uuid);
|
||||||
|
final var entity = testCustomerRepository.findByUuid(uuid);
|
||||||
|
final var count = testCustomerRepository.deleteByUuid(uuid);
|
||||||
|
System.out.println("DELETED temporary partner: " + uuid + (count > 0 ? " successful" : " failed") + " (" + entity.map(TestCustomerEntity::getPrefix).orElse("???") + ")");
|
||||||
|
}).assertSuccessful();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ class TestPackageControllerAcceptanceTest {
|
|||||||
void withDescriptionUpdatesDescription() {
|
void withDescriptionUpdatesDescription() {
|
||||||
|
|
||||||
assumeThat(getDescriptionOfPackage("xxx00"))
|
assumeThat(getDescriptionOfPackage("xxx00"))
|
||||||
.isEqualTo("Here can add your own description of package xxx00.");
|
.isEqualTo("Here you can add your own description of package xxx00.");
|
||||||
|
|
||||||
final var randomDescription = RandomStringUtils.randomAlphanumeric(80);
|
final var randomDescription = RandomStringUtils.randomAlphanumeric(80);
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ class TestPackageControllerAcceptanceTest {
|
|||||||
.port(port)
|
.port(port)
|
||||||
.when()
|
.when()
|
||||||
.patch("http://localhost/api/test/packages/{uuidOfPackage}", getUuidOfPackage("xxx00"))
|
.patch("http://localhost/api/test/packages/{uuidOfPackage}", getUuidOfPackage("xxx00"))
|
||||||
.then()
|
.then().log().all()
|
||||||
.assertThat()
|
.assertThat()
|
||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
@ -118,7 +118,7 @@ class TestPackageControllerAcceptanceTest {
|
|||||||
void withNullDescriptionUpdatesDescriptionToNull() {
|
void withNullDescriptionUpdatesDescriptionToNull() {
|
||||||
|
|
||||||
assumeThat(getDescriptionOfPackage("xxx01"))
|
assumeThat(getDescriptionOfPackage("xxx01"))
|
||||||
.isEqualTo("Here can add your own description of package xxx01.");
|
.isEqualTo("Here you can add your own description of package xxx01.");
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
RestAssured
|
RestAssured
|
||||||
@ -147,7 +147,7 @@ class TestPackageControllerAcceptanceTest {
|
|||||||
void withoutDescriptionDoesNothing() {
|
void withoutDescriptionDoesNothing() {
|
||||||
|
|
||||||
assumeThat(getDescriptionOfPackage("xxx02"))
|
assumeThat(getDescriptionOfPackage("xxx02"))
|
||||||
.isEqualTo("Here can add your own description of package xxx02.");
|
.isEqualTo("Here you can add your own description of package xxx02.");
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
RestAssured
|
RestAssured
|
||||||
@ -163,7 +163,7 @@ class TestPackageControllerAcceptanceTest {
|
|||||||
.statusCode(200)
|
.statusCode(200)
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.body("name", is("xxx02"))
|
.body("name", is("xxx02"))
|
||||||
.body("description", is("Here can add your own description of package xxx02.")); // unchanged
|
.body("description", is("Here you can add your own description of package xxx02.")); // unchanged
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,22 +98,22 @@ class TestPackageRepositoryIntegrationTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
final var result1 = jpaAttempt.transacted(() -> {
|
final var result1 = jpaAttempt.transacted(() -> {
|
||||||
globalAdminWithAssumedRole("test_package#xxx00.admin");
|
globalAdminWithAssumedRole("test_package#xxx00.owner");
|
||||||
pac.setDescription("description set by thread 1");
|
pac.setDescription("description set by thread 1");
|
||||||
testPackageRepository.save(pac);
|
testPackageRepository.save(pac);
|
||||||
});
|
});
|
||||||
final var result2 = jpaAttempt.transacted(() -> {
|
final var result2 = jpaAttempt.transacted(() -> {
|
||||||
globalAdminWithAssumedRole("test_package#xxx00.admin");
|
globalAdminWithAssumedRole("test_package#xxx00.owner");
|
||||||
pac.setDescription("description set by thread 2");
|
pac.setDescription("description set by thread 2");
|
||||||
testPackageRepository.save(pac);
|
testPackageRepository.save(pac);
|
||||||
sleep(1500);
|
sleep(1500);
|
||||||
});
|
});
|
||||||
|
|
||||||
// then
|
// then
|
||||||
em.refresh(pac);
|
|
||||||
assertThat(pac.getDescription()).isEqualTo("description set by thread 1");
|
|
||||||
assertThat(result1.caughtException()).isNull();
|
assertThat(result1.caughtException()).isNull();
|
||||||
assertThat(result2.caughtException()).isInstanceOf(ObjectOptimisticLockingFailureException.class);
|
assertThat(result2.caughtException()).isInstanceOf(ObjectOptimisticLockingFailureException.class);
|
||||||
|
em.refresh(pac);
|
||||||
|
assertThat(pac.getDescription()).isEqualTo("description set by thread 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sleep(final int millis) {
|
private void sleep(final int millis) {
|
||||||
|
Loading…
Reference in New Issue
Block a user