1
0

introduce defineContext replacing explicit "set local current..."

This commit is contained in:
Michael Hoennig
2022-08-30 09:35:59 +02:00
parent 8045b66324
commit a1c3e95032
19 changed files with 328 additions and 248 deletions

View File

@ -4,10 +4,24 @@
-- ============================================================================
--changeset context-DEFINE:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Callback which is called after the context has been (re-) defined.
This function will be overwritten by later changesets.
*/
create procedure contextDefined(
currentTask varchar,
currentRequest varchar,
currentUser varchar,
assumedRoles varchar
)
language plpgsql as $$
begin
end; $$;
/*
Defines the transaction context.
*/
create or replace procedure defineContext(
currentTask varchar,
currentRequest varchar,
@ -16,14 +30,18 @@ create or replace procedure defineContext(
)
language plpgsql as $$
begin
raise notice 'currentRequest: %', defineContext.currentRequest;
execute format('set local hsadminng.currentTask to %L', currentTask);
currentRequest := coalesce(currentRequest, '');
execute format('set local hsadminng.currentRequest to %L', currentRequest);
currentUser := coalesce(currentUser, '');
execute format('set local hsadminng.currentUser to %L', currentUser);
if length(assumedRoles) > 0 then
execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
else
execute format('set local hsadminng.assumedRoles to %L', '');
end if;
assumedRoles := coalesce(assumedRoles, '');
execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
call contextDefined(currentTask, currentRequest, currentUser, assumedRoles);
end; $$;
--//
@ -49,7 +67,7 @@ begin
currentTask := null;
end;
if (currentTask is null or currentTask = '') then
raise exception '[401] hsadminng.currentTask must be defined, please use "SET LOCAL ...;"';
raise exception '[401] currentTask must be defined, please call `defineContext(...)`';
end if;
raise debug 'currentTask: %', currentTask;
return currentTask;
@ -61,8 +79,7 @@ end; $$;
--changeset context-CURRENT-USER:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Returns the current user as set by `hsadminng.currentUser`.
Raises exception if not set.
Returns the current user as defined by `defineContext(...)`.
*/
create or replace function currentUser()
returns varchar(63)
@ -77,10 +94,6 @@ begin
when others then
currentUser := null;
end;
if (currentUser is null or currentUser = '') then
raise exception '[401] hsadminng.currentUser must be defined, please use "SET LOCAL ...;"';
end if;
raise debug 'currentUser: %', currentUser;
return currentUser;
end; $$;
--//

View File

@ -9,15 +9,15 @@ create or replace function assumedRoleUuid()
stable leakproof
language plpgsql as $$
declare
currentSubjectUuids uuid[];
currentSubjectsUuids uuid[];
begin
-- exactly one role must be assumed, not none not more than one
if cardinality(assumedRoles()) <> 1 then
raise exception '[400] Granting roles to user is only possible if exactly one role is assumed, given: %', assumedRoles();
end if;
currentSubjectUuids := currentSubjectsUuids();
return currentSubjectUuids[1];
currentSubjectsUuids := currentSubjectsUuids();
return currentSubjectsUuids[1];
end; $$;
create or replace procedure grantRoleToUserUnchecked(grantedByRoleUuid uuid, roleUuid uuid, userUuid uuid, doAssume boolean = true)

View File

@ -1,45 +1,32 @@
--liquibase formatted sql
-- ============================================================================
--changeset rbac-context-CURRENT-USER-ID:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Returns the id of the current user as set by `hsadminng.currentUser`.
Raises exception if not set.
*/
create or replace function currentUserUuid()
-- ============================================================================
--changeset rbac-context-DETERMINE:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
create or replace function determineCurrentUserUuid(currentUser varchar)
returns uuid
stable leakproof
language plpgsql as $$
declare
currentUser varchar(63);
currentUserUuid uuid;
begin
currentUser := currentUser();
currentUserUuid = (select uuid from RbacUser where name = currentUser);
if currentUserUuid is null then
raise exception '[401] hsadminng.currentUser defined as %, but does not exists', currentUser;
if currentUser = '' then
return null;
end if;
select uuid from RbacUser where name = currentUser into currentUserUuid;
-- TODO: maybe this should be changed, and in this case no user name defined in context?
-- no exception if user does not exist because users can register themselves
return currentUserUuid;
end; $$;
--//
-- ============================================================================
--changeset rbac-context-CURRENT-SUBJECT-IDS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Returns id of current user as set in `hsadminng.currentUser`
or, if any, ids of assumed role names as set in `hsadminng.assumedRoles`
or empty array, if not set.
*/
create or replace function currentSubjectsUuids()
create or replace function determineCurrentSubjectsUuids(currentUserUuid uuid, assumedRoles varchar)
returns uuid[]
stable leakproof
language plpgsql as $$
declare
currentUserUuid uuid;
roleNames varchar(63)[];
roleName varchar(63);
objectTableToAssume varchar(63);
objectNameToAssume varchar(63);
@ -48,19 +35,18 @@ declare
roleIdsToAssume uuid[];
roleUuidToAssume uuid;
begin
currentUserUuid := currentUserUuid();
if currentUserUuid is null then
raise exception '[401] user % does not exist', currentUser();
if length(coalesce(assumedRoles, '')) > 0 then
raise exception '[403] undefined has no permission to assume role %', assumedRoles;
else
return array[]::uuid[];
end if;
end if;
roleNames := assumedRoles();
if cardinality(roleNames) = 0 then
if length(coalesce(assumedRoles, '')) = 0 then
return array [currentUserUuid];
end if;
raise notice 'assuming roles: %', roleNames;
foreach roleName in array roleNames
foreach roleName in array string_to_array(assumedRoles, ';')
loop
roleName = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), '.'));
objectTableToAssume = split_part(roleName, '#', 1);
@ -69,19 +55,117 @@ begin
objectUuidToAssume = findObjectUuidByIdName(objectTableToAssume, objectNameToAssume);
-- TODO: either the result needs to be cached at least per transaction or we need to get rid of SELCT in a loop
select uuid as roleuuidToAssume
from RbacRole r
where r.objectUuid = objectUuidToAssume
and r.roleType = roleTypeToAssume
into roleUuidToAssume;
if (not isGranted(currentUserUuid, roleUuidToAssume)) then
raise exception '[403] user % (%) has no permission to assume role % (%)', currentUser(), currentUserUuid, roleName, roleUuidToAssume;
raise exception '[403] user % has no permission to assume role %', currentUser(), roleName;
end if;
roleIdsToAssume := roleIdsToAssume || roleUuidToAssume;
end loop;
return roleIdsToAssume;
end; $$;
-- ============================================================================
--changeset rbac-context-CONTEXT-DEFINED:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Callback which is called after the context has been (re-) defined.
This function will be overwritten by later changesets.
*/
create or replace procedure contextDefined(
currentTask varchar,
currentRequest varchar,
currentUser varchar,
assumedRoles varchar
)
language plpgsql as $$
declare
currentUserUuid uuid;
begin
execute format('set local hsadminng.currentTask to %L', currentTask);
execute format('set local hsadminng.currentRequest to %L', currentRequest);
execute format('set local hsadminng.currentUser to %L', currentUser);
select determineCurrentUserUuid(currentUser) into currentUserUuid;
execute format('set local hsadminng.currentUserUuid to %L', coalesce(currentUserUuid::text, ''));
execute format('set local hsadminng.assumedRoles to %L', assumedRoles);
execute format('set local hsadminng.currentSubjectsUuids to %L',
(select array_to_string(determinecurrentSubjectsUuids(currentUserUuid, assumedRoles), ';')));
raise notice 'Context defined as: %, %, %, [%]', currentTask, currentRequest, currentUser, assumedRoles;
end; $$;
-- ============================================================================
--changeset rbac-context-CURRENT-USER-ID:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Returns the uuid of the current user as set via `defineContext(...)`.
*/
create or replace function currentUserUuid()
returns uuid
stable leakproof
language plpgsql as $$
declare
currentUserUuid text;
currentUserName text;
begin
begin
currentUserUuid := current_setting('hsadminng.currentUserUuid');
exception
when others then
currentUserUuid := null;
end;
if (currentUserUuid is null or currentUserUuid = '') then
currentUserName := currentUser();
if (length(currentUserName) > 0) then
raise exception '[401] currentUserUuid cannot be determined, unknown user name "%"', currentUserName;
else
raise exception '[401] currentUserUuid cannot be determined, please call `defineContext(...)` first;"';
end if;
end if;
return currentUserUuid::uuid;
end; $$;
--//
-- ============================================================================
--changeset rbac-context-CURRENT-SUBJECT-UUIDS:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Returns the uuid of the current user as set via `defineContext(...)`,
or, if any, the uuids of all assumed roles as set via `defineContext(...)`
or empty array, if context is not defined.
*/
create or replace function currentSubjectsUuids()
returns uuid[]
stable leakproof
language plpgsql as $$
declare
currentSubjectsUuids text;
currentUserName text;
begin
begin
currentSubjectsUuids := current_setting('hsadminng.currentSubjectsUuids');
exception
when others then
currentSubjectsUuids := null;
end;
if (currentSubjectsUuids is null or length(currentSubjectsUuids) = 0 ) then
currentUserName := currentUser();
if (length(currentUserName) > 0) then
raise exception '[401] currentUserUuid cannot be determined, unknown user name "%"', currentUserName;
else
raise exception '[401] currentSubjectsUuids cannot be determined, please call `defineContext(...)` first;"';
end if;
end if;
return string_to_array(currentSubjectsUuids, ';');
end; $$;
--//

View File

@ -21,12 +21,13 @@ grant select on global to restricted;
/**
A single row to be referenced as a global object.
*/
set local hsadminng.currentUser to 'init';
set local hsadminng.currentTask to 'initializing table "global"';
insert
into RbacObject (objecttable) values ('global');
insert
into Global (uuid, name) values ((select uuid from RbacObject where objectTable = 'global'), 'hostsharing');
begin transaction;
call defineContext('initializing table "global"', null, null, null);
insert
into RbacObject (objecttable) values ('global');
insert
into Global (uuid, name) values ((select uuid from RbacObject where objectTable = 'global'), 'hostsharing');
commit;
--//
@ -40,8 +41,7 @@ create or replace function hasGlobalPermission(op RbacOp)
$$
-- TODO: this could to be optimized
select (select uuid from global) in
(select queryAccessibleObjectUuidsOfSubjectIds(
op, 'global', currentSubjectsUuids()));
(select queryAccessibleObjectUuidsOfSubjectIds(op, 'global', currentSubjectsUuids()));
$$;
--//
@ -94,9 +94,10 @@ create or replace function hostsharingAdmin()
select 'global', (select uuid from RbacObject where objectTable = 'global'), 'admin'::RbacRoleType;
$$;
set local hsadminng.currentUser to 'init';
set local hsadminng.currentTask to 'creating Hostsharing admin role';
select createRole(hostsharingAdmin());
begin transaction;
call defineContext('creating Hostsharing admin role', null, null, null);
select createRole(hostsharingAdmin());
commit;
-- ============================================================================
--changeset hs-base-ADMIN-USERS:1 context:dev,tc endDelimiter:--//
@ -108,8 +109,7 @@ do language plpgsql $$
declare
admins uuid ;
begin
set local hsadminng.currentUser to 'init';
set local hsadminng.currentTask to 'creating fake Hostsharing admin users';
call defineContext('creating fake Hostsharing admin users', null, null, null);
admins = findRoleId(hostsharingAdmin());
call grantRoleToUserUnchecked(admins, admins, createRbacUser('mike@hostsharing.net'));
@ -131,13 +131,13 @@ do language plpgsql $$
declare
userName varchar;
begin
set local hsadminng.currentUser = 'sven@hostsharing.net';
call defineContext('testing currentUserUuid', null, 'sven@hostsharing.net', null);
select userName from RbacUser where uuid = currentUserUuid() into userName;
if userName <> 'sven@hostsharing.net' then
raise exception 'setting or fetching initial currentUser failed, got: %', userName;
end if;
set local hsadminng.currentUser = 'mike@hostsharing.net';
call defineContext('testing currentUserUuid', null, 'mike@hostsharing.net', null);
select userName from RbacUser where uuid = currentUserUuid() into userName;
if userName = 'mike@hostsharing.net' then
raise exception 'currentUser should not change in one transaction, but did change, got: %', userName;

View File

@ -206,8 +206,7 @@ do language plpgsql $$
hostsharingObjectUuid uuid;
hsAdminRoleUuid uuid ;
begin
set local hsadminng.currentUser to 'init';
set local hsadminng.currentTask to 'granting global add-customer permission to Hostsharing admin role';
call defineContext('granting global add-customer permission to Hostsharing admin role', null, null, null);
hsAdminRoleUuid := findRoleId(hostsharingAdmin());
hostsharingObjectUuid := (select uuid from global);
@ -224,7 +223,8 @@ create or replace function addCustomerNotAllowedForCurrentSubjects()
language PLPGSQL
as $$
begin
raise exception '[403] add-customer not permitted for %', array_to_string(currentSubjects(), ';', 'null');
raise exception '[403] add-customer not permitted for %',
array_to_string(currentSubjects(), ';', 'null');
end; $$;
/**

View File

@ -30,8 +30,7 @@ declare
custAdminName varchar;
begin
currentTask = 'creating RBAC test customer #' || custReference || '/' || custPrefix;
set local hsadminng.currentUser to 'mike@hostsharing.net';
set local hsadminng.assumedRoles to 'global#hostsharing.admin';
call defineContext(currentTask, null, 'mike@hostsharing.net', 'global#hostsharing.admin');
execute format('set local hsadminng.currentTask to %L', currentTask);
custRowId = uuid_generate_v4();
@ -53,8 +52,6 @@ create or replace procedure createCustomerTestData(
)
language plpgsql as $$
begin
set hsadminng.currentUser to '';
for t in startCount..endCount
loop
call createCustomerTestData(testCustomerReference(t), intToVarChar(t, 3));

View File

@ -26,9 +26,7 @@ begin
custAdminUser = 'customer-admin@' || cust.prefix || '.example.com';
custAdminRole = 'customer#' || cust.prefix || '.admin';
execute format('set local hsadminng.currentUser to %L', custAdminUser);
execute format('set local hsadminng.assumedRoles to %L', custAdminRole);
execute format('set local hsadminng.currentTask to %L', currentTask);
call defineContext(currentTask, null, custAdminUser, custAdminRole);
raise notice 'task: % by % as %', currentTask, custAdminUser, custAdminRole;
insert
@ -53,8 +51,6 @@ create or replace procedure createPackageTestData()
declare
cust customer;
begin
set hsadminng.currentUser to '';
for cust in (select * from customer)
loop
continue when cust.reference >= 90000; -- reserved for functional testing

View File

@ -13,8 +13,6 @@ declare
pacAdmin varchar;
currentTask varchar;
begin
set hsadminng.currentUser to '';
select p.uuid, p.name, c.prefix as custPrefix
from package p
join customer c on p.customeruuid = c.uuid
@ -26,9 +24,7 @@ begin
currentTask = 'creating RBAC test unixuser #' || t || ' for package ' || pac.name || ' #' || pac.uuid;
raise notice 'task: %', currentTask;
pacAdmin = 'pac-admin-' || pac.name || '@' || pac.custPrefix || '.example.com';
execute format('set local hsadminng.currentTask to %L', currentTask);
execute format('set local hsadminng.currentUser to %L', pacAdmin);
set local hsadminng.assumedRoles = '';
call defineContext(currentTask, null, pacAdmin, null);
insert
into unixuser (name, packageUuid)
@ -46,8 +42,6 @@ declare
pacAdmin varchar;
currentTask varchar;
begin
set hsadminng.currentUser to '';
for pac in
(select p.uuid, p.name
from package p