A hook is %included from %mp_include, so its open-code %let runs in that macro's local scope: a %global for the same name inside the hook raises "exists in a local environment", sets SYSCC, and puts the session into syntax check mode - every check after it silently skipped while the step still aborts. The derived hook now sets its (renamed) control variables in the including scope and does not %global them. The RLS hook also referenced rls_msg before assigning it whenever there was no derived conflict, which produced an unresolved-symbol WARNING and SYSCC=4 on every clean rule submission. It is initialised now.
134 lines
4.0 KiB
SAS
134 lines
4.0 KiB
SAS
/**
|
|
@file
|
|
@brief Post Edit Hook script for the MPE_ROW_LEVEL_SECURITY table
|
|
@details Post edit hooks provide additional backend validation for user
|
|
provided data. The incoming dataset is named `work.staging_ds` and is
|
|
provided in mpe_loader.sas.
|
|
|
|
Available macro variables:
|
|
@li DC_LIBREF - The DC control library
|
|
@li LIBREF - The library of the dataset being edited (is assigned)
|
|
@li DS - The dataset being edited
|
|
|
|
This validation checks the incoming row_level_security settings to ensure
|
|
each individual filter is
|
|
|
|
It also checks that EDIT scope security is not being applied to tables
|
|
with a REPLACE loadtype (these are incompatible - see issue #211).
|
|
|
|
<h4> SAS Macros </h4>
|
|
@li dc_assignlib.sas
|
|
@li mf_nobs.sas
|
|
@li mp_abort.sas
|
|
@li mp_filtercheck.sas
|
|
|
|
<h4> Related Macros </h4>
|
|
@li mpe_loader.sas
|
|
|
|
**/
|
|
|
|
|
|
/* REPLACE loadtype is incompatible with row level EDIT security (#211) */
|
|
proc sql noprint;
|
|
create table work.badloadtypes as
|
|
select distinct cats(upcase(a.rls_libref),'.',upcase(a.rls_table)) as libds
|
|
from work.staging_ds(where=(rls_active=1
|
|
and upcase(rls_scope) in ('EDIT','ALL')
|
|
and upcase(_____DELETE__THIS__RECORD_____) ne 'YES')) a
|
|
inner join &dc_libref..mpe_tables(where=(&dc_dttmtfmt. lt tx_to)) b
|
|
on upcase(a.rls_libref)=upcase(b.libref)
|
|
and upcase(a.rls_table)=upcase(b.dsn)
|
|
where upcase(b.loadtype)='REPLACE';
|
|
|
|
%let badloadtypes=0;
|
|
%let badloadtype=;
|
|
data _null_;
|
|
set work.badloadtypes;
|
|
call symputx('badloadtypes',_n_);
|
|
call symputx('badloadtype',libds);
|
|
run;
|
|
|
|
%mp_abort(iftrue=(&badloadtypes ne 0)
|
|
,mac=mpe_row_level_security_postedit
|
|
,msg=%str(Row level EDIT security cannot be applied to table(s) with a%trim(
|
|
) REPLACE loadtype, eg: &badloadtype. Remove the security rule(s) or%trim(
|
|
) change the loadtype.)
|
|
)
|
|
|
|
/* group and subgroup are not used for validation */
|
|
proc sql;
|
|
create table work.batches as
|
|
select distinct upcase(rls_libref) as rls_libref,
|
|
upcase(rls_table) as rls_table,
|
|
rls_group_logic as group_logic,
|
|
rls_subgroup_logic as subgroup_logic,
|
|
rls_subgroup_id as subgroup_id,
|
|
rls_variable_nm as variable_nm,
|
|
rls_operator_nm as operator_nm,
|
|
rls_raw_value as raw_value
|
|
from work.staging_ds
|
|
where rls_active=1
|
|
and upcase(_____DELETE__THIS__RECORD_____) ne 'YES'
|
|
order by rls_libref, rls_table;
|
|
|
|
%let cnt=0;
|
|
data _null_;
|
|
set work.batches;
|
|
by rls_libref rls_table;
|
|
putlog (_all_)(=);
|
|
if last.rls_table then do;
|
|
x+1;
|
|
call symputx(cats('libds',x),cats(rls_libref,'.',rls_table));
|
|
call symputx('cnt',x);
|
|
end;
|
|
run;
|
|
|
|
/* derived variables exist only in the view the EDIT screen reads - a rule on
|
|
one cannot be validated against the physical table, so reject it here rather
|
|
than let mp_filtercheck fail with a variable-not-found error. Applies to
|
|
every scope: the VIEW scope could evaluate the column, but this hook checks
|
|
rules against the table they are submitted for. */
|
|
proc sql;
|
|
create table work.rls_derived as
|
|
select b.rls_libref, b.rls_table, b.variable_nm
|
|
from work.batches b
|
|
inner join &DC_LIBREF..mpe_derived d
|
|
on upcase(d.libref)=b.rls_libref
|
|
and upcase(d.dsn)=b.rls_table
|
|
and upcase(d.var_nm)=upcase(b.variable_nm)
|
|
where &dc_dttmtfmt lt d.tx_to
|
|
and d.active=1;
|
|
quit;
|
|
|
|
%let rls_msg=;
|
|
data _null_;
|
|
set work.rls_derived(obs=1);
|
|
call symputx('rls_msg'
|
|
,cats('MPE_ROW_LEVEL_SECURITY: ',variable_nm
|
|
,' is a derived variable in ',rls_libref,'.',rls_table
|
|
,' - derived variables cannot be used in a row level security rule'));
|
|
run;
|
|
|
|
%mp_abort(iftrue= (0 < %mf_nobs(work.rls_derived))
|
|
,mac=mpe_row_level_security_postedit
|
|
,msg=%superq(rls_msg)
|
|
)
|
|
|
|
%macro quickloop();
|
|
%do i=1 %to &cnt;
|
|
data work.inds&i;
|
|
set work.batches;
|
|
if cats(rls_libref,'.',rls_table)="&&libds&i";
|
|
keep group_logic subgroup_logic subgroup_id variable_nm operator_nm
|
|
raw_value;
|
|
run;
|
|
%dc_assignlib(READ,%scan(&&libds&i,1,.))
|
|
%mp_filtercheck(work.inds&i
|
|
,targetds=&&libds&i
|
|
,outds=work.badrecords
|
|
,abort=YES
|
|
)
|
|
%end;
|
|
%mend quickloop;
|
|
|
|
%quickloop() |