hs.hsadmin.ng/src/main/resources/db/changelog/2022-07-28-006-rbac-current.sql

188 lines
5.8 KiB
MySQL
Raw Normal View History

--liquibase formatted sql
-- ============================================================================
--changeset rbac-current-CURRENT-USER:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Returns the current user as set by `hsadminng.currentUser`.
Raises exception if not set.
*/
create or replace function currentUser()
returns varchar(63)
stable leakproof
language plpgsql as $$
declare
currentUser varchar(63);
begin
begin
currentUser := current_setting('hsadminng.currentUser');
exception
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;
return currentUser;
end; $$;
create or replace function currentUserId()
returns uuid
stable leakproof
language plpgsql as $$
declare
currentUser varchar(63);
currentUserId uuid;
begin
currentUser := currentUser();
currentUserId = (select uuid from RbacUser where name = currentUser);
if currentUserId is null then
raise exception '[401] hsadminng.currentUser defined as %, but does not exists', currentUser;
end if;
return currentUserId;
end; $$;
--//
-- ============================================================================
--changeset rbac-current-ASSUMED-ROLES:1 endDelimiter:--//
-- ----------------------------------------------------------------------------
/*
Returns assumed role names as set in `hsadminng.assumedRoles`
or empty array, if not set.
*/
create or replace function assumedRoles()
returns varchar(63)[]
stable leakproof
language plpgsql as $$
declare
currentSubject varchar(63);
begin
begin
currentSubject := current_setting('hsadminng.assumedRoles');
exception
when others then
return array []::varchar[];
end;
if (currentSubject = '') then
return array []::varchar[];
end if;
return string_to_array(currentSubject, ';');
end; $$;
create or replace function pureIdentifier(rawIdentifier varchar)
returns varchar
returns null on null input
language plpgsql as $$
begin
return regexp_replace(rawIdentifier, '\W+', '');
end; $$;
create or replace function findObjectUuidByIdName(objectTable varchar, objectIdName varchar)
returns uuid
returns null on null input
language plpgsql as $$
declare
sql varchar;
uuid uuid;
begin
objectTable := pureIdentifier(objectTable);
objectIdName := pureIdentifier(objectIdName);
sql := format('select * from %sUuidByIdName(%L);', objectTable, objectIdName);
begin
raise notice 'sql: %', sql;
execute sql into uuid;
exception
when others then
raise exception 'function %UuidByIdName(...) not found, add identity view support for table %', objectTable, objectTable;
end;
return uuid;
end ; $$;
create or replace function findIdNameByObjectUuid(objectTable varchar, objectUuid uuid)
returns varchar
returns null on null input
language plpgsql as $$
declare
sql varchar;
idName varchar;
begin
objectTable := pureIdentifier(objectTable);
sql := format('select * from %sIdNameByUuid(%L::uuid);', objectTable, objectUuid);
begin
raise notice 'sql: %', sql;
execute sql into idName;
exception
when others then
raise exception 'function %IdNameByUuid(...) not found, add identity view support for table %', objectTable, objectTable;
end;
return idName;
end ; $$;
create or replace function currentSubjects()
returns varchar(63)[]
stable leakproof
language plpgsql as $$
declare
assumedRoles varchar(63)[];
begin
assumedRoles := assumedRoles();
if array_length(assumedRoles(), 1) > 0 then
return assumedRoles();
else
return array [currentUser()]::varchar(63)[];
end if;
end; $$;
create or replace function currentSubjectIds()
returns uuid[]
stable leakproof
language plpgsql as $$
declare
currentUserId uuid;
roleNames varchar(63)[];
roleName varchar(63);
objectTableToAssume varchar(63);
objectNameToAssume varchar(63);
objectUuidToAssume uuid;
roleTypeToAssume RbacRoleType;
roleIdsToAssume uuid[];
roleUuidToAssume uuid;
begin
currentUserId := currentUserId();
if currentUserId is null then
raise exception '[401] user % does not exist', currentUser();
end if;
roleNames := assumedRoles();
if cardinality(roleNames) = 0 then
return array [currentUserId];
end if;
raise notice 'assuming roles: %', roleNames;
foreach roleName in array roleNames
loop
roleName = overlay(roleName placing '#' from length(roleName) + 1 - strpos(reverse(roleName), '.'));
objectTableToAssume = split_part(roleName, '#', 1);
objectNameToAssume = split_part(roleName, '#', 2);
roleTypeToAssume = split_part(roleName, '#', 3);
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(currentUserId, roleUuidToAssume)) then
raise exception '[403] user % (%) has no permission to assume role % (%)', currentUser(), currentUserId, roleName, roleUuidToAssume;
end if;
roleIdsToAssume := roleIdsToAssume || roleUuidToAssume;
end loop;
return roleIdsToAssume;
end; $$;
--//