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