platform-db/changesets/registry/procedures/f_check_permissions.sql (51 lines of code) (raw):
--liquibase formatted sql
/*
* Copyright 2021 EPAM Systems.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--changeset platform:f_check_permissions splitStatements:false stripComments:false runOnChange:true
CREATE OR REPLACE FUNCTION public.f_check_permissions(p_object_name text, p_roles_arr text[], p_operation type_operation DEFAULT 'S'::type_operation, p_columns_arr text[] DEFAULT NULL::text[])
RETURNS boolean
LANGUAGE plpgsql
AS $function$
DECLARE
c_unit_name text := 'f_check_permissions';
l_ret BOOLEAN;
l_is_role_found integer;
BEGIN
call p_raise_notice(format('%s: p_object_name [%s]', c_unit_name, p_object_name));
call p_raise_notice(format('%s: p_roles_arr (list of user roles) [%s]', c_unit_name, p_roles_arr));
call p_raise_notice(format('%s: p_operation [%s]', c_unit_name, p_operation));
call p_raise_notice(format('%s: p_columns_arr (list of updated columns) [%s]', c_unit_name, p_columns_arr));
-- check list of user roles
if p_roles_arr is not null and cardinality(p_roles_arr) = 4 then
select 1
into l_is_role_found
from (select unnest(p_roles_arr) as role) as t
where t.role is not null
limit 1;
if l_is_role_found is null then
call p_raise_notice(format('%s: list of user roles has four null elements => system role marker => rbac check is skipped', c_unit_name));
return true;
end if;
end if;
-- check if table is RBAC regulated
SELECT count(1) = 0 INTO l_ret FROM (SELECT 1 FROM ddm_role_permission WHERE object_name = p_object_name LIMIT 1) s;
IF l_ret THEN
call p_raise_notice(format('%s: table [%s] is not RBAC regiulated => rbac check is skipped', c_unit_name, p_object_name));
RETURN l_ret;
END IF;
-- check permission for all columns
call p_raise_notice(format('%s: list of user roles for check [%s]', c_unit_name, array_append(p_roles_arr,'isAuthenticated')));
SELECT count(1) > 0 INTO l_ret FROM ddm_role_permission
WHERE object_name = p_object_name AND operation = p_operation AND role_name = ANY(array_append(p_roles_arr,'isAuthenticated')) AND trim(coalesce(column_name, '')) = '';
--
if l_ret then
call p_raise_notice(format('%s: table [%s], operation [%s], one of user roles found => access permitted', c_unit_name, p_object_name, p_operation));
return l_ret;
elsif not l_ret and p_operation in ('S', 'I', 'D') then
call p_raise_notice(format('%s: table [%s], operation [%s], none of user roles found => access denied', c_unit_name, p_object_name, p_operation));
return l_ret;
end if;
-- we are here if operation = U and permission for all columns is not set
-- check the list of updated columns
if p_columns_arr is null or cardinality(p_columns_arr) = 0 then
call p_raise_notice(format('%s: table [%s], operation [%s], none of user roles found, list of updated columns is empty => access denied', c_unit_name, p_object_name, p_operation));
return false;
end if;
-- check permissions per column
SELECT count(DISTINCT column_name) = array_length(p_columns_arr, 1) INTO l_ret FROM ddm_role_permission
WHERE object_name = p_object_name AND operation = p_operation AND role_name = ANY(array_append(p_roles_arr,'isAuthenticated')) AND column_name = ANY(p_columns_arr);
--
call p_raise_notice(format('%s: table [%s], operation [%s] => access ' || case when l_ret then 'permitted' else 'denied' end, c_unit_name, p_object_name, p_operation));
RETURN l_ret;
END;
$function$
SECURITY DEFINER
SET search_path = registry, public, pg_temp;