introduces agent+guest role for role-system around debitor+partner

This commit is contained in:
Michael Hoennig 2022-10-12 15:48:56 +02:00
parent de0c8dcfbc
commit 0b60b9f0ff
25 changed files with 899 additions and 523 deletions

View File

@ -220,19 +220,52 @@ By this, all roles ob sub-objects, which are assigned to the 'admin' role, are a
The admin-role is granted to a role of those subjects who manage the business object. The admin-role is granted to a role of those subjects who manage the business object.
E.g. a 'package' is manged by the admin of the customer. E.g. a 'package' is manged by the admin of the customer.
Whoever has the admin-role assigned, do everything with the related business-object, including deleting (or deactivating) it. Whoever has the admin-role assigned, can usually edit the related business-object but not deleting (or deactivating) it.
In most cases, the permissions to the 'view' operation is granted through the 'tenant' role. The admin-role also comprises lesser roles, through which the view-permission is granted.
By this, all roles ob sub-objects, which are assigned to the 'tenent' role, are also granted to the 'admin'.
#### agent
The agent-role is not used in the examples of this document, because it's for more complex cases.
It's usually granted to those roles and users who represent the related business-object, but are not allowed to edit it.
Other than the tenant-role, it usually offers broader visibility of sub-business-objects (joined entities).
E.g. a package-admin is allowed to see the related debitor-business-object,
but not its banking data.
#### tenant #### tenant
The tenant-role is granted to everybody who needs to be able to view the business-object. The tenant-role is granted to everybody who needs to be able to view the business-object and (probably some) related business-objects.
Usually all owners, admins and tenants of sub-objects get this role granted. Usually all owners, admins and tenants of sub-objects get this role granted.
Some business-objects only have very limited data directly in the main business-object and store more sensitive data in special sub-objects (e.g. 'customer-details') to which tenants of sub-objects of the main-object (e.g. package admins) do not get view permission. Some business-objects only have very limited data directly in the main business-object and store more sensitive data in special sub-objects (e.g. 'customer-details') to which tenants of sub-objects of the main-object (e.g. package admins) do not get view permission.
#### guest
Like the agent-role, the guest-role too is not used in the examples of this document, because it's for more complex cases.
If the guest-role exists, the view-permission is granted to it, instead of to the tenant-role.
Other than the tenant-role, the guest-roles does never grant any roles of related objects.
Also, if the guest-role exists, the tenant-role receives the view-permission through the guest-role.
### Referenced Business Objects and Role-Depreciation
A general rule is, if one business object *origin* references another object *target* (in other words: one database table joins another table),
**and** a role for *origin* needs also access to *target*,
then usually the *target* role is granted to the *origin* role which is one level lower.
E.g. the admin-role of the *origin* object gets granted the agent-role (or, if it does not exist, then the tenant-role) of the *target* object.
Following this rule, also implies, that the number of indirections to which visibility can be granted is limited.
The admin-role of one object could be granted visibility to another object through at maximum 3 joins (agent->tenant->guest).
But not in all cases role-depreciation takes place.
E.g. often a tenant-role is granted another tenant-role,
because it should be again allowed to view sub-objects.
The same for the agent-role, often it is granted another agent-role.
## Example Users, Roles, Permissions and Business-Objects ## Example Users, Roles, Permissions and Business-Objects

View File

@ -1,5 +1,5 @@
package net.hostsharing.hsadminng.rbac.rbacrole; package net.hostsharing.hsadminng.rbac.rbacrole;
public enum RbacRoleType { public enum RbacRoleType {
owner, admin, tenant owner, admin, agent, tenant, guest
} }

View File

@ -186,7 +186,7 @@ end; $$;
*/ */
create type RbacRoleType as enum ('owner', 'admin', 'tenant'); create type RbacRoleType as enum ('owner', 'admin', 'agent', 'tenant', 'guest');
create table RbacRole create table RbacRole
( (

View File

@ -2,47 +2,19 @@
-- ============================================================================ -- ============================================================================
-- PERMISSIONS -- PERMISSIONS
--changeset rbac-role-builder-permissions:1 endDelimiter:--// --changeset rbac-role-builder-to-uuids:1 endDelimiter:--//
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
/*
*/ create or replace function toPermissionUuids(forObjectUuid uuid, permitOps RbacOp[])
returns uuid[]
create type RbacPermissions as
(
permissionUuids uuid[]
);
create or replace function grantingPermissions(forObjectUuid uuid, permitOps RbacOp[])
returns RbacPermissions
language plpgsql language plpgsql
strict as $$ strict as $$
begin begin
return row (createPermissions(forObjectUuid, permitOps))::RbacPermissions; return createPermissions(forObjectUuid, permitOps);
end; $$; end; $$;
create or replace function withoutPermissions() create or replace function toRoleUuids(roleDescriptors RbacRoleDescriptor[])
returns RbacPermissions returns uuid[]
language plpgsql
strict as $$
begin
return row (array []::uuid[]);
end; $$;
--//
--changeset rbac-role-builder-super-roles:1 endDelimiter:--//
/*
*/
create type RbacSuperRoles as
(
roleUuids uuid[]
);
create or replace function beneathRoles(roleDescriptors RbacRoleDescriptor[])
returns RbacSuperRoles
language plpgsql language plpgsql
strict as $$ strict as $$
declare declare
@ -56,145 +28,22 @@ begin
end if; end if;
end loop; end loop;
return row (superRoleUuids)::RbacSuperRoles; return superRoleUuids;
end; $$; end; $$;
create or replace function beneathRole(roleDescriptor RbacRoleDescriptor)
returns RbacSuperRoles
language plpgsql
strict as $$
begin
return beneathRoles(array [roleDescriptor]);
end; $$;
create or replace function beneathRole(roleUuid uuid)
returns RbacSuperRoles
language plpgsql
strict as $$
begin
return row (array [roleUuid]::uuid[])::RbacSuperRoles;
end; $$;
create or replace function asTopLevelRole()
returns RbacSuperRoles
language plpgsql
strict as $$
begin
return row (array []::uuid[])::RbacSuperRoles;
end; $$;
--//
-- =================================================================
-- SUB ROLES
--changeset rbac-role-builder-sub-roles:1 endDelimiter:--//
-- -----------------------------------------------------------------
/*
*/
create type RbacSubRoles as
(
roleUuids uuid[]
);
create or replace function beingItselfA(roleUuid uuid)
returns RbacSubRoles
language plpgsql
strict as $$
begin
return row (array [roleUuid]::uuid[])::RbacSubRoles;
end; $$;
create or replace function beingItselfA(roleDescriptor RbacRoleDescriptor)
returns RbacSubRoles
language plpgsql
strict as $$
begin
return beingItselfA(getRoleId(roleDescriptor, 'fail'));
end; $$;
create or replace function withSubRoles(roleDescriptors RbacRoleDescriptor[])
returns RbacSubRoles
language plpgsql
strict as $$
declare
subRoleDescriptor RbacRoleDescriptor;
subRoleUuids uuid[] := array []::uuid[];
begin
foreach subRoleDescriptor in array roleDescriptors
loop
if subRoleDescriptor is not null then
subRoleUuids := subRoleUuids || getRoleId(subRoleDescriptor, 'fail');
end if;
end loop;
return row (subRoleUuids)::RbacSubRoles;
end; $$;
create or replace function withoutSubRoles()
returns RbacSubRoles
language plpgsql
strict as $$
begin
return row (array []::uuid[]);
end; $$;
--//
-- =================================================================
-- USERS
--changeset rbac-role-builder-users:1 endDelimiter:--//
-- -----------------------------------------------------------------
/*
*/
create type RbacUsers as
(
userUuids uuid[]
);
create or replace function withUsers(userNames varchar[])
returns RbacUsers
language plpgsql
strict as $$
declare
userName varchar;
userUuids uuid[] := array []::uuid[];
begin
foreach userName in array userNames
loop
userUuids := userUuids || getRbacUserId(userName, 'fail');
end loop;
return row (userUuids)::RbacUsers;
end; $$;
create or replace function withUser(userName varchar, whenNotExists RbacWhenNotExists = 'fail')
returns RbacUsers
returns null on null input
language plpgsql as $$
begin
return row (array [getRbacUserId(userName, whenNotExists)]);
end; $$;
--//
-- ================================================================= -- =================================================================
-- CREATE ROLE -- CREATE ROLE
--changeset rbac-role-builder-create-role:1 endDelimiter:--// --changeset rbac-role-builder-create-role:1 endDelimiter:--//
-- ----------------------------------------------------------------- -- -----------------------------------------------------------------
/* create or replace function createRoleWithGrants(
*/
create or replace function createRole(
roleDescriptor RbacRoleDescriptor, roleDescriptor RbacRoleDescriptor,
permissions RbacPermissions, permissions RbacOp[] = array[]::RbacOp[],
superRoles RbacSuperRoles, incomingSuperRoles RbacRoleDescriptor[] = array[]::RbacRoleDescriptor[],
subRoles RbacSubRoles = null, outgoingSubRoles RbacRoleDescriptor[] = array[]::RbacRoleDescriptor[],
users RbacUsers = null, userUuids uuid[] = array[]::uuid[],
grantingRoleUuid uuid = null grantedByRole RbacRoleDescriptor = null
) )
returns uuid returns uuid
called on null input called on null input
@ -204,80 +53,37 @@ declare
superRoleUuid uuid; superRoleUuid uuid;
subRoleUuid uuid; subRoleUuid uuid;
userUuid uuid; userUuid uuid;
grantedByRoleUuid uuid;
begin begin
raise notice 'will createRole for %', roleDescriptor; raise notice 'will createRole for %', roleDescriptor;
roleUuid = createRole(roleDescriptor); roleUuid := createRole(roleDescriptor);
call grantPermissionsToRole(roleUuid, permissions.permissionUuids); if cardinality(permissions) >0 then
call grantPermissionsToRole(roleUuid, toPermissionUuids(roleDescriptor.objectuuid, permissions));
if superRoles is not null then
foreach superRoleUuid in array superRoles.roleuUids
loop
call grantRoleToRole(roleUuid, superRoleUuid);
end loop;
end if; end if;
if subRoles is not null then foreach superRoleUuid in array toRoleUuids(incomingSuperRoles)
foreach subRoleUuid in array subRoles.roleuUids loop
loop call grantRoleToRole(roleUuid, superRoleUuid);
call grantRoleToRole(subRoleUuid, roleUuid); end loop;
end loop;
end if;
if users is not null then foreach subRoleUuid in array toRoleUuids(outgoingSubRoles)
foreach userUuid in array users.useruUids loop
call grantRoleToRole(subRoleUuid, roleUuid);
end loop;
if cardinality(userUuids) > 0 then
if grantedByRole is null then
raise exception 'to directly assign users to roles, grantingRole has to be given';
end if;
grantedByRoleUuid := getRoleId(grantedByRole, 'fail');
foreach userUuid in array userUuids
loop loop
call grantRoleToUserUnchecked(grantingRoleUuid, roleUuid, userUuid); call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid);
end loop; end loop;
end if; end if;
return roleUuid; return roleUuid;
end; $$; end; $$;
create or replace function createRole(
roleDescriptor RbacRoleDescriptor,
permissions RbacPermissions,
users RbacUsers = null,
grantingRoleUuid uuid = null
)
returns uuid
called on null input
language plpgsql as $$
begin
return createRole(roleDescriptor, permissions, null, null, users, grantingRoleUuid);
end; $$;
create or replace function createRole(
roleDescriptor RbacRoleDescriptor,
permissions RbacPermissions,
subRoles RbacSubRoles,
users RbacUsers = null,
grantingRoleUuid uuid = null
)
returns uuid
called on null input
language plpgsql as $$
begin
return createRole(roleDescriptor, permissions, null, subRoles, users, grantingRoleUuid);
end; $$;
--// --//
-- =================================================================
-- CREATE ROLE
--changeset rbac-role-builder-GRANTED-BY-ROLE:1 endDelimiter:--//
-- -----------------------------------------------------------------
/*
Used in role-builder-DSL to convert a role descriptor to it's uuid
for use as `grantedByRoleUuid`.
*/
create or replace function grantedByRole(roleDescriptor RbacRoleDescriptor)
returns uuid
strict leakproof
language plpgsql as $$
begin
return getRoleId(roledescriptor, 'fail');
end; $$;
--//

View File

@ -58,6 +58,14 @@ begin
return roleDescriptor('%2$s', entity.uuid, 'admin'); return roleDescriptor('%2$s', entity.uuid, 'admin');
end; $f$; end; $f$;
create or replace function %1$sAgent(entity %2$s)
returns RbacRoleDescriptor
language plpgsql
strict as $f$
begin
return roleDescriptor('%2$s', entity.uuid, 'agent');
end; $f$;
create or replace function %1$sTenant(entity %2$s) create or replace function %1$sTenant(entity %2$s)
returns RbacRoleDescriptor returns RbacRoleDescriptor
language plpgsql language plpgsql
@ -66,6 +74,14 @@ begin
return roleDescriptor('%2$s', entity.uuid, 'tenant'); return roleDescriptor('%2$s', entity.uuid, 'tenant');
end; $f$; end; $f$;
create or replace function %1$sGuest(entity %2$s)
returns RbacRoleDescriptor
language plpgsql
strict as $f$
begin
return roleDescriptor('%2$s', entity.uuid, 'guest');
end; $f$;
$sql$, prefix, targetTable); $sql$, prefix, targetTable);
execute sql; execute sql;
end; $$; end; $$;

View File

@ -35,28 +35,28 @@ begin
end if; end if;
-- the owner role with full access for Hostsharing administrators -- the owner role with full access for Hostsharing administrators
testCustomerOwnerUuid = createRole( testCustomerOwnerUuid = createRoleWithGrants(
testCustomerOwner(NEW), testCustomerOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), permissions => array['*'],
beneathRole(globalAdmin()) incomingSuperRoles => array[globalAdmin()]
); );
-- the admin role for the customer's admins, who can view and add products -- the admin role for the customer's admins, who can view and add products
customerAdminUuid = createRole( customerAdminUuid = createRoleWithGrants(
testCustomerAdmin(NEW), testCustomerAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view', 'add-package']), permissions => array['view', 'add-package'],
-- NO auto assume for customer owner to avoid exploding permissions for administrators -- NO auto assume for customer owner to avoid exploding permissions for administrators
withUser(NEW.adminUserName, 'create'), -- implicitly ignored if null userUuids => array[getRbacUserId(NEW.adminUserName, 'create')], -- implicitly ignored if null
grantedByRole(globalAdmin()) grantedByRole => globalAdmin()
); );
-- allow the customer owner role (thus administrators) to assume the customer admin role -- allow the customer owner role (thus administrators) to assume the customer admin role
call grantRoleToRole(customerAdminUuid, testCustomerOwnerUuid, false); call grantRoleToRole(customerAdminUuid, testCustomerOwnerUuid, false);
-- the tenant role which later can be used by owners+admins of sub-objects -- the tenant role which later can be used by owners+admins of sub-objects
perform createRole( perform createRoleWithGrants(
testCustomerTenant(NEW), testCustomerTenant(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']) permissions => array['view']
); );
return NEW; return NEW;

View File

@ -36,25 +36,25 @@ begin
select * from test_customer as c where c.uuid = NEW.customerUuid into parentCustomer; select * from test_customer as c where c.uuid = NEW.customerUuid into parentCustomer;
-- 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( perform createRoleWithGrants(
testPackageOwner(NEW), testPackageOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), permissions => array ['*'],
beneathRole(testCustomerAdmin(parentCustomer)) incomingSuperRoles => array[testCustomerAdmin(parentCustomer)]
); );
-- an owner role is created and assigned to the package owner role -- an owner role is created and assigned to the package owner role
packageAdminRoleUuid = createRole( perform createRoleWithGrants(
testPackageAdmin(NEW), testPackageAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['add-domain']), permissions => array ['add-domain'],
beneathRole(packageOwnerRoleUuid) incomingSuperRoles => array[testPackageOwner(NEW)]
); );
-- and a package tenant role is created and assigned to the package admin as well -- and a package tenant role is created and assigned to the package admin as well
perform createRole( perform createRoleWithGrants(
testPackageTenant(NEW), testPackageTenant(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']), permissions => array['view'],
beneathRole(packageAdminRoleUuid), incomingsuperroles => array[testPackageAdmin(NEW)],
beingItselfA(testCustomerTenant(parentCustomer)) outgoingSubRoles => array[testCustomerTenant(parentCustomer)]
); );
return NEW; return NEW;

View File

@ -26,10 +26,10 @@ begin
return domainTenantRoleUuid; return domainTenantRoleUuid;
end if; end if;
return createRole( return createRoleWithGrants(
domainTenantRoleDesc, domainTenantRoleDesc,
grantingPermissions(forObjectUuid => domain.uuid, permitOps => array ['view']), permissions => array['view'],
beneathRole(testdomainAdmin(domain)) incomingSuperRoles => array[testdomainAdmin(domain)]
); );
end; $$; end; $$;
--// --//
@ -48,8 +48,6 @@ create or replace function createRbacRulesForTestDomain()
strict as $$ strict as $$
declare declare
parentPackage test_package; parentPackage test_package;
domainOwnerRoleId uuid;
domainAdminRoleId uuid;
begin begin
if TG_OP <> 'INSERT' then if TG_OP <> 'INSERT' then
raise exception 'invalid usage of TRIGGER AFTER INSERT'; raise exception 'invalid usage of TRIGGER AFTER INSERT';
@ -58,18 +56,18 @@ begin
select * from test_package where uuid = NEW.packageUuid into parentPackage; select * from test_package where uuid = NEW.packageUuid into parentPackage;
-- an owner role is created and assigned to the package's admin group -- an owner role is created and assigned to the package's admin group
domainOwnerRoleId = createRole( perform createRoleWithGrants(
testdomainOwner(NEW), testDomainOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), permissions => array['*'],
beneathRole(testPackageAdmin(parentPackage)) incomingSuperRoles => array[testPackageAdmin(parentPackage)]
); );
-- and a domain admin role is created and assigned to the domain owner as well -- and a domain admin role is created and assigned to the domain owner as well
domainAdminRoleId = createRole( perform createRoleWithGrants(
testdomainAdmin(NEW), testDomainAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']), permissions => array['edit'],
beneathRole(domainOwnerRoleId), incomingSuperRoles => array[testDomainOwner(NEW)],
beingItselfA(testPackageTenant(parentPackage)) outgoingSubRoles => array[testPackageTenant(parentPackage)]
); );
-- a tenent role is only created on demand -- a tenent role is only created on demand

View File

@ -34,28 +34,29 @@ begin
raise exception 'invalid usage of TRIGGER AFTER INSERT'; raise exception 'invalid usage of TRIGGER AFTER INSERT';
end if; end if;
-- the owner role with full access for the creator assigned to the current user perform createRoleWithGrants(
ownerRole := createRole( hsOfficeContactOwner(NEW),
hsOfficeContactOwner(NEW), permissions => array['*'],
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), incomingSuperRoles => array[globalAdmin()],
beneathRole(globalAdmin()), userUuids => array[currentUserUuid()],
withoutSubRoles(), grantedByRole => globalAdmin()
withUser(currentUser()), -- TODO.spec: Who is owner of a new contact?
grantedByRole(globalAdmin())
); );
-- the tenant role for those related users who can view the data perform createRoleWithGrants(
adminRole := createRole(
hsOfficeContactAdmin(NEW), hsOfficeContactAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']), permissions => array['edit'],
beneathRole(ownerRole) incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
); );
-- the tenant role for those related users who can view the data perform createRoleWithGrants(
perform createRole( hsOfficeContactTenant(NEW),
hsOfficeContactTenant(NEW), incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']), );
beneathRole(adminRole)
perform createRoleWithGrants(
hsOfficeContactGuest(NEW),
permissions => array['view'],
incomingSuperRoles => array[hsOfficeContactTenant(NEW)]
); );
return NEW; return NEW;

View File

@ -24,36 +24,34 @@ create or replace function createRbacRolesForHsOfficePerson()
returns trigger returns trigger
language plpgsql language plpgsql
strict as $$ strict as $$
declare
ownerRole uuid;
adminRole uuid;
begin begin
if TG_OP <> 'INSERT' then if TG_OP <> 'INSERT' then
raise exception 'invalid usage of TRIGGER AFTER INSERT'; raise exception 'invalid usage of TRIGGER AFTER INSERT';
end if; end if;
-- the owner role with full access for the creator assigned to the current user perform createRoleWithGrants(
ownerRole := createRole(
hsOfficePersonOwner(NEW), hsOfficePersonOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), permissions => array['*'],
beneathRole(globalAdmin()), incomingSuperRoles => array[globalAdmin()],
withoutSubRoles(), userUuids => array[currentUserUuid()],
withUser(currentUser()), -- TODO.spec: Who is owner of a new person? grantedByRole => globalAdmin()
grantedByRole(globalAdmin())
); );
-- the tenant role for those related users who can view the data perform createRoleWithGrants(
adminRole := createRole(
hsOfficePersonAdmin(NEW), hsOfficePersonAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']), permissions => array['edit'],
beneathRole(ownerRole) incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
); );
-- the tenant role for those related users who can view the data perform createRoleWithGrants(
perform createRole(
hsOfficePersonTenant(NEW), hsOfficePersonTenant(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']), incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
beneathRole(adminRole) );
perform createRoleWithGrants(
hsOfficePersonGuest(NEW),
permissions => array['view'],
incomingSuperRoles => array[hsOfficePersonTenant(NEW)]
); );
return NEW; return NEW;

View File

@ -0,0 +1,66 @@
### hs_office_partner RBAC
```mermaid
flowchart TB
subgraph global
style global fill:#eee
role:global.admin[global.admin]
end
subgraph hsOfficeContact
direction TB
style hsOfficeContact fill:#eee
role:hsOfficeContact.admin[contact.admin]
--> role:hsOfficeContact.tenant[contact.tenant]
--> role:hsOfficeContact.guest[contact.guest]
end
subgraph hsOfficePerson
direction TB
style hsOfficePerson fill:#eee
role:hsOfficePerson.admin[person.admin]
--> role:hsOfficePerson.tenant[person.tenant]
--> role:hsOfficePerson.guest[person.guest]
end
subgraph hsOfficePartner
role:hsOfficePartner.owner[partner.owner]
%% permissions
role:hsOfficePartner.owner --> perm:hsOfficePartner.*{{partner.*}}
%% incoming
role:global.admin ---> role:hsOfficePartner.owner
role:hsOfficePartner.admin[partner.admin]
%% permissions
role:hsOfficePartner.admin --> perm:hsOfficePartner.edit{{partner.edit}}
%% incoming
role:hsOfficePartner.owner ---> role:hsOfficePartner.admin
%% outgoing
role:hsOfficePartner.admin --> role:hsOfficePerson.tenant
role:hsOfficePartner.admin --> role:hsOfficeContact.tenant
role:hsOfficePartner.agent[partner.agent]
%% incoming
role:hsOfficePartner.admin ---> role:hsOfficePartner.agent
role:hsOfficePerson.admin --> role:hsOfficePartner.agent
role:hsOfficeContact.admin --> role:hsOfficePartner.agent
role:hsOfficePartner.tenant[partner.tenant]
%% incoming
role:hsOfficePartner.agent --> role:hsOfficePartner.tenant
%% outgoing
role:hsOfficePartner.tenant --> role:hsOfficePerson.guest
role:hsOfficePartner.tenant --> role:hsOfficeContact.guest
role:hsOfficePartner.guest[partner.guest]
%% permissions
role:hsOfficePartner.guest --> perm:hsOfficePartner.view{{partner.view}}
%% incoming
role:hsOfficePartner.tenant --> role:hsOfficePartner.guest
end
```

View File

@ -28,46 +28,56 @@ create or replace function hsOfficePartnerRbacRolesTrigger()
strict as $$ strict as $$
declare declare
hsOfficePartnerTenant RbacRoleDescriptor; hsOfficePartnerTenant RbacRoleDescriptor;
ownerRole uuid;
adminRole uuid;
oldPerson hs_office_person; oldPerson hs_office_person;
newPerson hs_office_person; newPerson hs_office_person;
oldContact hs_office_contact; oldContact hs_office_contact;
newContact hs_office_contact; newContact hs_office_contact;
begin begin
hsOfficePartnerTenant := hsOfficePartnerTenant(NEW);
select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson; select * from hs_office_person as p where p.uuid = NEW.personUuid into newPerson;
select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact; select * from hs_office_contact as c where c.uuid = NEW.contactUuid into newContact;
if TG_OP = 'INSERT' then if TG_OP = 'INSERT' then
-- the owner role with full access for the global admins perform createRoleWithGrants(
ownerRole = createRole(
hsOfficePartnerOwner(NEW), hsOfficePartnerOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), permissions => array['*'],
beneathRole(globalAdmin()) incomingSuperRoles => array[globalAdmin()]
); );
-- the admin role with full access for owner perform createRoleWithGrants(
adminRole = createRole(
hsOfficePartnerAdmin(NEW), hsOfficePartnerAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']), permissions => array['edit'],
beneathRole(ownerRole) incomingSuperRoles => array[
hsOfficePartnerOwner(NEW)],
outgoingSubRoles => array[
hsOfficePersonTenant(newPerson),
hsOfficeContactTenant(newContact)]
); );
-- the tenant role for those related users who can view the data perform createRoleWithGrants(
perform createRole( hsOfficePartnerAgent(NEW),
hsOfficePartnerTenant, incomingSuperRoles => array[
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
beneathRoles(array[
hsOfficePartnerAdmin(NEW), hsOfficePartnerAdmin(NEW),
hsOfficePersonAdmin(newPerson), hsOfficePersonAdmin(newPerson),
hsOfficeContactAdmin(newContact)]), hsOfficeContactAdmin(newContact)]
withSubRoles(array[ );
hsOfficePersonTenant(newPerson),
hsOfficeContactTenant(newContact)]) perform createRoleWithGrants(
hsOfficePartnerTenant(NEW),
incomingSuperRoles => array[
hsOfficePartnerAgent(NEW)],
outgoingSubRoles => array[
hsOfficePersonGuest(newPerson),
hsOfficeContactGuest(newContact)]
);
perform createRoleWithGrants(
hsOfficePartnerGuest(NEW),
permissions => array['view'],
incomingSuperRoles => array[
hsOfficePartnerTenant(NEW)]
); );
elsif TG_OP = 'UPDATE' then elsif TG_OP = 'UPDATE' then
@ -75,21 +85,27 @@ begin
if OLD.personUuid <> NEW.personUuid then if OLD.personUuid <> NEW.personUuid then
select * from hs_office_person as p where p.uuid = OLD.personUuid into oldPerson; select * from hs_office_person as p where p.uuid = OLD.personUuid into oldPerson;
call revokeRoleFromRole( hsOfficePartnerTenant, hsOfficePersonAdmin(oldPerson) ); call revokeRoleFromRole(hsOfficePersonTenant(oldPerson), hsOfficePartnerAdmin(OLD));
call grantRoleToRole( hsOfficePartnerTenant, hsOfficePersonAdmin(newPerson) ); call grantRoleToRole(hsOfficePersonTenant(newPerson), hsOfficePartnerAdmin(NEW));
call revokeRoleFromRole( hsOfficePersonTenant(oldPerson), hsOfficePartnerTenant ); call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficePersonAdmin(oldPerson));
call grantRoleToRole( hsOfficePersonTenant(newPerson), hsOfficePartnerTenant ); call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficePersonAdmin(newPerson));
call revokeRoleFromRole(hsOfficePersonGuest(oldPerson), hsOfficePartnerTenant(OLD));
call grantRoleToRole(hsOfficePersonGuest(newPerson), hsOfficePartnerTenant(NEW));
end if; end if;
if OLD.contactUuid <> NEW.contactUuid then if OLD.contactUuid <> NEW.contactUuid then
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact; select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
call revokeRoleFromRole( hsOfficePartnerTenant, hsOfficeContactAdmin(oldContact) ); call revokeRoleFromRole(hsOfficeContactTenant(oldContact), hsOfficePartnerAdmin(OLD));
call grantRoleToRole( hsOfficePartnerTenant, hsOfficeContactAdmin(newContact) ); call grantRoleToRole(hsOfficeContactTenant(newContact), hsOfficePartnerAdmin(NEW));
call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficePartnerTenant ); call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeContactAdmin(oldContact));
call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficePartnerTenant ); call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeContactAdmin(newContact));
call revokeRoleFromRole(hsOfficeContactGuest(oldContact), hsOfficePartnerTenant(OLD));
call grantRoleToRole(hsOfficeContactGuest(newContact), hsOfficePartnerTenant(NEW));
end if; end if;
else else
raise exception 'invalid usage of TRIGGER'; raise exception 'invalid usage of TRIGGER';

View File

@ -0,0 +1,192 @@
### hs_office_relationship RBAC
```mermaid
flowchart TB
subgraph global
style global fill:#eee
role:global.admin[global.admin]
end
subgraph hsOfficeContact
direction TB
style hsOfficeContact fill:#eee
role:hsOfficeContact.admin[contact.admin]
--> role:hsOfficeContact.tenant[contact.tenant]
--> role:hsOfficeContact.guest[contact.guest]
end
subgraph hsOfficePerson
direction TB
style hsOfficePerson fill:#eee
role:hsOfficePerson.admin[person.admin]
--> role:hsOfficePerson.tenant[person.tenant]
--> role:hsOfficePerson.guest[person.guest]
end
subgraph hsOfficeRelationship
role:hsOfficePerson#relAnchor.admin[person#anchor.admin]
--- role:hsOfficePerson.admin
role:hsOfficeRelationship.owner[relationship.owner]
%% permissions
role:hsOfficeRelationship.owner --> perm:hsOfficeRelationship.*{{relationship.*}}
%% incoming
role:global.admin ---> role:hsOfficeRelationship.owner
role:hsOfficePersonAdmin#relAnchor.admin
end
```
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();
--//

View File

@ -28,8 +28,6 @@ create or replace function hsOfficeRelationshipRbacRolesTrigger()
strict as $$ strict as $$
declare declare
hsOfficeRelationshipTenant RbacRoleDescriptor; hsOfficeRelationshipTenant RbacRoleDescriptor;
ownerRole uuid;
adminRole uuid;
newRelAnchor hs_office_person; newRelAnchor hs_office_person;
newRelHolder hs_office_person; newRelHolder hs_office_person;
oldContact hs_office_contact; oldContact hs_office_contact;
@ -44,38 +42,38 @@ begin
if TG_OP = 'INSERT' then if TG_OP = 'INSERT' then
-- the owner role with full access for admins of the relAnchor global admins perform createRoleWithGrants(
ownerRole = createRole(
hsOfficeRelationshipOwner(NEW), hsOfficeRelationshipOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), permissions => array['*'],
beneathRoles(array[ incomingSuperRoles => array[
globalAdmin(), globalAdmin(),
hsOfficePersonAdmin(newRelAnchor)]) hsOfficePersonAdmin(newRelAnchor)]
); );
-- the admin role with full access for the owner perform createRoleWithGrants(
adminRole = createRole(
hsOfficeRelationshipAdmin(NEW), hsOfficeRelationshipAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']), permissions => array['edit'],
beneathRole(ownerRole) incomingSuperRoles => array[hsOfficeRelationshipOwner(NEW)]
); );
-- the tenant role for those related users who can view the data -- the tenant role for those related users who can view the data
perform createRole( perform createRoleWithGrants(
hsOfficeRelationshipTenant, hsOfficeRelationshipTenant,
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']), permissions => array['view'],
beneathRoles(array[ incomingSuperRoles => array[
hsOfficeRelationshipAdmin(NEW),
hsOfficePersonAdmin(newRelAnchor), hsOfficePersonAdmin(newRelAnchor),
hsOfficePersonAdmin(newRelHolder), hsOfficePersonAdmin(newRelHolder),
hsOfficeContactAdmin(newContact)]), hsOfficeContactAdmin(newContact)],
withSubRoles(array[ outgoingSubRoles => array[
hsOfficePersonTenant(newRelAnchor), hsOfficePersonTenant(newRelAnchor),
hsOfficePersonTenant(newRelHolder), hsOfficePersonTenant(newRelHolder),
hsOfficeContactTenant(newContact)]) hsOfficeContactTenant(newContact)]
); );
-- anchor and holder admin roles need each others tenant role -- anchor and holder admin roles need each others tenant role
-- to be able to see the joined relationship -- to be able to see the joined relationship
-- TODO: this can probably be avoided through agent+guest roles
call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder)); call grantRoleToRole(hsOfficePersonTenant(newRelAnchor), hsOfficePersonAdmin(newRelHolder));
call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor)); call grantRoleToRole(hsOfficePersonTenant(newRelHolder), hsOfficePersonAdmin(newRelAnchor));
call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact)); call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact));

View File

@ -3,41 +3,38 @@
```mermaid ```mermaid
flowchart TB flowchart TB
%% ---------- generated start: ----------
subgraph global subgraph global
style hsOfficeBankAccount fill: #e9f7ef
role:global.admin[global.admin] role:global.admin[global.admin]
end end
subgraph context subgraph hsOfficeBankAccount
user:current([current]) direction TB
style hsOfficeBankAccount fill: #e9f7ef
user:hsOfficeBankAccount.creator([bankAccount.creator])
role:hsOfficeBankAccount.owner[[bankAccount.owner]]
%% permissions
role:hsOfficeBankAccount.owner --> perm:hsOfficeBankAccount.*{{hsOfficeBankAccount.delete}}
%% incoming
role:global.admin --> role:hsOfficeBankAccount.owner
user:hsOfficeBankAccount.creator ---> role:hsOfficeBankAccount.owner
role:hsOfficeBankAccount.admin[[bankAccount.admin]]
%% incoming
role:hsOfficeBankAccount.owner ---> role:hsOfficeBankAccount.admin
role:hsOfficeBankAccount.tenant[[bankAccount.tenant]]
%% incoming
role:hsOfficeBankAccount.admin ---> role:hsOfficeBankAccount.tenant
role:hsOfficeBankAccount.guest[[bankAccount.guest]]
%% permissions
role:hsOfficeBankAccount.guest --> perm:hsOfficeBankAccount.view{{hsOfficeBankAccount.view}}
%% incoming
role:hsOfficeBankAccount.tenant ---> role:hsOfficeBankAccount.guest
end end
subgraph bankaccount
subgraph roles[ ]
role:bankaccount.owner[[bankaccount.owner]]
role:bankaccount.admin[[bankaccount.admin]]
role:bankaccount.tenant[[bankaccount.tenant]]
end
subgraph perms[ ]
perm:bankaccount.delete{{bankaccount.delete}}
perm:bankaccount.view{{bankaccount.view}}
end
end
%% ---------- generated end. ----------
role:bankaccount.owner --> perm:bankaccount.delete
role:global.admin --> role:bankaccount.owner
user:current --> role:bankaccount.owner
role:bankaccount.owner --> role:bankaccount.admin
role:bankaccount.admin --> role:bankaccount.tenant
role:bankaccount.tenant --> perm:bankaccount.view
``` ```

View File

@ -26,40 +26,33 @@ create or replace function createRbacRolesForHsOfficeBankAccount()
returns trigger returns trigger
language plpgsql language plpgsql
strict as $$ strict as $$
declare
ownerRole uuid;
adminRole uuid;
begin begin
if TG_OP <> 'INSERT' then if TG_OP <> 'INSERT' then
raise exception 'invalid usage of TRIGGER AFTER INSERT'; raise exception 'invalid usage of TRIGGER AFTER INSERT';
end if; end if;
-- the owner role with full access for the creator assigned to the current user perform createRoleWithGrants(
ownerRole := createRole( hsOfficeBankAccountOwner(NEW),
hsOfficeBankAccountOwner(NEW), permissions => array['delete'],
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['delete']), incomingSuperRoles => array[globalAdmin()],
beneathRole(globalAdmin()), userUuids => array[currentUserUuid()],
withoutSubRoles(), grantedByRole => globalAdmin()
withUser(currentUser()), -- TODO.spec: Who is owner of a new bankaccount?
grantedByRole(globalAdmin())
); );
-- the admin role for those related users who can view the data and related records perform createRoleWithGrants(
adminRole := createRole(
hsOfficeBankAccountAdmin(NEW), hsOfficeBankAccountAdmin(NEW),
-- Where bankaccounts can be created, assigned, re-assigned and deleted, they cannot be updated. incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
-- Thus SQL UPDATE and 'edit' permission are being implemented.
withoutPermissions(),
beneathRole(ownerRole)
); );
-- TODO.spec: assumption can not be updated perform createRoleWithGrants(
hsOfficeBankAccountTenant(NEW),
incomingSuperRoles => array[hsOfficeBankAccountAdmin(NEW)]
);
-- the tenant role for those related users who can view the data perform createRoleWithGrants(
perform createRole( hsOfficeBankAccountGuest(NEW),
hsOfficeBankAccountTenant(NEW), permissions => array['view'],
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']), incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)]
beneathRole(adminRole)
); );
return NEW; return NEW;

View File

@ -1,49 +1,208 @@
### hs_office_debitor RBAC Roles ### hs_office_debitor RBAC Roles
```mermaid ```mermaid
flowchart TB; flowchart TB
subgraph bankaccount; subgraph global
style global fill:#eee
%% oversimplified version for now role:global.admin[global.admin]
%% end
%% Beware: role:debitor.tenant should NOT be granted role:bankaccount.tenent
%% because otherwise, later in the development,
%% e.g. package admins could see the debitors bank account,
%% except if we do NOT use the debitor in the hosting super module.
role:bankaccount.tenant --> perm:bankaccount.view{{bankaccount.view}}; subgraph office
end; style office fill:#eee
subgraph debitor[" "]; subgraph bankaccount
direction TB; style bankaccount fill: #e9f7ef
role:debitor.owner[[debitor.owner]] user:hsOfficeBankAccount.creator([bankaccount.creator])
role:debitor.owner --> perm:debitor.*{{debitor.*}};
role:debitor.admin[[debitor.admin]] role:hsOfficeBankAccount.owner[bankaccount.owner]
%% super-roles %% permissions
role:debitor.owner --> role:debitor.admin; role:hsOfficeBankAccount.owner --> perm:hsOfficeBankAccount.*{{bankaccount.*}}
role:partner.admin --> role:debitor.admin; %% incoming
role:person.admin --> role:debitor.admin; role:global.admin --> role:hsOfficeBankAccount.owner
role:contact.admin --> role:debitor.admin; user:hsOfficeBankAccount.creator ---> role:hsOfficeBankAccount.owner
%% sub-roles
role:debitor.admin --> role:partner.tenant;
role:debitor.admin --> role:person.tenant;
role:debitor.admin --> role:contact.tenant;
role:debitor.admin --> role:bankaccount.tenant;
role:debitor.tenant[[debitor.tenant]] role:hsOfficeBankAccount.admin[bankaccount.admin]
role:debitor.tenant --> perm:debitor.view{{debitor.view}}; %% permissions
%% super-roles role:hsOfficeBankAccount.admin --> perm:hsOfficeBankAccount.edit{{bankaccount.edit}}
role:debitor.admin --> role:debitor.tenant; %% incoming
%% sub-roles role:hsOfficeBankAccount.owner ---> role:hsOfficeBankAccount.admin
end; role:hsOfficeBankAccount.tenant[bankaccount.tenant]
%% incoming
role:hsOfficeBankAccount.admin ---> role:hsOfficeBankAccount.tenant
subgraph global; role:hsOfficeBankAccount.guest[bankaccount.guest]
role:global.admin --> role:debitor.owner; %% permissions
end; role:hsOfficeBankAccount.guest --> perm:hsOfficeBankAccount.view{{bankaccount.view}}
%% incoming
role:hsOfficeBankAccount.tenant ---> role:hsOfficeBankAccount.guest
end
subgraph contact
style contact fill: #e9f7ef
user:hsOfficeContact.creator([contact.creator])
role:hsOfficeContact.owner[contact.owner]
%% permissions
role:hsOfficeContact.owner --> perm:hsOfficeContact.*{{contact.*}}
%% incoming
role:global.admin --> role:hsOfficeContact.owner
user:hsOfficeContact.creator ---> role:hsOfficeContact.owner
role:hsOfficeContact.admin[contact.admin]
%% permissions
role:hsOfficeContact.admin ---> perm:hsOfficeContact.edit{{contact.edit}}
%% incoming
role:hsOfficeContact.owner ---> role:hsOfficeContact.admin
role:hsOfficeContact.tenant[contact.tenant]
%% incoming
role:hsOfficeContact.admin ----> role:hsOfficeContact.tenant
role:hsOfficeContact.guest[contact.guest]
%% permissions
role:hsOfficeContact.guest --> perm:hsOfficeContact.view{{contact.view}}
%% incoming
role:hsOfficeContact.tenant ---> role:hsOfficeContact.guest
end
subgraph partner-person
subgraph person
style person fill: #e9f7ef
user:hsOfficePerson.creator([personcreator])
role:hsOfficePerson.owner[person.owner]
%% permissions
role:hsOfficePerson.owner --> perm:hsOfficePerson.*{{person.*}}
%% incoming
user:hsOfficePerson.creator ---> role:hsOfficePerson.owner
role:global.admin --> role:hsOfficePerson.owner
role:hsOfficePerson.admin[person.admin]
%% permissions
role:hsOfficePerson.admin --> perm:hsOfficePerson.edit{{person.edit}}
%% incoming
role:hsOfficePerson.owner ---> role:hsOfficePerson.admin
role:hsOfficePerson.tenant[person.tenant]
%% incoming
role:hsOfficePerson.admin -----> role:hsOfficePerson.tenant
role:hsOfficePerson.guest[person.guest]
%% permissions
role:hsOfficePerson.guest --> perm:hsOfficePerson.edit{{person.view}}
%% incoming
role:hsOfficePerson.tenant ---> role:hsOfficePerson.guest
end
subgraph partner
role:hsOfficePartner.owner[partner.owner]
%% permissions
role:hsOfficePartner.owner --> perm:hsOfficePartner.*{{partner.*}}
%% incoming
role:global.admin ---> role:hsOfficePartner.owner
role:hsOfficePartner.admin[partner.admin]
%% permissions
role:hsOfficePartner.admin --> perm:hsOfficePartner.edit{{partner.edit}}
%% incoming
role:hsOfficePartner.owner ---> role:hsOfficePartner.admin
%% outgoing
role:hsOfficePartner.admin --> role:hsOfficePerson.tenant
role:hsOfficePartner.admin --> role:hsOfficeContact.tenant
role:hsOfficePartner.agent[partner.agent]
%% incoming
role:hsOfficePartner.admin --> role:hsOfficePartner.agent
role:hsOfficePerson.admin --> role:hsOfficePartner.agent
role:hsOfficeContact.admin --> role:hsOfficePartner.agent
role:hsOfficePartner.tenant[partner.tenant]
%% incoming
role:hsOfficePartner.agent ---> role:hsOfficePartner.tenant
%% outgoing
role:hsOfficePartner.tenant --> role:hsOfficePerson.guest
role:hsOfficePartner.tenant --> role:hsOfficeContact.guest
role:hsOfficePartner.guest[partner.guest]
%% permissions
role:hsOfficePartner.guest --> perm:hsOfficePartner.view{{partner.view}}
%% incoming
role:hsOfficePartner.tenant ---> role:hsOfficePartner.guest
end
end
subgraph debitor
style debitor stroke-width:6px
user:hsOfficeDebitor.creator([debitor.creator])
%% created by role
user:hsOfficeDebitor.creator --> role:hsOfficePartner.agent
role:hsOfficeDebitor.owner[debitor.owner]
%% permissions
role:hsOfficeDebitor.owner --> perm:hsOfficeDebitor.*{{debitor.*}}
%% incoming
user:hsOfficeDebitor.creator --> role:hsOfficeDebitor.owner
role:global.admin --> role:hsOfficeDebitor.owner
role:hsOfficeDebitor.admin[debitor.admin]
%% permissions
role:hsOfficeDebitor.admin --> perm:hsOfficeDebitor.edit{{debitor.edit}}
%% incoming
role:hsOfficeDebitor.owner ---> role:hsOfficeDebitor.admin
role:hsOfficeDebitor.agent[debitor.agent]
%% incoming
role:hsOfficeDebitor.admin ---> role:hsOfficeDebitor.agent
role:hsOfficePartner.admin --> role:hsOfficeDebitor.agent
role:hsOfficeContact.admin --> role:hsOfficeDebitor.agent
%% outgoing
role:hsOfficeDebitor.agent --> role:hsOfficeBankAccount.tenant
role:hsOfficeDebitor.tenant[debitor.tenant]
%% incoming
role:hsOfficeDebitor.agent ---> role:hsOfficeDebitor.tenant
role:hsOfficePartner.agent --> role:hsOfficeDebitor.tenant
role:hsOfficeBankAccount.admin --> role:hsOfficeDebitor.tenant
%% outgoing
role:hsOfficeDebitor.tenant --> role:hsOfficePartner.tenant
role:hsOfficeDebitor.tenant --> role:hsOfficeContact.guest
role:hsOfficeDebitor.guest[debitor.guest]
%% permissions
role:hsOfficeDebitor.guest --> perm:hsOfficeDebitor.view{{debitor.view}}
%% incoming
role:hsOfficeDebitor.tenant --> role:hsOfficeDebitor.guest
end
end
subgraph hosting
style hosting fill:#eee
subgraph package
style package fill: #e9f7ef
role:package.owner[package.owner]
--> role:package.admin[package.admin]
--> role:package.tenant[package.tenant]
role:hsOfficeDebitor.agent --> role:package.owner
role:package.admin --> role:hsOfficeDebitor.tenant
role:hsOfficePartner.tenant --> role:hsOfficeDebitor.guest
end
end
``` ```

View File

@ -47,35 +47,48 @@ begin
select * from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid into newBankAccount; select * from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid into newBankAccount;
if TG_OP = 'INSERT' then if TG_OP = 'INSERT' then
-- the owner role with full access for the global admins
ownerRole = createRole( perform createRoleWithGrants(
hsOfficeDebitorOwner(NEW), hsOfficeDebitorOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']), permissions => array['*'],
beneathRole(globalAdmin()) incomingSuperRoles => array[globalAdmin()],
userUuids => array[currentUserUuid()],
grantedByRole => globalAdmin()
); );
-- the admin role with full access for owner perform createRoleWithGrants(
adminRole = createRole(
hsOfficeDebitorAdmin(NEW), hsOfficeDebitorAdmin(NEW),
withoutPermissions(), permissions => array['edit'],
beneathRoles(array [ incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)]
hsOfficeDebitorOwner(NEW),
hsOfficePartnerAdmin(newPartner),
hsOfficePersonAdmin(newPerson),
hsOfficeContactAdmin(newContact),
hsOfficeBankAccountAdmin(newBankAccount)]),
withSubRoles(array [
hsOfficePartnerTenant(newPartner),
hsOfficePersonTenant(newPerson),
hsOfficeContactTenant(newContact),
hsOfficeBankAccountTenant(newBankAccount)])
); );
-- the tenant role for those related users who can view the data perform createRoleWithGrants(
perform createRole( hsOfficeDebitorAgent(NEW),
hsOfficeDebitorTenant, incomingSuperRoles => array[
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']), hsOfficeDebitorAdmin(NEW),
beneathRole(hsOfficeDebitorAdmin(NEW)) hsOfficePartnerAdmin(newPartner),
hsOfficeContactAdmin(newContact)],
outgoingSubRoles => array[
hsOfficeBankAccountTenant(newBankaccount)]
);
perform createRoleWithGrants(
hsOfficeDebitorTenant(NEW),
incomingSuperRoles => array[
hsOfficeDebitorAgent(NEW),
hsOfficePartnerAgent(newPartner),
hsOfficeBankAccountAdmin(newBankaccount)],
outgoingSubRoles => array[
hsOfficePartnerTenant(newPartner),
hsOfficeContactGuest(newContact),
hsOfficeBankAccountGuest(newBankaccount)]
);
perform createRoleWithGrants(
hsOfficeDebitorGuest(NEW),
permissions => array['view'],
incomingSuperRoles => array[
hsOfficeDebitorTenant(NEW)]
); );
elsif TG_OP = 'UPDATE' then elsif TG_OP = 'UPDATE' then
@ -83,33 +96,37 @@ begin
if OLD.partnerUuid <> NEW.partnerUuid then if OLD.partnerUuid <> NEW.partnerUuid then
select * from hs_office_partner as p where p.uuid = OLD.partnerUuid into oldPartner; select * from hs_office_partner as p where p.uuid = OLD.partnerUuid into oldPartner;
call revokeRoleFromRole(hsOfficeDebitorAdmin(OLD), hsOfficePartnerAdmin(oldPartner)); call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficePartnerAdmin(oldPartner));
call grantRoleToRole(hsOfficeDebitorAdmin(NEW), hsOfficePartnerAdmin(newPartner)); call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficePartnerAdmin(newPartner));
call revokeRoleFromRole(hsOfficePartnerTenant(oldPartner), hsOfficeDebitorAdmin(OLD)); call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficePartnerAgent(oldPartner));
call grantRoleToRole(hsOfficePartnerTenant(newPartner), hsOfficeDebitorAdmin(NEW)); call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficePartnerAgent(newPartner));
-- TODO: What about the person of the partner? And what if the person of the partner changes? call revokeRoleFromRole(hsOfficePartnerTenant(oldPartner), hsOfficeDebitorTenant(OLD));
call grantRoleToRole(hsOfficePartnerTenant(newPartner), hsOfficeDebitorTenant(NEW));
end if; end if;
if OLD.billingContactUuid <> NEW.billingContactUuid then if OLD.billingContactUuid <> NEW.billingContactUuid then
select * from hs_office_contact as c where c.uuid = OLD.billingContactUuid into oldContact; select * from hs_office_contact as c where c.uuid = OLD.billingContactUuid into oldContact;
call revokeRoleFromRole(hsOfficeDebitorAdmin(OLD), hsOfficeContactAdmin(oldContact)); call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficeContactAdmin(oldContact));
call grantRoleToRole(hsOfficeDebitorAdmin(NEW), hsOfficeContactAdmin(newContact)); call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficeContactAdmin(newContact));
call revokeRoleFromRole(hsOfficeContactTenant(oldContact), hsOfficeDebitorAdmin(OLD)); call revokeRoleFromRole(hsOfficeContactGuest(oldContact), hsOfficeDebitorTenant(OLD));
call grantRoleToRole(hsOfficeContactTenant(newContact), hsOfficeDebitorAdmin(NEW)); call grantRoleToRole(hsOfficeContactGuest(newContact), hsOfficeDebitorTenant(NEW));
end if; end if;
if OLD.refundBankAccountUuid <> NEW.refundBankAccountUuid then if OLD.refundBankAccountUuid <> NEW.refundBankAccountUuid then
select * from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid into oldBankAccount; select * from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid into oldBankAccount;
call revokeRoleFromRole(hsOfficeDebitorAdmin(OLD), hsOfficeBankAccountAdmin(oldBankAccount)); call revokeRoleFromRole(hsOfficeBankAccountTenant(oldBankaccount), hsOfficeDebitorAgent(OLD));
call grantRoleToRole(hsOfficeDebitorAdmin(NEW), hsOfficeBankAccountAdmin(newBankAccount)); call grantRoleToRole(hsOfficeBankAccountTenant(newBankaccount), hsOfficeDebitorAgent(NEW));
call revokeRoleFromRole(hsOfficeBankAccountTenant(oldBankAccount), hsOfficeDebitorAdmin(OLD)); call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficeBankAccountAdmin(oldBankaccount));
call grantRoleToRole(hsOfficeBankAccountTenant(newBankAccount), hsOfficeDebitorAdmin(NEW)); call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficeBankAccountAdmin(newBankaccount));
call revokeRoleFromRole(hsOfficeBankAccountGuest(oldBankaccount), hsOfficeDebitorTenant(OLD));
call grantRoleToRole(hsOfficeBankAccountGuest(newBankaccount), hsOfficeDebitorTenant(NEW));
end if; end if;
else else
raise exception 'invalid usage of TRIGGER'; raise exception 'invalid usage of TRIGGER';

View File

@ -0,0 +1,27 @@
package net.hostsharing.hsadminng;
import lombok.experimental.UtilityClass;
import javax.validation.constraints.NotNull;
import java.util.Map;
import static liquibase.repackaged.org.apache.commons.text.StringSubstitutor.replace;
import static org.apache.commons.lang3.StringUtils.stripEnd;
@UtilityClass
public class StringTemplater {
@SafeVarargs
public static String indentedMultilineTemplate(final String template, final Map.Entry<String, String>... properties) {
return stripEnd(replace(template, Map.ofEntries(properties)).indent(4), null);
}
public static Map.Entry<String, String> property(final String name, final String value) {
return Map.entry(name, value);
}
public static Map.Entry<String, String> property(final String name, @NotNull final Object value) {
return Map.entry(name, value.toString());
}
}

View File

@ -110,7 +110,8 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTest {
initialRoleNames, initialRoleNames,
"hs_office_bankaccount#sometempaccC.owner", "hs_office_bankaccount#sometempaccC.owner",
"hs_office_bankaccount#sometempaccC.admin", "hs_office_bankaccount#sometempaccC.admin",
"hs_office_bankaccount#sometempaccC.tenant" "hs_office_bankaccount#sometempaccC.tenant",
"hs_office_bankaccount#sometempaccC.guest"
)); ));
assertThat(grantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted( assertThat(grantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames, initialGrantNames,
@ -120,8 +121,10 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTest {
"{ grant role hs_office_bankaccount#sometempaccC.admin to role hs_office_bankaccount#sometempaccC.owner by system and assume }", "{ grant role hs_office_bankaccount#sometempaccC.admin to role hs_office_bankaccount#sometempaccC.owner by system and assume }",
"{ grant perm view on hs_office_bankaccount#sometempaccC to role hs_office_bankaccount#sometempaccC.tenant by system and assume }",
"{ grant role hs_office_bankaccount#sometempaccC.tenant to role hs_office_bankaccount#sometempaccC.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 view 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 }",
null null
)); ));
} }
@ -258,9 +261,9 @@ class HsOfficeBankAccountRepositoryIntegrationTest extends ContextBasedTest {
final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()); final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll());
final var givenBankAccount = givenSomeTemporaryBankAccount("selfregistered-user-drew@hostsharing.org"); final var givenBankAccount = givenSomeTemporaryBankAccount("selfregistered-user-drew@hostsharing.org");
assertThat(rawRoleRepo.findAll().size()).as("unexpected number of roles created") assertThat(rawRoleRepo.findAll().size()).as("unexpected number of roles created")
.isEqualTo(initialRoleNames.size() + 3); .isEqualTo(initialRoleNames.size() + 4);
assertThat(rawGrantRepo.findAll().size()).as("unexpected number of grants created") assertThat(rawGrantRepo.findAll().size()).as("unexpected number of grants created")
.isEqualTo(initialGrantNames.size() + 6); .isEqualTo(initialGrantNames.size() + 7);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {

View File

@ -112,7 +112,8 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTest {
initialRoleNames, initialRoleNames,
"hs_office_contact#anothernewcontact.owner", "hs_office_contact#anothernewcontact.owner",
"hs_office_contact#anothernewcontact.admin", "hs_office_contact#anothernewcontact.admin",
"hs_office_contact#anothernewcontact.tenant" "hs_office_contact#anothernewcontact.tenant",
"hs_office_contact#anothernewcontact.guest"
)); ));
assertThat(grantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.from( assertThat(grantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialGrantNames, initialGrantNames,
@ -121,7 +122,8 @@ class HsOfficeContactRepositoryIntegrationTest extends ContextBasedTest {
"{ grant role hs_office_contact#anothernewcontact.tenant 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 * on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.owner by system and assume }", "{ grant perm * 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.admin to role hs_office_contact#anothernewcontact.owner by system and assume }",
"{ grant perm view on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.tenant by system and assume }", "{ grant perm view on hs_office_contact#anothernewcontact to role hs_office_contact#anothernewcontact.guest by system and assume }",
"{ grant role hs_office_contact#anothernewcontact.guest to role hs_office_contact#anothernewcontact.tenant by system and assume }",
"{ grant role hs_office_contact#anothernewcontact.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }" "{ grant role hs_office_contact#anothernewcontact.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }"
)); ));
} }

View File

@ -99,7 +99,13 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll()); final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()); final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()).stream()
.map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex"))
.map(s -> s.replace("20002Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("forthcontact", "4th"))
.map(s -> s.replace("hs_office_", ""))
.toList();
// when // when
attempt(em, () -> { attempt(em, () -> {
@ -117,26 +123,44 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
// then // then
assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames, initialRoleNames,
"hs_office_debitor#20002Fourthe.G.-forthcontact.admin",
"hs_office_debitor#20002Fourthe.G.-forthcontact.owner", "hs_office_debitor#20002Fourthe.G.-forthcontact.owner",
"hs_office_debitor#20002Fourthe.G.-forthcontact.tenant")); "hs_office_debitor#20002Fourthe.G.-forthcontact.admin",
assertThat(grantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.fromFormatted( "hs_office_debitor#20002Fourthe.G.-forthcontact.agent",
initialGrantNames, "hs_office_debitor#20002Fourthe.G.-forthcontact.tenant",
"{ grant perm * on hs_office_debitor#20002Fourthe.G.-forthcontact to role hs_office_debitor#20002Fourthe.G.-forthcontact.owner by system and assume }", "hs_office_debitor#20002Fourthe.G.-forthcontact.guest"));
"{ grant role hs_office_debitor#20002Fourthe.G.-forthcontact.owner to role global#global.admin by system and assume }", assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
.map(s -> s.replace("superuser-alex@hostsharing.net", "superuser-alex"))
.map(s -> s.replace("20002Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("Fourthe.G.-forthcontact", "FeG"))
.map(s -> s.replace("forthcontact", "4th"))
.map(s -> s.replace("hs_office_", ""))
.containsExactlyInAnyOrder(Array.fromFormatted(
initialGrantNames,
// owner
"{ grant perm * on debitor#FeG to role debitor#FeG.owner by system and assume }",
"{ grant role debitor#FeG.owner to role global#global.admin by system and assume }",
"{ grant role debitor#FeG.owner to user superuser-alex by global#global.admin and assume }",
"{ grant role hs_office_debitor#20002Fourthe.G.-forthcontact.admin to role hs_office_debitor#20002Fourthe.G.-forthcontact.owner by system and assume }", // admin
"{ grant role hs_office_debitor#20002Fourthe.G.-forthcontact.admin to role hs_office_partner#Fourthe.G.-forthcontact.admin by system and assume }", "{ grant perm edit on debitor#FeG to role debitor#FeG.admin by system and assume }",
"{ grant role hs_office_debitor#20002Fourthe.G.-forthcontact.admin to role hs_office_person#Fourthe.G..admin by system and assume }", "{ grant role debitor#FeG.admin to role debitor#FeG.owner by system and assume }",
"{ grant role hs_office_debitor#20002Fourthe.G.-forthcontact.admin to role hs_office_contact#forthcontact.admin by system and assume }",
"{ grant role hs_office_contact#forthcontact.tenant to role hs_office_debitor#20002Fourthe.G.-forthcontact.admin by system and assume }",
"{ grant role hs_office_partner#Fourthe.G.-forthcontact.tenant to role hs_office_debitor#20002Fourthe.G.-forthcontact.admin by system and assume }",
"{ grant role hs_office_person#Fourthe.G..tenant to role hs_office_debitor#20002Fourthe.G.-forthcontact.admin by system and assume }",
"{ grant role hs_office_debitor#20002Fourthe.G.-forthcontact.tenant to role hs_office_debitor#20002Fourthe.G.-forthcontact.admin by system and assume }",
"{ grant perm view on hs_office_debitor#20002Fourthe.G.-forthcontact to role hs_office_debitor#20002Fourthe.G.-forthcontact.tenant by system and assume }", // agent
"{ grant role debitor#FeG.agent to role debitor#FeG.admin by system and assume }",
"{ grant role debitor#FeG.agent to role contact#4th.admin by system and assume }",
"{ grant role debitor#FeG.agent to role partner#FeG.admin by system and assume }",
null)); // tenant
"{ grant role contact#4th.guest to role debitor#FeG.tenant by system and assume }",
"{ grant role debitor#FeG.tenant to role debitor#FeG.agent by system and assume }",
"{ grant role debitor#FeG.tenant to role partner#FeG.agent by system and assume }",
"{ grant role partner#FeG.tenant to role debitor#FeG.tenant by system and assume }",
// guest
"{ grant perm view on debitor#FeG to role debitor#FeG.guest by system and assume }",
"{ grant role debitor#FeG.guest to role debitor#FeG.tenant by system and assume }",
null));
} }
private void assertThatDebitorIsPersisted(final HsOfficeDebitorEntity saved) { private void assertThatDebitorIsPersisted(final HsOfficeDebitorEntity saved) {
@ -247,6 +271,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
givenDebitor.setBillingContact(rawReference(givenNewContact)); givenDebitor.setBillingContact(rawReference(givenNewContact));
// TODO.test: also test update of partner+bankAccount
// givenDebitor.setPartner(rawReference(givenNewPartner));
// givenDebitor.setRefundBankAccount(rawReference(givenNewBankAccount));
givenDebitor.setVatId(givenNewVatId); givenDebitor.setVatId(givenNewVatId);
givenDebitor.setVatCountryCode(givenNewVatCountryCode); givenDebitor.setVatCountryCode(givenNewVatCountryCode);
givenDebitor.setVatBusiness(givenNewVatBusiness); givenDebitor.setVatBusiness(givenNewVatBusiness);
@ -390,9 +417,9 @@ class HsOfficeDebitorRepositoryIntegrationTest extends ContextBasedTest {
final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll())); final var initialGrantNames = Array.from(grantDisplaysOf(rawGrantRepo.findAll()));
final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "twelfth"); final var givenDebitor = givenSomeTemporaryDebitor("Fourth", "twelfth");
assertThat(rawRoleRepo.findAll().size()).as("precondition failed: unexpected number of roles created") assertThat(rawRoleRepo.findAll().size()).as("precondition failed: unexpected number of roles created")
.isEqualTo(initialRoleNames.length + 3); .isEqualTo(initialRoleNames.length + 5);
assertThat(rawGrantRepo.findAll().size()).as("precondition failed: unexpected number of grants created") assertThat(rawGrantRepo.findAll().size()).as("precondition failed: unexpected number of grants created")
.isEqualTo(initialGrantNames.length + 11); .isEqualTo(initialGrantNames.length + 14);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {

View File

@ -96,7 +96,11 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
// given // given
context("superuser-alex@hostsharing.net"); context("superuser-alex@hostsharing.net");
final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll()); final var initialRoleNames = roleNamesOf(rawRoleRepo.findAll());
final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()); final var initialGrantNames = grantDisplaysOf(rawGrantRepo.findAll()).stream()
.map(s -> s.replace("ErbenBesslerMelBessler", "EBess"))
.map(s -> s.replace("forthcontact", "4th"))
.map(s -> s.replace("hs_office_", ""))
.toList();
// when // when
attempt(em, () -> { attempt(em, () -> {
@ -114,20 +118,40 @@ class HsOfficePartnerRepositoryIntegrationTest extends ContextBasedTest {
assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from( assertThat(roleNamesOf(rawRoleRepo.findAll())).containsExactlyInAnyOrder(Array.from(
initialRoleNames, initialRoleNames,
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.admin", "hs_office_partner#ErbenBesslerMelBessler-forthcontact.admin",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.agent",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.owner", "hs_office_partner#ErbenBesslerMelBessler-forthcontact.owner",
"hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant")); "hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant",
assertThat(grantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(Array.from( "hs_office_partner#ErbenBesslerMelBessler-forthcontact.guest"));
initialGrantNames, assertThat(grantDisplaysOf(rawGrantRepo.findAll()))
"{ grant role hs_office_partner#ErbenBesslerMelBessler-forthcontact.owner to role global#global.admin by system and assume }", .map(s -> s.replace("ErbenBesslerMelBessler", "EBess"))
"{ grant role hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant to role hs_office_contact#forthcontact.admin by system and assume }", .map(s -> s.replace("forthcontact", "4th"))
"{ grant perm edit on hs_office_partner#ErbenBesslerMelBessler-forthcontact to role hs_office_partner#ErbenBesslerMelBessler-forthcontact.admin by system and assume }", .map(s -> s.replace("hs_office_", ""))
"{ grant role hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant to role hs_office_partner#ErbenBesslerMelBessler-forthcontact.admin by system and assume }", .containsExactlyInAnyOrder(Array.fromFormatted(
"{ grant perm * on hs_office_partner#ErbenBesslerMelBessler-forthcontact to role hs_office_partner#ErbenBesslerMelBessler-forthcontact.owner by system and assume }", initialGrantNames,
"{ grant role hs_office_partner#ErbenBesslerMelBessler-forthcontact.admin to role hs_office_partner#ErbenBesslerMelBessler-forthcontact.owner by system and assume }", // owner
"{ grant perm view on hs_office_partner#ErbenBesslerMelBessler-forthcontact to role hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant by system and assume }", "{ grant perm * on partner#EBess-4th to role partner#EBess-4th.owner by system and assume }",
"{ grant role hs_office_contact#forthcontact.tenant to role hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant by system and assume }", "{ grant role partner#EBess-4th.owner to role global#global.admin by system and assume }",
"{ grant role hs_office_person#ErbenBesslerMelBessler.tenant to role hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant by system and assume }",
"{ grant role hs_office_partner#ErbenBesslerMelBessler-forthcontact.tenant to role hs_office_person#ErbenBesslerMelBessler.admin by system and assume }")); // admin
"{ grant perm edit on partner#EBess-4th to role partner#EBess-4th.admin by system and assume }",
"{ grant role partner#EBess-4th.admin to role partner#EBess-4th.owner by system and assume }",
"{ grant role person#EBess.tenant to role partner#EBess-4th.admin by system and assume }",
"{ grant role contact#4th.tenant to role partner#EBess-4th.admin by system and assume }",
// agent
"{ grant role partner#EBess-4th.agent to role partner#EBess-4th.admin by system and assume }",
"{ grant role partner#EBess-4th.agent to role person#EBess.admin by system and assume }",
"{ grant role partner#EBess-4th.agent to role contact#4th.admin by system and assume }",
// tenant
"{ grant role partner#EBess-4th.tenant to role partner#EBess-4th.agent by system and assume }",
"{ grant role person#EBess.guest to role partner#EBess-4th.tenant by system and assume }",
"{ grant role contact#4th.guest to role partner#EBess-4th.tenant by system and assume }",
// guest
"{ grant perm view on partner#EBess-4th to role partner#EBess-4th.guest by system and assume }",
"{ grant role partner#EBess-4th.guest to role partner#EBess-4th.tenant by system and assume }",
null));
} }
private void assertThatPartnerIsPersisted(final HsOfficePartnerEntity saved) { private void assertThatPartnerIsPersisted(final HsOfficePartnerEntity saved) {

View File

@ -110,7 +110,8 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTest {
initialRoleNames, initialRoleNames,
"hs_office_person#anothernewperson.owner", "hs_office_person#anothernewperson.owner",
"hs_office_person#anothernewperson.admin", "hs_office_person#anothernewperson.admin",
"hs_office_person#anothernewperson.tenant" "hs_office_person#anothernewperson.tenant",
"hs_office_person#anothernewperson.guest"
)); ));
assertThat(grantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder( assertThat(grantDisplaysOf(rawGrantRepo.findAll())).containsExactlyInAnyOrder(
Array.from( Array.from(
@ -120,7 +121,8 @@ class HsOfficePersonRepositoryIntegrationTest extends ContextBasedTest {
"{ grant role hs_office_person#anothernewperson.tenant to role hs_office_person#anothernewperson.admin by system and assume }", "{ grant role hs_office_person#anothernewperson.tenant to role hs_office_person#anothernewperson.admin by system and assume }",
"{ grant perm * on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.owner by system and assume }", "{ grant perm * on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.owner by system and assume }",
"{ grant role hs_office_person#anothernewperson.admin to role hs_office_person#anothernewperson.owner by system and assume }", "{ grant role hs_office_person#anothernewperson.admin to role hs_office_person#anothernewperson.owner by system and assume }",
"{ grant perm view on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.tenant by system and assume }", "{ grant perm view on hs_office_person#anothernewperson to role hs_office_person#anothernewperson.guest by system and assume }",
"{ grant role hs_office_person#anothernewperson.guest to role hs_office_person#anothernewperson.tenant by system and assume }",
"{ grant role hs_office_person#anothernewperson.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }" "{ grant role hs_office_person#anothernewperson.owner to user selfregistered-user-drew@hostsharing.org by global#global.admin and assume }"
)); ));
} }

View File

@ -135,6 +135,7 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest {
"{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant to role hs_office_contact#forthcontact.admin by system and assume }", "{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant to role hs_office_contact#forthcontact.admin by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }", "{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant to role hs_office_person#BesslerAnita.admin by system and assume }",
"{ grant role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.admin by system and assume }",
"{ grant role hs_office_contact#forthcontact.tenant to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant by system and assume }", "{ grant role hs_office_contact#forthcontact.tenant to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant by system and assume }",
"{ grant role hs_office_person#BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant by system and assume }", "{ grant role hs_office_person#BesslerAnita.tenant to role hs_office_relationship#BesslerAnita-with-JOINT_AGENT-BesslerAnita.tenant by system and assume }",
null) null)
@ -353,7 +354,7 @@ class HsOfficeRelationshipRepositoryIntegrationTest extends ContextBasedTest {
assertThat(rawRoleRepo.findAll().size()).as("unexpected number of roles created") assertThat(rawRoleRepo.findAll().size()).as("unexpected number of roles created")
.isEqualTo(initialRoleNames.length + 3); .isEqualTo(initialRoleNames.length + 3);
assertThat(rawGrantRepo.findAll().size()).as("unexpected number of grants created") assertThat(rawGrantRepo.findAll().size()).as("unexpected number of grants created")
.isEqualTo(initialGrantNames.length + 12); .isEqualTo(initialGrantNames.length + 13);
// when // when
final var result = jpaAttempt.transacted(() -> { final var result = jpaAttempt.transacted(() -> {