Loading...
Searching...
No Matches
stagedata.test.3.sas
Go to the documentation of this file.
1/**
2 @file
3 @brief testing stagedata RLS / REPLACE loadtype incompatibility (#211)
4 @details Backend validations should abort when:
5 1. loading row level EDIT security against a table with LOADTYPE=REPLACE
6 2. applying LOADTYPE=REPLACE to a table with row level EDIT security
7
8 The staged loads are aborted by the post edit hooks inside the child
9 service. The abort is recorded as a FAILED load in MPE_LOADS (via
10 mpe_loadfail), so the assertions query MPE_LOADS rather than the child
11 response - a canceled child does not always return a webout or log.
12
13 <h4> SAS Macros </h4>
14 @li mx_execute.sas
15 @li mp_assert.sas
16
17
18**/
19
20%let _program=&appLoc/services/editors/stagedata;
21
22/**
23 * Prep - register a DCTEST table with REPLACE loadtype (direct insert,
24 * bypassing stagedata) and create the physical table
25 */
26data dctest.dc_replace;
27 x=1;
28run;
29proc sql noprint;
30delete from &dc_libref..mpe_tables where libref="DCTEST" and dsn='DC_REPLACE';
31insert into &dc_libref..mpe_tables
32 set tx_from=0
33 ,tx_to='31DEC5999:23:59:59'dt
34 ,libref="DCTEST"
35 ,dsn='DC_REPLACE'
36 ,buskey='X'
37 ,loadtype='REPLACE'
38 ,num_of_approvals_required=1;
39/* ensure the RLS table's post edit hook is registered (matches the
40 * mpe_makedata registration - some estates were initialised before the
41 * hook existed, which would silently skip the test 1 validation) */
42update &dc_libref..mpe_tables
43 set post_edit_hook='services/hooks/mpe_row_level_security_postedit'
44 where upcase(dsn)='MPE_ROW_LEVEL_SECURITY'
45 and post_edit_hook='';
46/* insert an active EDIT rule for DCTEST.WIDEBOY up front - the child
47 * services run in separate (possibly pooled) compute sessions, so the
48 * rule must be committed to the shared filesystem well before the test
49 * 2 child reads it */
50delete from &dc_libref..mpe_row_level_security
51 where rls_libref="DCTEST" and rls_table='WIDEBOY';
52select coalesce(max(rls_rk),0)+1 into: rlsrk
53 from &dc_libref..mpe_row_level_security;
54insert into &dc_libref..mpe_row_level_security
55 set tx_from=0
56 ,tx_to='31DEC5999:23:59:59'dt
57 ,rls_rk=&rlsrk
58 ,rls_scope='EDIT'
59 ,rls_group='SASAdministrators'
60 ,rls_libref='DCTEST'
61 ,rls_table='WIDEBOY'
62 ,rls_group_logic='AND'
63 ,rls_subgroup_logic='OR'
64 ,rls_subgroup_id=0
65 ,rls_variable_nm='ROW_ID'
66 ,rls_operator_nm='NE'
67 ,rls_raw_value='1'
68 ,rls_active=1;
69quit;
70
71/**
72 * Test 1 - submitting an RLS EDIT rule against a REPLACE table should abort
73 */
74%let t1=%sysfunc(datetime());
75data work.sascontroltable;
76 action='LOAD';
77 message='RLS EDIT rule on REPLACE table should abort';
78 libds="&dc_libref..MPE_ROW_LEVEL_SECURITY";
79 output;
80 stop;
81run;
82
83data work.jsdata;
84 length RLS_SCOPE $8 RLS_GROUP $128 RLS_LIBREF $8 RLS_TABLE $32
85 RLS_GROUP_LOGIC $3 RLS_SUBGROUP_LOGIC $3 RLS_VARIABLE_NM $32
86 RLS_OPERATOR_NM $12 RLS_RAW_VALUE $4000
87 _____DELETE__THIS__RECORD_____ $3;
88 RLS_SCOPE='EDIT';
89 RLS_GROUP='SASAdministrators';
90 RLS_LIBREF='DCTEST';
91 RLS_TABLE='DC_REPLACE';
92 RLS_GROUP_LOGIC='AND';
93 RLS_SUBGROUP_LOGIC='OR';
94 RLS_SUBGROUP_ID=0;
95 RLS_VARIABLE_NM='X';
96 RLS_OPERATOR_NM='NE';
97 RLS_RAW_VALUE='1';
98 RLS_ACTIVE=1;
99 _____DELETE__THIS__RECORD_____='No';
100 output;
101run;
102
103%mx_execute(&_program,
104 viyacontext=&defaultcontext,
105 inputdatasets=work.sascontroltable work.jsdata,
106 outlib=web1,
107 outref=wb1,
108 mdebug=&sasjs_mdebug
109)
110
111/* a hook-aborted load is terminated before mpe_loadfail can record a
112 * FAILED status, so the load row stays IN PROGRESS - the abort is
113 * proved by the load NOT completing successfully. Tests in this suite run
114 * in parallel sessions, so match the row for THIS test by its unique
115 * reason_txt (the submitted message) rather than any load in the time
116 * window - an unrelated aborted/IN PROGRESS load would otherwise satisfy
117 * the assertion even if this load completed. The row id (csv_dir/mperef)
118 * is generated inside the aborted child service, which returns no webout,
119 * so reason_txt is the only stable identifier the test controls. */
120%let abort1=0;
121proc sql noprint;
122select count(*) into: abort1
123 from &dc_libref..mpe_loads
124 where processed_dttm>&t1
125 and reason_txt="RLS EDIT rule on REPLACE table should abort"
126 and upcase(status) ne 'SUCCESS';
127quit;
128
129%mp_assert(
130 iftrue=(&abort1>0),
131 desc=Checking RLS EDIT rule is rejected for REPLACE loadtype table (#211),
132 outds=work.test_results
133)
134
135/**
136 * Test 2 - applying REPLACE loadtype to a table with RLS EDIT security
137 * should abort. The active EDIT rule was inserted in the prep section,
138 * long before this child service reads it.
139 */
140%let t2=%sysfunc(datetime());
141
142/* now stage a REPLACE loadtype for DCTEST.WIDEBOY */
143data work.sascontroltable;
144 action='LOAD';
145 message='REPLACE loadtype on RLS-secured table should abort';
146 libds="&dc_libref..MPE_TABLES";
147 output;
148 stop;
149run;
150
151data work.jsdata;
152 set &dc_libref..mpe_tables(where=(libref='DCTEST' and dsn='WIDEBOY'));
153 loadtype='REPLACE';
154 length _____DELETE__THIS__RECORD_____ $3;
155 _____DELETE__THIS__RECORD_____='No';
156run;
157
158%mx_execute(&_program,
159 viyacontext=&defaultcontext,
160 inputdatasets=work.sascontroltable work.jsdata,
161 outlib=web2,
162 outref=wb2,
163 mdebug=&sasjs_mdebug
164)
165
166%let abort2=0;
167proc sql noprint;
168select count(*) into: abort2
169 from &dc_libref..mpe_loads
170 where processed_dttm>&t2
171 and reason_txt="REPLACE loadtype on RLS-secured table should abort"
172 and upcase(status) ne 'SUCCESS';
173quit;
174
175%mp_assert(
176 iftrue=(&abort2>0),
177 desc=Checking REPLACE loadtype is rejected for RLS EDIT-secured table (#211),
178 outds=work.test_results
179)
180
181/**
182 * Test 3 - sanity check: REPLACE loadtype on an unsecured table is fine
183 */
184data work.jsdata;
185 set &dc_libref..mpe_tables(where=(libref='DCTEST' and dsn='WIDEBOY'));
186 loadtype='UPDATE';
187 length _____DELETE__THIS__RECORD_____ $3;
188 _____DELETE__THIS__RECORD_____='No';
189run;
190
191/* remove the RLS rule first (cleanup of test 2) */
192proc sql noprint;
193delete from &dc_libref..mpe_row_level_security
194 where rls_libref="DCTEST" and rls_table='WIDEBOY';
195quit;
196
197%mx_execute(&_program,
198 viyacontext=&defaultcontext,
199 inputdatasets=work.sascontroltable work.jsdata,
200 outlib=web3,
201 mdebug=&sasjs_mdebug
202)
203
204%let status3=0;
205data _null_;
206 set web3.sasparams;
207 putlog (_all_)(=);
208 if status='SUCCESS' then call symputx('status3',1);
209run;
210
211%mp_assert(
212 iftrue=(&status3=1),
213 desc=Checking non-REPLACE loadtype still stages successfully after cleanup,
214 outds=work.test_results
215)
216
217/* final cleanup - remove the REPLACE table registration */
218proc sql noprint;
219delete from &dc_libref..mpe_tables where libref="DCTEST" and dsn='DC_REPLACE';
220quit;