introduce defineContext replacing explicit "set local current..."
This commit is contained in:
@ -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; $$;
|
||||
--//
|
||||
|
@ -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)
|
||||
|
@ -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; $$;
|
||||
--//
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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; $$;
|
||||
|
||||
/**
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user