1
0

add unixuser prototype as preparation for testability of grants

This commit is contained in:
Michael Hoennig
2022-08-14 16:44:26 +02:00
parent bc05fb1eeb
commit 7869d07d30
11 changed files with 443 additions and 206 deletions

View File

@ -629,19 +629,21 @@ create or replace function queryAllRbacUsersWithPermissionsFor(objectId uuid)
language sql as $$
select *
from RbacUser
where uuid in (with recursive grants as (select descendantUuid,
ascendantUuid
from RbacGrants
where descendantUuid = objectId
union all
select "grant".descendantUuid,
"grant".ascendantUuid
from RbacGrants "grant"
inner join grants recur on recur.ascendantUuid = "grant".descendantUuid)
select ascendantUuid
from grants);
where uuid in (
-- @formatter:off
with recursive grants as (
select descendantUuid, ascendantUuid
from RbacGrants
where descendantUuid = objectId
union all
select "grant".descendantUuid, "grant".ascendantUuid
from RbacGrants "grant"
inner join grants recur on recur.ascendantUuid = "grant".descendantUuid
)
-- @formatter:on
select ascendantUuid
from grants);
$$;
--//

View File

@ -19,7 +19,7 @@ select *
where isGranted(currentSubjectIds(), r.uuid)
) as unordered
-- @formatter:on
order by objectIdName;
order by objectTable || '#' || objectIdName || '.' || roleType;
grant all privileges on rbacrole_rv to restricted;
--//
@ -49,7 +49,7 @@ select userName, objectTable||'#'||objectIdName||'.'||roletype as roleIdName,
where isGranted(currentSubjectIds(), r.uuid)
) as unordered
-- @formatter:on
order by objectIdName;
order by roleIdName;
grant all privileges on rbacrole_rv to restricted;
--//

View File

@ -9,6 +9,6 @@ create table if not exists package
uuid uuid unique references RbacObject (uuid),
customerUuid uuid references customer (uuid),
name varchar(5),
description varchar(80)
description varchar(96)
);
--//

View File

@ -0,0 +1,14 @@
--liquibase formatted sql
-- ============================================================================
--changeset hs-unixuser-MAIN-TABLE:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
create table if not exists UnixUser
(
uuid uuid unique references RbacObject (uuid),
packageUuid uuid references package (uuid),
name character varying(32),
description character varying(96)
);
--//

View File

@ -0,0 +1,211 @@
--liquibase formatted sql
-- ============================================================================
--changeset hs-package-rbac-CREATE-OBJECT:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates the related RbacObject through a BEFORE INSERT TRIGGER.
*/
drop trigger if exists createRbacObjectForUnixUser_Trigger on UnixUser;
create trigger createRbacObjectForUnixUser_Trigger
before insert
on UnixUser
for each row
execute procedure createRbacObject();
--//
-- ============================================================================
--changeset hs-unixuser-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
create or replace function unixUserOwner(uu UnixUser)
returns RbacRoleDescriptor
returns null on null input
language plpgsql as $$
begin
return roleDescriptor('unixuser', uu.uuid, 'owner');
end; $$;
create or replace function unixUserAdmin(uu UnixUser)
returns RbacRoleDescriptor
returns null on null input
language plpgsql as $$
begin
return roleDescriptor('unixuser', uu.uuid, 'admin');
end; $$;
create or replace function unixUserTenant(uu UnixUser)
returns RbacRoleDescriptor
returns null on null input
language plpgsql as $$
begin
return roleDescriptor('unixuser', uu.uuid, 'tenant');
end; $$;
create or replace function createUnixUserTenantRoleIfNotExists(unixUser UnixUser)
returns uuid
returns null on null input
language plpgsql as $$
declare
unixUserTenantRoleDesc RbacRoleDescriptor;
unixUserTenantRoleUuid uuid;
begin
unixUserTenantRoleDesc = unixUserTenant(unixUser);
unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleDesc);
if unixUserTenantRoleUuid is not null then
return unixUserTenantRoleUuid;
end if;
return createRole(
unixUserTenantRoleDesc,
grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => array ['view']),
beneathRole(unixUserAdmin(unixUser))
);
end; $$;
--//
-- ============================================================================
--changeset hs-unixuser-rbac-ROLES-CREATION:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates the roles and their assignments for a new UnixUser for the AFTER INSERT TRIGGER.
*/
create or replace function createRbacRulesForUnixUser()
returns trigger
language plpgsql
strict as $$
declare
parentPackage package;
unixuserOwnerRoleId uuid;
unixuserAdminRoleId uuid;
begin
if TG_OP <> 'INSERT' then
raise exception 'invalid usage of TRIGGER AFTER INSERT';
end if;
select * from package where uuid = NEW.packageUuid into parentPackage;
-- an owner role is created and assigned to the package's admin group
unixuserOwnerRoleId = createRole(
unixUserOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
beneathRole(packageAdmin(parentPackage))
);
-- and a unixuser admin role is created and assigned to the unixuser owner as well
unixuserAdminRoleId = createRole(
unixUserAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
beneathRole(unixuserOwnerRoleId),
beingItselfA(packageTenant(parentPackage))
);
-- a tenent role is only created on demand
return NEW;
end; $$;
/*
An AFTER INSERT TRIGGER which creates the role structure for a new UnixUser.
*/
drop trigger if exists createRbacRulesForUnixUser_Trigger on UnixUser;
create trigger createRbacRulesForUnixUser_Trigger
after insert
on UnixUser
for each row
execute procedure createRbacRulesForUnixUser();
--//
-- ============================================================================
--changeset hs-unixuser-rbac-ROLES-REMOVAL:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Deletes the roles and their assignments of a deleted UnixUser for the BEFORE DELETE TRIGGER.
*/
create or replace function deleteRbacRulesForUnixUser()
returns trigger
language plpgsql
strict as $$
begin
if TG_OP = 'DELETE' then
call deleteRole(findRoleId(unixUserOwner(OLD)));
call deleteRole(findRoleId(unixUserAdmin(OLD)));
call deleteRole(findRoleId(unixUserTenant(OLD)));
else
raise exception 'invalid usage of TRIGGER BEFORE DELETE';
end if;
end; $$;
/*
An BEFORE DELETE TRIGGER which deletes the role structure of a UnixUser.
*/
drop trigger if exists deleteRbacRulesForUnixUser_Trigger on package;
create trigger deleteRbacRulesForUnixUser_Trigger
before delete
on UnixUser
for each row
execute procedure deleteRbacRulesForUnixUser();
--//
-- ============================================================================
--changeset hs-unixuser-rbac-IDENTITY-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates a view to the UnixUser main table which maps the identifying name
(in this case, actually the column `name`) to the objectUuid.
*/
drop view if exists UnixUser_iv;
create or replace view UnixUser_iv as
select distinct target.uuid, target.name as idName
from UnixUser as target;
-- TODO: Is it ok that everybody has access to this information?
grant all privileges on UnixUser_iv to restricted;
/*
Returns the objectUuid for a given identifying name (in this case, actually the column `name`).
*/
create or replace function unixUserUuidByIdName(idName varchar)
returns uuid
language sql
strict as $$
select uuid from UnixUser_iv iv where iv.idName = unixUserUuidByIdName.idName;
$$;
/*
Returns the identifying name for a given objectUuid (in this case the name).
*/
create or replace function unixUserIdNameByUuid(uuid uuid)
returns varchar
stable leakproof
language sql
strict as $$
select idName from UnixUser_iv iv where iv.uuid = unixUserIdNameByUuid.uuid;
$$;
--//
-- ============================================================================
--changeset hs-package-rbac-RESTRICTED-VIEW:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates a view to the customer main table which maps the identifying name
(in this case, the prefix) to the objectUuid.
*/
drop view if exists unixuser_rv;
create or replace view unixuser_rv as
select target.*
from unixuser as target
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'unixuser', currentSubjectIds()));
grant all privileges on unixuser_rv to restricted;
--//

View File

@ -0,0 +1,62 @@
--liquibase formatted sql
-- ============================================================================
--changeset hs-unixuser-TEST-DATA-GENERATOR:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Creates test data for the package main table.
*/
create or replace procedure createUnixUserTestData(
minCustomerReference integer, -- skip customers with reference below this
unixUserPerPackage integer, -- create this many unix users for each package
doCommitAfterEach boolean -- only for mass data creation outside of Liquibase
)
language plpgsql as $$
declare
pac record;
pacAdmin varchar;
currentTask varchar;
begin
set hsadminng.currentUser to '';
for pac in
(select p.uuid, p.name
from package p
join customer c on p.customeruuid = c.uuid
where c.reference >= minCustomerReference)
loop
for t in 0..(unixUserPerPackage-1)
loop
currentTask = 'creating RBAC test unixuser #' || t || ' for package ' || pac.name || ' #' || pac.uuid;
raise notice 'task: %', currentTask;
pacAdmin = 'admin@' || pac.name || '.example.com';
set local hsadminng.currentUser to 'mike@hostsharing.net'; -- TODO: use a package-admin
set local hsadminng.assumedRoles = '';
set local hsadminng.currentTask to currentTask;
insert
into unixuser (name, packageUuid)
values (pac.name || '-' || intToVarChar(t, 4), pac.uuid);
if doCommitAfterEach then
commit;
end if;
end loop;
end loop;
end;
$$;
--//
-- ============================================================================
--changeset hs-unixuser-TEST-DATA-GENERATION:1 context=dev,tc endDelimiter:--//
-- ----------------------------------------------------------------------------
do language plpgsql $$
begin
call createUnixUserTestData(0, 2, false);
end;
$$;
--//

View File

@ -1,159 +0,0 @@
-- ========================================================
-- UnixUser example with RBAC
-- --------------------------------------------------------
set session session authorization default;
create table if not exists UnixUser
(
uuid uuid unique references RbacObject (uuid),
name character varying(32),
comment character varying(96),
packageUuid uuid references package (uuid)
);
create or replace function unixUserOwner(uu UnixUser)
returns RbacRoleDescriptor
returns null on null input
language plpgsql as $$
begin
return roleDescriptor('unixuser', uu.uuid, 'owner');
end; $$;
create or replace function unixUserAdmin(uu UnixUser)
returns RbacRoleDescriptor
returns null on null input
language plpgsql as $$
begin
return roleDescriptor('unixuser', uu.uuid, 'admin');
end; $$;
create or replace function unixUserTenant(uu UnixUser)
returns RbacRoleDescriptor
returns null on null input
language plpgsql as $$
begin
return roleDescriptor('unixuser', uu.uuid, 'tenant');
end; $$;
create or replace function createUnixUserTenantRoleIfNotExists(unixUser UnixUser)
returns uuid
returns null on null input
language plpgsql as $$
declare
unixUserTenantRoleDesc RbacRoleDescriptor;
unixUserTenantRoleUuid uuid;
begin
unixUserTenantRoleDesc = unixUserTenant(unixUser);
unixUserTenantRoleUuid = findRoleId(unixUserTenantRoleDesc);
if unixUserTenantRoleUuid is not null then
return unixUserTenantRoleUuid;
end if;
return createRole(
unixUserTenantRoleDesc,
grantingPermissions(forObjectUuid => unixUser.uuid, permitOps => array ['view']),
beneathRole(unixUserAdmin(unixUser))
);
end; $$;
drop trigger if exists createRbacObjectForUnixUser_Trigger on UnixUser;
create trigger createRbacObjectForUnixUser_Trigger
before insert
on UnixUser
for each row
execute procedure createRbacObject();
create or replace function createRbacRulesForUnixUser()
returns trigger
language plpgsql
strict as $$
declare
parentPackage package;
unixuserOwnerRoleId uuid;
unixuserAdminRoleId uuid;
begin
if TG_OP <> 'INSERT' then
raise exception 'invalid usage of TRIGGER AFTER INSERT';
end if;
select * from package where uuid = NEW.packageUuid into parentPackage;
-- an owner role is created and assigned to the package's admin group
unixuserOwnerRoleId = createRole(
unixUserOwner(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['*']),
beneathRole(packageAdmin(parentPackage))
);
-- and a unixuser admin role is created and assigned to the unixuser owner as well
unixuserAdminRoleId = createRole(
unixUserAdmin(NEW),
grantingPermissions(forObjectUuid => NEW.uuid, permitOps => array ['edit']),
beneathRole(unixuserOwnerRoleId),
beingItselfA(packageTenant(parentPackage))
);
-- a tenent role is only created on demand
return NEW;
end; $$;
drop trigger if exists createRbacRulesForUnixUser_Trigger on UnixUser;
create trigger createRbacRulesForUnixUser_Trigger
after insert
on UnixUser
for each row
execute procedure createRbacRulesForUnixUser();
-- TODO: CREATE OR REPLACE FUNCTION deleteRbacRulesForUnixUser()
-- create RBAC-restricted view
set session session authorization default;
-- ALTER TABLE unixuser ENABLE ROW LEVEL SECURITY;
drop view if exists unixuser_rv;
create or replace view unixuser_rv as
select target.*
from unixuser as target
where target.uuid in (select queryAccessibleObjectUuidsOfSubjectIds('view', 'unixuser', currentSubjectIds()));
grant all privileges on unixuser_rv to restricted;
-- generate UnixUser test data
do language plpgsql $$
declare
pac record;
pacAdmin varchar;
currentTask varchar;
begin
set hsadminng.currentUser to '';
for pac in (select p.uuid, p.name
from package p
join customer c on p.customeruuid = c.uuid
-- WHERE c.reference >= 18000
)
loop
for t in 0..9
loop
currentTask = 'creating RBAC test unixuser #' || t || ' for package ' || pac.name || ' #' || pac.uuid;
raise notice 'task: %', currentTask;
pacAdmin = 'admin@' || pac.name || '.example.com';
set local hsadminng.currentUser to 'mike@hostsharing.net'; -- TODO: use a package-admin
set local hsadminng.assumedRoles = '';
set local hsadminng.currentTask to currentTask;
insert
into unixuser (name, packageUuid)
values (pac.name || '-' || intToVarChar(t, 4), pac.uuid);
commit;
end loop;
end loop;
end;
$$;

View File

@ -30,6 +30,12 @@ databaseChangeLog:
- include:
file: db/changelog/2022-07-29-070-hs-package-rbac.sql
- include:
file: db/changelog/2022-07-29-070-hs-package-test-data.sql
file: db/changelog/2022-07-29-070-hs-package-test-data.sql
- include:
file: db/changelog/2022-08-14-080-hs-unixuser.sql
- include:
file: db/changelog/2022-08-14-081-hs-unixuser-rbac.sql
- include:
file: db/changelog/2022-08-14-082-hs-unixuser-test-data.sql