introduces agent+guest role for role-system around debitor+partner
This commit is contained in:
@ -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
|
||||
(
|
||||
|
@ -2,47 +2,19 @@
|
||||
|
||||
-- ============================================================================
|
||||
-- PERMISSIONS
|
||||
--changeset rbac-role-builder-permissions:1 endDelimiter:--//
|
||||
--changeset rbac-role-builder-to-uuids:1 endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
create type RbacPermissions as
|
||||
(
|
||||
permissionUuids uuid[]
|
||||
);
|
||||
|
||||
create or replace function grantingPermissions(forObjectUuid uuid, permitOps RbacOp[])
|
||||
returns RbacPermissions
|
||||
create or replace function toPermissionUuids(forObjectUuid uuid, permitOps RbacOp[])
|
||||
returns uuid[]
|
||||
language plpgsql
|
||||
strict as $$
|
||||
begin
|
||||
return row (createPermissions(forObjectUuid, permitOps))::RbacPermissions;
|
||||
return createPermissions(forObjectUuid, permitOps);
|
||||
end; $$;
|
||||
|
||||
create or replace function withoutPermissions()
|
||||
returns RbacPermissions
|
||||
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
|
||||
create or replace function toRoleUuids(roleDescriptors RbacRoleDescriptor[])
|
||||
returns uuid[]
|
||||
language plpgsql
|
||||
strict as $$
|
||||
declare
|
||||
@ -56,145 +28,22 @@ begin
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
return row (superRoleUuids)::RbacSuperRoles;
|
||||
return superRoleUuids;
|
||||
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
|
||||
--changeset rbac-role-builder-create-role:1 endDelimiter:--//
|
||||
-- -----------------------------------------------------------------
|
||||
|
||||
/*
|
||||
*/
|
||||
create or replace function createRole(
|
||||
create or replace function createRoleWithGrants(
|
||||
roleDescriptor RbacRoleDescriptor,
|
||||
permissions RbacPermissions,
|
||||
superRoles RbacSuperRoles,
|
||||
subRoles RbacSubRoles = null,
|
||||
users RbacUsers = null,
|
||||
grantingRoleUuid uuid = null
|
||||
permissions RbacOp[] = array[]::RbacOp[],
|
||||
incomingSuperRoles RbacRoleDescriptor[] = array[]::RbacRoleDescriptor[],
|
||||
outgoingSubRoles RbacRoleDescriptor[] = array[]::RbacRoleDescriptor[],
|
||||
userUuids uuid[] = array[]::uuid[],
|
||||
grantedByRole RbacRoleDescriptor = null
|
||||
)
|
||||
returns uuid
|
||||
called on null input
|
||||
@ -204,80 +53,37 @@ declare
|
||||
superRoleUuid uuid;
|
||||
subRoleUuid uuid;
|
||||
userUuid uuid;
|
||||
grantedByRoleUuid uuid;
|
||||
begin
|
||||
raise notice 'will createRole for %', roleDescriptor;
|
||||
roleUuid = createRole(roleDescriptor);
|
||||
roleUuid := createRole(roleDescriptor);
|
||||
|
||||
call grantPermissionsToRole(roleUuid, permissions.permissionUuids);
|
||||
|
||||
if superRoles is not null then
|
||||
foreach superRoleUuid in array superRoles.roleuUids
|
||||
loop
|
||||
call grantRoleToRole(roleUuid, superRoleUuid);
|
||||
end loop;
|
||||
if cardinality(permissions) >0 then
|
||||
call grantPermissionsToRole(roleUuid, toPermissionUuids(roleDescriptor.objectuuid, permissions));
|
||||
end if;
|
||||
|
||||
if subRoles is not null then
|
||||
foreach subRoleUuid in array subRoles.roleuUids
|
||||
loop
|
||||
call grantRoleToRole(subRoleUuid, roleUuid);
|
||||
end loop;
|
||||
end if;
|
||||
foreach superRoleUuid in array toRoleUuids(incomingSuperRoles)
|
||||
loop
|
||||
call grantRoleToRole(roleUuid, superRoleUuid);
|
||||
end loop;
|
||||
|
||||
if users is not null then
|
||||
foreach userUuid in array users.useruUids
|
||||
foreach subRoleUuid in array toRoleUuids(outgoingSubRoles)
|
||||
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
|
||||
call grantRoleToUserUnchecked(grantingRoleUuid, roleUuid, userUuid);
|
||||
call grantRoleToUserUnchecked(grantedByRoleUuid, roleUuid, userUuid);
|
||||
end loop;
|
||||
end if;
|
||||
|
||||
return roleUuid;
|
||||
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; $$;
|
||||
--//
|
||||
|
||||
|
||||
|
@ -58,6 +58,14 @@ begin
|
||||
return roleDescriptor('%2$s', entity.uuid, 'admin');
|
||||
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)
|
||||
returns RbacRoleDescriptor
|
||||
language plpgsql
|
||||
@ -66,6 +74,14 @@ begin
|
||||
return roleDescriptor('%2$s', entity.uuid, 'tenant');
|
||||
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);
|
||||
execute sql;
|
||||
end; $$;
|
||||
|
@ -35,28 +35,28 @@ begin
|
||||
end if;
|
||||
|
||||
-- the owner role with full access for Hostsharing administrators
|
||||
testCustomerOwnerUuid = createRole(
|
||||
testCustomerOwnerUuid = createRoleWithGrants(
|
||||
testCustomerOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRole(globalAdmin())
|
||||
permissions => array['*'],
|
||||
incomingSuperRoles => array[globalAdmin()]
|
||||
);
|
||||
|
||||
-- the admin role for the customer's admins, who can view and add products
|
||||
customerAdminUuid = createRole(
|
||||
customerAdminUuid = createRoleWithGrants(
|
||||
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
|
||||
withUser(NEW.adminUserName, 'create'), -- implicitly ignored if null
|
||||
grantedByRole(globalAdmin())
|
||||
userUuids => array[getRbacUserId(NEW.adminUserName, 'create')], -- implicitly ignored if null
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
|
||||
-- allow the customer owner role (thus administrators) to assume the customer admin role
|
||||
call grantRoleToRole(customerAdminUuid, testCustomerOwnerUuid, false);
|
||||
|
||||
-- the tenant role which later can be used by owners+admins of sub-objects
|
||||
perform createRole(
|
||||
perform createRoleWithGrants(
|
||||
testCustomerTenant(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view'])
|
||||
permissions => array['view']
|
||||
);
|
||||
|
||||
return NEW;
|
||||
|
@ -36,25 +36,25 @@ begin
|
||||
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
|
||||
packageOwnerRoleUuid = createRole(
|
||||
testPackageOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRole(testCustomerAdmin(parentCustomer))
|
||||
perform createRoleWithGrants(
|
||||
testPackageOwner(NEW),
|
||||
permissions => array ['*'],
|
||||
incomingSuperRoles => array[testCustomerAdmin(parentCustomer)]
|
||||
);
|
||||
|
||||
-- an owner role is created and assigned to the package owner role
|
||||
packageAdminRoleUuid = createRole(
|
||||
testPackageAdmin(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['add-domain']),
|
||||
beneathRole(packageOwnerRoleUuid)
|
||||
perform createRoleWithGrants(
|
||||
testPackageAdmin(NEW),
|
||||
permissions => array ['add-domain'],
|
||||
incomingSuperRoles => array[testPackageOwner(NEW)]
|
||||
);
|
||||
|
||||
-- and a package tenant role is created and assigned to the package admin as well
|
||||
perform createRole(
|
||||
testPackageTenant(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
|
||||
beneathRole(packageAdminRoleUuid),
|
||||
beingItselfA(testCustomerTenant(parentCustomer))
|
||||
perform createRoleWithGrants(
|
||||
testPackageTenant(NEW),
|
||||
permissions => array['view'],
|
||||
incomingsuperroles => array[testPackageAdmin(NEW)],
|
||||
outgoingSubRoles => array[testCustomerTenant(parentCustomer)]
|
||||
);
|
||||
|
||||
return NEW;
|
||||
|
@ -26,10 +26,10 @@ begin
|
||||
return domainTenantRoleUuid;
|
||||
end if;
|
||||
|
||||
return createRole(
|
||||
return createRoleWithGrants(
|
||||
domainTenantRoleDesc,
|
||||
grantingPermissions(forObjectUuid => domain.uuid, permitOps => array ['view']),
|
||||
beneathRole(testdomainAdmin(domain))
|
||||
permissions => array['view'],
|
||||
incomingSuperRoles => array[testdomainAdmin(domain)]
|
||||
);
|
||||
end; $$;
|
||||
--//
|
||||
@ -48,8 +48,6 @@ create or replace function createRbacRulesForTestDomain()
|
||||
strict as $$
|
||||
declare
|
||||
parentPackage test_package;
|
||||
domainOwnerRoleId uuid;
|
||||
domainAdminRoleId uuid;
|
||||
begin
|
||||
if TG_OP <> 'INSERT' then
|
||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||
@ -58,18 +56,18 @@ begin
|
||||
select * from test_package where uuid = NEW.packageUuid into parentPackage;
|
||||
|
||||
-- an owner role is created and assigned to the package's admin group
|
||||
domainOwnerRoleId = createRole(
|
||||
testdomainOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRole(testPackageAdmin(parentPackage))
|
||||
perform createRoleWithGrants(
|
||||
testDomainOwner(NEW),
|
||||
permissions => array['*'],
|
||||
incomingSuperRoles => array[testPackageAdmin(parentPackage)]
|
||||
);
|
||||
|
||||
-- and a domain admin role is created and assigned to the domain owner as well
|
||||
domainAdminRoleId = createRole(
|
||||
testdomainAdmin(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
|
||||
beneathRole(domainOwnerRoleId),
|
||||
beingItselfA(testPackageTenant(parentPackage))
|
||||
perform createRoleWithGrants(
|
||||
testDomainAdmin(NEW),
|
||||
permissions => array['edit'],
|
||||
incomingSuperRoles => array[testDomainOwner(NEW)],
|
||||
outgoingSubRoles => array[testPackageTenant(parentPackage)]
|
||||
);
|
||||
|
||||
-- a tenent role is only created on demand
|
||||
|
@ -34,28 +34,29 @@ begin
|
||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||
end if;
|
||||
|
||||
-- the owner role with full access for the creator assigned to the current user
|
||||
ownerRole := createRole(
|
||||
hsOfficeContactOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRole(globalAdmin()),
|
||||
withoutSubRoles(),
|
||||
withUser(currentUser()), -- TODO.spec: Who is owner of a new contact?
|
||||
grantedByRole(globalAdmin())
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactOwner(NEW),
|
||||
permissions => array['*'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()],
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
adminRole := createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactAdmin(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
|
||||
beneathRole(ownerRole)
|
||||
permissions => array['edit'],
|
||||
incomingSuperRoles => array[hsOfficeContactOwner(NEW)]
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
perform createRole(
|
||||
hsOfficeContactTenant(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
|
||||
beneathRole(adminRole)
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactTenant(NEW),
|
||||
incomingSuperRoles => array[hsOfficeContactAdmin(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeContactGuest(NEW),
|
||||
permissions => array['view'],
|
||||
incomingSuperRoles => array[hsOfficeContactTenant(NEW)]
|
||||
);
|
||||
|
||||
return NEW;
|
||||
|
@ -24,36 +24,34 @@ create or replace function createRbacRolesForHsOfficePerson()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
declare
|
||||
ownerRole uuid;
|
||||
adminRole uuid;
|
||||
begin
|
||||
if TG_OP <> 'INSERT' then
|
||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||
end if;
|
||||
|
||||
-- the owner role with full access for the creator assigned to the current user
|
||||
ownerRole := createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRole(globalAdmin()),
|
||||
withoutSubRoles(),
|
||||
withUser(currentUser()), -- TODO.spec: Who is owner of a new person?
|
||||
grantedByRole(globalAdmin())
|
||||
permissions => array['*'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()],
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
adminRole := createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonAdmin(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
|
||||
beneathRole(ownerRole)
|
||||
permissions => array['edit'],
|
||||
incomingSuperRoles => array[hsOfficePersonOwner(NEW)]
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
perform createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonTenant(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
|
||||
beneathRole(adminRole)
|
||||
incomingSuperRoles => array[hsOfficePersonAdmin(NEW)]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePersonGuest(NEW),
|
||||
permissions => array['view'],
|
||||
incomingSuperRoles => array[hsOfficePersonTenant(NEW)]
|
||||
);
|
||||
|
||||
return NEW;
|
||||
|
@ -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
|
||||
```
|
@ -28,46 +28,56 @@ create or replace function hsOfficePartnerRbacRolesTrigger()
|
||||
strict as $$
|
||||
declare
|
||||
hsOfficePartnerTenant RbacRoleDescriptor;
|
||||
ownerRole uuid;
|
||||
adminRole uuid;
|
||||
oldPerson hs_office_person;
|
||||
newPerson hs_office_person;
|
||||
oldContact hs_office_contact;
|
||||
newContact hs_office_contact;
|
||||
begin
|
||||
|
||||
hsOfficePartnerTenant := hsOfficePartnerTenant(NEW);
|
||||
|
||||
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;
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
|
||||
-- the owner role with full access for the global admins
|
||||
ownerRole = createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRole(globalAdmin())
|
||||
permissions => array['*'],
|
||||
incomingSuperRoles => array[globalAdmin()]
|
||||
);
|
||||
|
||||
-- the admin role with full access for owner
|
||||
adminRole = createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerAdmin(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
|
||||
beneathRole(ownerRole)
|
||||
permissions => array['edit'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePartnerOwner(NEW)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficePersonTenant(newPerson),
|
||||
hsOfficeContactTenant(newContact)]
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
perform createRole(
|
||||
hsOfficePartnerTenant,
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
|
||||
beneathRoles(array[
|
||||
perform createRoleWithGrants(
|
||||
hsOfficePartnerAgent(NEW),
|
||||
incomingSuperRoles => array[
|
||||
hsOfficePartnerAdmin(NEW),
|
||||
hsOfficePersonAdmin(newPerson),
|
||||
hsOfficeContactAdmin(newContact)]),
|
||||
withSubRoles(array[
|
||||
hsOfficePersonTenant(newPerson),
|
||||
hsOfficeContactTenant(newContact)])
|
||||
hsOfficeContactAdmin(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
|
||||
@ -75,21 +85,27 @@ begin
|
||||
if OLD.personUuid <> NEW.personUuid then
|
||||
select * from hs_office_person as p where p.uuid = OLD.personUuid into oldPerson;
|
||||
|
||||
call revokeRoleFromRole( hsOfficePartnerTenant, hsOfficePersonAdmin(oldPerson) );
|
||||
call grantRoleToRole( hsOfficePartnerTenant, hsOfficePersonAdmin(newPerson) );
|
||||
|
||||
call revokeRoleFromRole( hsOfficePersonTenant(oldPerson), hsOfficePartnerTenant );
|
||||
call grantRoleToRole( hsOfficePersonTenant(newPerson), hsOfficePartnerTenant );
|
||||
call revokeRoleFromRole(hsOfficePersonTenant(oldPerson), hsOfficePartnerAdmin(OLD));
|
||||
call grantRoleToRole(hsOfficePersonTenant(newPerson), hsOfficePartnerAdmin(NEW));
|
||||
|
||||
call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficePersonAdmin(oldPerson));
|
||||
call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficePersonAdmin(newPerson));
|
||||
|
||||
call revokeRoleFromRole(hsOfficePersonGuest(oldPerson), hsOfficePartnerTenant(OLD));
|
||||
call grantRoleToRole(hsOfficePersonGuest(newPerson), hsOfficePartnerTenant(NEW));
|
||||
end if;
|
||||
|
||||
if OLD.contactUuid <> NEW.contactUuid then
|
||||
select * from hs_office_contact as c where c.uuid = OLD.contactUuid into oldContact;
|
||||
|
||||
call revokeRoleFromRole( hsOfficePartnerTenant, hsOfficeContactAdmin(oldContact) );
|
||||
call grantRoleToRole( hsOfficePartnerTenant, hsOfficeContactAdmin(newContact) );
|
||||
call revokeRoleFromRole(hsOfficeContactTenant(oldContact), hsOfficePartnerAdmin(OLD));
|
||||
call grantRoleToRole(hsOfficeContactTenant(newContact), hsOfficePartnerAdmin(NEW));
|
||||
|
||||
call revokeRoleFromRole( hsOfficeContactTenant(oldContact), hsOfficePartnerTenant );
|
||||
call grantRoleToRole( hsOfficeContactTenant(newContact), hsOfficePartnerTenant );
|
||||
call revokeRoleFromRole(hsOfficePartnerAgent(OLD), hsOfficeContactAdmin(oldContact));
|
||||
call grantRoleToRole(hsOfficePartnerAgent(NEW), hsOfficeContactAdmin(newContact));
|
||||
|
||||
call revokeRoleFromRole(hsOfficeContactGuest(oldContact), hsOfficePartnerTenant(OLD));
|
||||
call grantRoleToRole(hsOfficeContactGuest(newContact), hsOfficePartnerTenant(NEW));
|
||||
end if;
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
|
@ -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();
|
||||
--//
|
||||
|
@ -28,8 +28,6 @@ create or replace function hsOfficeRelationshipRbacRolesTrigger()
|
||||
strict as $$
|
||||
declare
|
||||
hsOfficeRelationshipTenant RbacRoleDescriptor;
|
||||
ownerRole uuid;
|
||||
adminRole uuid;
|
||||
newRelAnchor hs_office_person;
|
||||
newRelHolder hs_office_person;
|
||||
oldContact hs_office_contact;
|
||||
@ -44,38 +42,38 @@ begin
|
||||
|
||||
if TG_OP = 'INSERT' then
|
||||
|
||||
-- the owner role with full access for admins of the relAnchor global admins
|
||||
ownerRole = createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationshipOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRoles(array[
|
||||
permissions => array['*'],
|
||||
incomingSuperRoles => array[
|
||||
globalAdmin(),
|
||||
hsOfficePersonAdmin(newRelAnchor)])
|
||||
hsOfficePersonAdmin(newRelAnchor)]
|
||||
);
|
||||
|
||||
-- the admin role with full access for the owner
|
||||
adminRole = createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationshipAdmin(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
|
||||
beneathRole(ownerRole)
|
||||
permissions => array['edit'],
|
||||
incomingSuperRoles => array[hsOfficeRelationshipOwner(NEW)]
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
perform createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeRelationshipTenant,
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
|
||||
beneathRoles(array[
|
||||
permissions => array['view'],
|
||||
incomingSuperRoles => array[
|
||||
hsOfficeRelationshipAdmin(NEW),
|
||||
hsOfficePersonAdmin(newRelAnchor),
|
||||
hsOfficePersonAdmin(newRelHolder),
|
||||
hsOfficeContactAdmin(newContact)]),
|
||||
withSubRoles(array[
|
||||
hsOfficeContactAdmin(newContact)],
|
||||
outgoingSubRoles => array[
|
||||
hsOfficePersonTenant(newRelAnchor),
|
||||
hsOfficePersonTenant(newRelHolder),
|
||||
hsOfficeContactTenant(newContact)])
|
||||
hsOfficeContactTenant(newContact)]
|
||||
);
|
||||
|
||||
-- anchor and holder admin roles need each others tenant role
|
||||
-- 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(newRelHolder), hsOfficePersonAdmin(newRelAnchor));
|
||||
call grantRoleToRoleIfNotNull(hsOfficePersonTenant(newRelHolder), hsOfficeContactAdmin(newContact));
|
||||
|
@ -3,41 +3,38 @@
|
||||
```mermaid
|
||||
flowchart TB
|
||||
|
||||
%% ---------- generated start: ----------
|
||||
|
||||
subgraph global
|
||||
style hsOfficeBankAccount fill: #e9f7ef
|
||||
|
||||
role:global.admin[global.admin]
|
||||
end
|
||||
|
||||
subgraph context
|
||||
user:current([current])
|
||||
subgraph hsOfficeBankAccount
|
||||
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
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
|
@ -26,40 +26,33 @@ create or replace function createRbacRolesForHsOfficeBankAccount()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
strict as $$
|
||||
declare
|
||||
ownerRole uuid;
|
||||
adminRole uuid;
|
||||
begin
|
||||
if TG_OP <> 'INSERT' then
|
||||
raise exception 'invalid usage of TRIGGER AFTER INSERT';
|
||||
end if;
|
||||
|
||||
-- the owner role with full access for the creator assigned to the current user
|
||||
ownerRole := createRole(
|
||||
hsOfficeBankAccountOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['delete']),
|
||||
beneathRole(globalAdmin()),
|
||||
withoutSubRoles(),
|
||||
withUser(currentUser()), -- TODO.spec: Who is owner of a new bankaccount?
|
||||
grantedByRole(globalAdmin())
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountOwner(NEW),
|
||||
permissions => array['delete'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()],
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
|
||||
-- the admin role for those related users who can view the data and related records
|
||||
adminRole := createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountAdmin(NEW),
|
||||
-- Where bankaccounts can be created, assigned, re-assigned and deleted, they cannot be updated.
|
||||
-- Thus SQL UPDATE and 'edit' permission are being implemented.
|
||||
withoutPermissions(),
|
||||
beneathRole(ownerRole)
|
||||
incomingSuperRoles => array[hsOfficeBankAccountOwner(NEW)]
|
||||
);
|
||||
|
||||
-- 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 createRole(
|
||||
hsOfficeBankAccountTenant(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
|
||||
beneathRole(adminRole)
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeBankAccountGuest(NEW),
|
||||
permissions => array['view'],
|
||||
incomingSuperRoles => array[hsOfficeBankAccountTenant(NEW)]
|
||||
);
|
||||
|
||||
return NEW;
|
||||
|
@ -1,49 +1,208 @@
|
||||
### hs_office_debitor RBAC Roles
|
||||
|
||||
```mermaid
|
||||
flowchart TB;
|
||||
flowchart TB
|
||||
|
||||
subgraph bankaccount;
|
||||
subgraph global
|
||||
style global fill:#eee
|
||||
|
||||
role:global.admin[global.admin]
|
||||
end
|
||||
|
||||
%% oversimplified version for now
|
||||
%%
|
||||
%% 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}};
|
||||
end;
|
||||
|
||||
subgraph debitor[" "];
|
||||
direction TB;
|
||||
|
||||
role:debitor.owner[[debitor.owner]]
|
||||
role:debitor.owner --> perm:debitor.*{{debitor.*}};
|
||||
|
||||
role:debitor.admin[[debitor.admin]]
|
||||
%% super-roles
|
||||
role:debitor.owner --> role:debitor.admin;
|
||||
role:partner.admin --> role:debitor.admin;
|
||||
role:person.admin --> role:debitor.admin;
|
||||
role:contact.admin --> role:debitor.admin;
|
||||
%% 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:debitor.tenant --> perm:debitor.view{{debitor.view}};
|
||||
%% super-roles
|
||||
role:debitor.admin --> role:debitor.tenant;
|
||||
%% sub-roles
|
||||
subgraph office
|
||||
style office fill:#eee
|
||||
|
||||
subgraph bankaccount
|
||||
style bankaccount fill: #e9f7ef
|
||||
|
||||
end;
|
||||
|
||||
subgraph global;
|
||||
role:global.admin --> role:debitor.owner;
|
||||
end;
|
||||
user:hsOfficeBankAccount.creator([bankaccount.creator])
|
||||
|
||||
role:hsOfficeBankAccount.owner[bankaccount.owner]
|
||||
%% permissions
|
||||
role:hsOfficeBankAccount.owner --> perm:hsOfficeBankAccount.*{{bankaccount.*}}
|
||||
%% incoming
|
||||
role:global.admin --> role:hsOfficeBankAccount.owner
|
||||
user:hsOfficeBankAccount.creator ---> role:hsOfficeBankAccount.owner
|
||||
|
||||
role:hsOfficeBankAccount.admin[bankaccount.admin]
|
||||
%% permissions
|
||||
role:hsOfficeBankAccount.admin --> perm:hsOfficeBankAccount.edit{{bankaccount.edit}}
|
||||
%% 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{{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
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
@ -47,35 +47,48 @@ begin
|
||||
select * from hs_office_bankaccount as b where b.uuid = NEW.refundBankAccountUuid into newBankAccount;
|
||||
if TG_OP = 'INSERT' then
|
||||
|
||||
-- the owner role with full access for the global admins
|
||||
ownerRole = createRole(
|
||||
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeDebitorOwner(NEW),
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
|
||||
beneathRole(globalAdmin())
|
||||
permissions => array['*'],
|
||||
incomingSuperRoles => array[globalAdmin()],
|
||||
userUuids => array[currentUserUuid()],
|
||||
grantedByRole => globalAdmin()
|
||||
);
|
||||
|
||||
-- the admin role with full access for owner
|
||||
adminRole = createRole(
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeDebitorAdmin(NEW),
|
||||
withoutPermissions(),
|
||||
beneathRoles(array [
|
||||
hsOfficeDebitorOwner(NEW),
|
||||
hsOfficePartnerAdmin(newPartner),
|
||||
hsOfficePersonAdmin(newPerson),
|
||||
hsOfficeContactAdmin(newContact),
|
||||
hsOfficeBankAccountAdmin(newBankAccount)]),
|
||||
withSubRoles(array [
|
||||
hsOfficePartnerTenant(newPartner),
|
||||
hsOfficePersonTenant(newPerson),
|
||||
hsOfficeContactTenant(newContact),
|
||||
hsOfficeBankAccountTenant(newBankAccount)])
|
||||
permissions => array['edit'],
|
||||
incomingSuperRoles => array[hsOfficeDebitorOwner(NEW)]
|
||||
);
|
||||
|
||||
-- the tenant role for those related users who can view the data
|
||||
perform createRole(
|
||||
hsOfficeDebitorTenant,
|
||||
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['view']),
|
||||
beneathRole(hsOfficeDebitorAdmin(NEW))
|
||||
perform createRoleWithGrants(
|
||||
hsOfficeDebitorAgent(NEW),
|
||||
incomingSuperRoles => array[
|
||||
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
|
||||
@ -83,33 +96,37 @@ begin
|
||||
if OLD.partnerUuid <> NEW.partnerUuid then
|
||||
select * from hs_office_partner as p where p.uuid = OLD.partnerUuid into oldPartner;
|
||||
|
||||
call revokeRoleFromRole(hsOfficeDebitorAdmin(OLD), hsOfficePartnerAdmin(oldPartner));
|
||||
call grantRoleToRole(hsOfficeDebitorAdmin(NEW), hsOfficePartnerAdmin(newPartner));
|
||||
call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficePartnerAdmin(oldPartner));
|
||||
call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficePartnerAdmin(newPartner));
|
||||
|
||||
call revokeRoleFromRole(hsOfficePartnerTenant(oldPartner), hsOfficeDebitorAdmin(OLD));
|
||||
call grantRoleToRole(hsOfficePartnerTenant(newPartner), hsOfficeDebitorAdmin(NEW));
|
||||
call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficePartnerAgent(oldPartner));
|
||||
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;
|
||||
|
||||
if OLD.billingContactUuid <> NEW.billingContactUuid then
|
||||
select * from hs_office_contact as c where c.uuid = OLD.billingContactUuid into oldContact;
|
||||
|
||||
call revokeRoleFromRole(hsOfficeDebitorAdmin(OLD), hsOfficeContactAdmin(oldContact));
|
||||
call grantRoleToRole(hsOfficeDebitorAdmin(NEW), hsOfficeContactAdmin(newContact));
|
||||
call revokeRoleFromRole(hsOfficeDebitorAgent(OLD), hsOfficeContactAdmin(oldContact));
|
||||
call grantRoleToRole(hsOfficeDebitorAgent(NEW), hsOfficeContactAdmin(newContact));
|
||||
|
||||
call revokeRoleFromRole(hsOfficeContactTenant(oldContact), hsOfficeDebitorAdmin(OLD));
|
||||
call grantRoleToRole(hsOfficeContactTenant(newContact), hsOfficeDebitorAdmin(NEW));
|
||||
call revokeRoleFromRole(hsOfficeContactGuest(oldContact), hsOfficeDebitorTenant(OLD));
|
||||
call grantRoleToRole(hsOfficeContactGuest(newContact), hsOfficeDebitorTenant(NEW));
|
||||
end if;
|
||||
|
||||
if OLD.refundBankAccountUuid <> NEW.refundBankAccountUuid then
|
||||
select * from hs_office_bankaccount as b where b.uuid = OLD.refundBankAccountUuid into oldBankAccount;
|
||||
|
||||
call revokeRoleFromRole(hsOfficeDebitorAdmin(OLD), hsOfficeBankAccountAdmin(oldBankAccount));
|
||||
call grantRoleToRole(hsOfficeDebitorAdmin(NEW), hsOfficeBankAccountAdmin(newBankAccount));
|
||||
call revokeRoleFromRole(hsOfficeBankAccountTenant(oldBankaccount), hsOfficeDebitorAgent(OLD));
|
||||
call grantRoleToRole(hsOfficeBankAccountTenant(newBankaccount), hsOfficeDebitorAgent(NEW));
|
||||
|
||||
call revokeRoleFromRole(hsOfficeBankAccountTenant(oldBankAccount), hsOfficeDebitorAdmin(OLD));
|
||||
call grantRoleToRole(hsOfficeBankAccountTenant(newBankAccount), hsOfficeDebitorAdmin(NEW));
|
||||
call revokeRoleFromRole(hsOfficeDebitorTenant(OLD), hsOfficeBankAccountAdmin(oldBankaccount));
|
||||
call grantRoleToRole(hsOfficeDebitorTenant(NEW), hsOfficeBankAccountAdmin(newBankaccount));
|
||||
|
||||
call revokeRoleFromRole(hsOfficeBankAccountGuest(oldBankaccount), hsOfficeDebitorTenant(OLD));
|
||||
call grantRoleToRole(hsOfficeBankAccountGuest(newBankaccount), hsOfficeDebitorTenant(NEW));
|
||||
end if;
|
||||
else
|
||||
raise exception 'invalid usage of TRIGGER';
|
||||
|
Reference in New Issue
Block a user