diff --git a/CHANGELOG.md b/CHANGELOG.md index 93e1315..d3d00f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,13 +10,6 @@ * updating editors/stagedata to address issues in particular viya configurations as described in issue [#33](https://git.datacontroller.io/dc/dc/issues/33) ([94ab949](https://git.datacontroller.io/dc/dc/commit/94ab949df8c75072525751a2156b7a32c2e641dc)) * updating logic for REPLACE loadtype ([1f2ce55](https://git.datacontroller.io/dc/dc/commit/1f2ce55f249f4af56f0cacdec47e69246cd47431)) -## [6.2.1](https://git.datacontroller.io/dc/dc/compare/v6.2.0...v6.2.1) (2023-08-25) - - -### Bug Fixes - -* approve, history and submit pages grouped in review module ([e056ece](https://git.datacontroller.io/dc/dc/commit/e056ece2234ef6aab050f6a5b1f8de633b163d91)) -* updating logic for REPLACE loadtype ([1f2ce55](https://git.datacontroller.io/dc/dc/commit/1f2ce55f249f4af56f0cacdec47e69246cd47431)) # [6.2.0](https://git.datacontroller.io/dc/dc/compare/v6.1.0...v6.2.0) (2023-08-24) diff --git a/sas/sasjs/macros/mpe_accesscheck.sas b/sas/sasjs/macros/mpe_accesscheck.sas index 7c92197..9c4555c 100755 --- a/sas/sasjs/macros/mpe_accesscheck.sas +++ b/sas/sasjs/macros/mpe_accesscheck.sas @@ -1,21 +1,33 @@ /** @file - @brief Checks the level of access a user has to the MP Editor + @brief Checks group access level for a table or library @details In order for a user to be able to EDIT or APPROVE a table they must - be in a metadata group that has been granted access to that table in the - &mpelib..mpe_security table. Alternatively, they may be in the - &mpeadmins group (has overall access). + be in a group that has been granted access to that table in the + MPE_SECURITY table. Alternatively, they may be in the &mpeadmins + group (which has full access to everything). + + @param [in] base_table The base table to check for + @param [in] user= The user for which the access level should be returned. If + not provided, the mf_user() result is used instead. + @param [in] access_level= (APPROVE) access_level (per MPE_SECURITY) reqd. + Valid values: + @li EDIT + @li APPROVE + @li VIEW + @param [in] cntl_lib_var= (MPELIB) The name of a global macro variable that + contains the libref in which the MPE_SECURITY table is stored + @param [out] outds= (MED_ACCESSCHECK) Output WORK table containing all the + groups for which the user is granted the particular ACCESS_LEVEL.

SAS Macros

@li mp_abort.sas + @li mf_getuniquename.sas + @li mf_getuser.sas @li mf_verifymacvars.sas @li mpe_getgroups.sas - @li mp_dropmembers.sas - @param [in] access_level= access_level (per &mpelib..mp_editor_security) reqd - - @returns outds A table containing all the groups that user is a member of, - which are granted the access_level requested. +

Related Macros

+ @li mpe_accesscheck.test.sas @version 9.2 @author 4GL Apps Ltd @@ -25,65 +37,64 @@ **/ %macro mpe_accesscheck( - base_table /* base table to check for */ + base_table ,outds=med_accesscheck /* WORK table to contain access details */ ,user= /* metadata user to check for */ ,access_level=APPROVE + ,cntl_lib_var=MPELIB ); %if &user= %then %let user=%mf_getuser(); - %if %index(&outds,.) %then %do; - %local lib ds; - %let lib=%scan(&outds,1,.); - %let ds=%scan(&outds,2,.); - %if %upcase(&lib) ne WORK %then %do; - %mp_abort(msg=outds should be a WORK table - ,mac=mpe_accesscheck); - %end; - %end; - %else %let ds=&outds; + %mp_abort( + iftrue=(%index(&outds,.)>0 and %upcase(%scan(&outds,1,.)) ne WORK) + ,mac=mpe_accesscheck + ,msg=%str(outds should be a WORK table) + ) %mp_abort( iftrue=(%mf_verifymacvars(base_table user access_level)=0) - ,mac=bitemporal_dataloader - ,msg=%str(Missing base_table/user access_level) + ,mac=mpe_accesscheck + ,msg=%str(Missing base_table/user access_level variables) ) - /* ensure any existing table is dropped */ - %mp_dropmembers(&ds) + /* make unique temp table vars */ + %local tempds1 tempds2; + %let tempds1=%mf_getuniquename(prefix=usergroups); + %let tempds2=%mf_getuniquename(prefix=tablegroups); - /* create a new table for temp use */ - data; run; - %local tempds; %let tempds=&syslast; - - /* overwrite with the list of groups */ - %mpe_getgroups(user=&user,outds=&tempds); + /* get list of user groups */ + %mpe_getgroups(user=&user,outds=&tempds1) + /* get list of groups with access for that table */ + proc sql; + create table &tempds2 as + select distinct sas_group + from &&&cntl_lib_var...mpe_security + where &dc_dttmtfmt. lt tx_to + and access_level="&access_level" + and ( + (libref="%scan(&base_table,1,.)" and upcase(dsn)="%scan(&base_table,2,.)") + or (libref="%scan(&base_table,1,.)" and dsn="*ALL*") + or (libref="*ALL*") + ); %if &_debug ge 131 %then %do; data _null_; - set &tempds; + set &tempds1; + putlog (_all_)(=); + run; + data _null_; + set &tempds2; putlog (_all_)(=); run; %end; proc sql; create table &outds as - select * from &tempds + select * from &tempds1 where groupname="&mpeadmins" - or groupname in - (select sas_group from &mpelib..mpe_security - where &dc_dttmtfmt. lt tx_to - and access_level="&access_level" - & ( - (libref="%scan(&base_table,1,.)" and dsn="%scan(&base_table,2,.)") - or (libref="%scan(&base_table,1,.)" and dsn="*ALL*") - or (libref="*ALL*") - ) - ); + or groupname in (select * from &tempds2); -%put base_table=&base_table; -%put libref=%scan(&base_table,1,.); -%put dsn=%scan(&base_table,2,.); -%put access_level=&access_level; + %put &sysmacroname: base_table=&base_table; + %put &sysmacroname: access_level=&access_level; %mend mpe_accesscheck; diff --git a/sas/sasjs/macros/mpe_accesscheck.test.sas b/sas/sasjs/macros/mpe_accesscheck.test.sas new file mode 100644 index 0000000..7deaa72 --- /dev/null +++ b/sas/sasjs/macros/mpe_accesscheck.test.sas @@ -0,0 +1,68 @@ +/** + @file + @brief Testing mpe_accesscheck macro + @details Checking functionality of mpe_accesscheck.sas macro + + +

SAS Macros

+ @li mf_getuniquename.sas + @li mf_getuser.sas + @li mp_assertdsobs.sas + @li mpe_getgroups.sas + @li mpe_accesscheck.sas + + + @author 4GL Apps Ltd + @copyright 4GL Apps Ltd. This code may only be used within Data Controller + and may not be re-distributed or re-sold without the express permission of + 4GL Apps Ltd. + +**/ + +/* get the groups this user is actually a member of */ +%mpe_getgroups(user=%mf_getuser(),outds=work.groups) + +data _null_; + set work.groups; + call symputx('groupname',groupname); +run; + +/* create demo MPE_SECURITY table */ +data work.mpe_security; + if 0 then set &dc_libref..mpe_security; + do access_level='EDIT','APPROVE','VIEW','SIGNOFF','AUDIT'; + LIBREF='SOMELIB'; + DSN='SOMEDS'; + sas_group="&groupname"; + tx_from=0; + tx_to='31dec4999:23:59:59'dt; + output; + end; +run; + +%let WRK=WORK; + +%mpe_accesscheck( + SOMELIB.SOMEDS + ,outds=work.test1 + ,access_level=APPROVE + ,cntl_lib_var=WRK +) +%mp_assertdsobs(work.test1, + desc=Test 1 - One record returned, + test=EQUALS 1, + outds=work.test_results +) + +%mpe_accesscheck( + SOMELIB.INVALID + ,outds=work.test2 + ,access_level=APPROVE + ,cntl_lib_var=WRK +) +%mp_assertdsobs(work.test2, + desc=Test 12 - 0 records returned, + test=EQUALS 0, + outds=work.test_results +) + diff --git a/sas/sasjs/macros/mpe_makedata.sas b/sas/sasjs/macros/mpe_makedata.sas index ef45a8e..2a734ac 100644 --- a/sas/sasjs/macros/mpe_makedata.sas +++ b/sas/sasjs/macros/mpe_makedata.sas @@ -1014,7 +1014,8 @@ insert into &lib..mpe_selectbox set ,buskey='LIBREF DSN ACCESS_LEVEL SAS_GROUP' ,var_txfrom='TX_FROM' ,var_txto='TX_TO' - ,notes='Shows which metadata groups can edit which tables' + ,notes='Determines which groups can view/edit/approve which tables' + ,post_edit_hook='services/hooks/mpe_security_postedit' ; insert into &lib..mpe_tables set tx_from=0 @@ -1351,6 +1352,15 @@ insert into &lib..MPE_VALIDATIONS set ,rule_value='UPCASE' ,rule_active=1 ,tx_to='31DEC5999:23:59:59'dt; +insert into &lib..MPE_VALIDATIONS set + tx_from=0 + ,base_lib="&lib" + ,base_ds="MPE_SECURITY" + ,base_col="LIBREF" + ,rule_type='CASE' + ,rule_value="UPCASE" + ,rule_active=1 + ,tx_to='31DEC5999:23:59:59'dt; insert into &lib..MPE_VALIDATIONS set tx_from=0 ,base_lib="&lib" @@ -1369,6 +1379,15 @@ insert into &lib..MPE_VALIDATIONS set ,rule_value="&lib..MPE_TABLES.LIBREF" ,rule_active=1 ,tx_to='31DEC5999:23:59:59'dt; +insert into &lib..MPE_VALIDATIONS set + tx_from=0 + ,base_lib="&lib" + ,base_ds="MPE_SECURITY" + ,base_col="DSN" + ,rule_type='CASE' + ,rule_value="UPCASE" + ,rule_active=1 + ,tx_to='31DEC5999:23:59:59'dt; insert into &lib..MPE_VALIDATIONS set tx_from=0 ,base_lib="&lib" diff --git a/sas/sasjs/services/hooks/mpe_security_postedit.sas b/sas/sasjs/services/hooks/mpe_security_postedit.sas new file mode 100644 index 0000000..7697bc4 --- /dev/null +++ b/sas/sasjs/services/hooks/mpe_security_postedit.sas @@ -0,0 +1,34 @@ +/** + @file + @brief Post Edit Hook script for the MPE_SECURITY table + @details Post edit hooks provide additional backend validation against + user-sourced data. The incoming dataset is always `work.staging_ds` and this + file is included from the mpe_loader.sas macro. + + Available (at runtime) macro variables: + @li DC_LIBREF - The DC control library for your site + @li LIBREF - The library of the dataset being edited (is assigned) + @li DS - The dataset being edited + + +**/ + +/* ensure upcase and check access level values*/ +%let errval=0; +%let errmsg=; +data work.staging_ds; + set work.staging_ds; + LIBREF=upcase(LIBREF); + DSN=upcase(DSN); + ACCESS_LEVEL=upcase(ACCESS_LEVEL); + if ACCESS_LEVEL not in ('EDIT','APPROVE','VIEW','SIGNOFF','AUDIT') then do; + putlog "ERR" +(-1) "OR: invalid ACCESS_LEVEL - " access_level; + call symputx('errval',1); + call symputx('errmsg',"Invalid ACCESS_LEVEL: "!!access_level); + end; +run; + +%mp_abort(iftrue=(&errval=1) + ,mac=mpe_security_postedit.sas + ,msg=%str(&errmsg) +) \ No newline at end of file