1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-11 06:24:35 +00:00

Compare commits

...

9 Commits

Author SHA1 Message Date
Allan Bowe
f0a5d89016 build.sh build on 2022-02-24:21:33:11 2022-02-24 21:33:11 +00:00
Allan Bowe
07bde4b25c feat: adding consul_token option as parameter in mv_registerclient. Closes #177 2022-02-24 21:16:23 +00:00
Allan Bowe
80b06af581 Merge pull request #176 from sasjs/streamserver
feat: adding SASjs server support to mp_streamfile.sas
2022-02-23 19:02:37 +02:00
Allan Bowe
3c026811e9 feat: adding SASjs server support to mp_streamfile.sas 2022-02-23 17:01:50 +00:00
Allan Bowe
cf547ce7e4 Merge pull request #175 from sasjs/authbranch
fix: tidyup of mm_getauthinfo.sas
2022-02-23 11:43:33 +02:00
Allan Bowe
6952c79899 fix: tidyup of mm_getauthinfo.sas 2022-02-23 09:42:47 +00:00
Allan Bowe
09e3f63da7 Merge pull request #174 from sasjs/stpabortissue
fix: missing dependency in mm_createstp
2022-02-22 13:47:05 +02:00
Allan Bowe
d6956f4122 fix: missing dependency in mm_createstp 2022-02-22 11:46:31 +00:00
Allan Bowe
6fca73e7da fix: adding content type to SASjs server webout response 2022-02-21 00:31:14 +00:00
11 changed files with 356 additions and 218 deletions

1
CNAME Normal file
View File

@@ -0,0 +1 @@
core.sasjs.io

274
all.sas
View File

@@ -11271,6 +11271,7 @@ create table &outds as
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mfs_httpheader.sas
@li mp_binarycopy.sas
@author Allan Bowe
@@ -11304,7 +11305,7 @@ data _null_;
run;
%if &contentype=CSV %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/csv');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
@@ -11315,10 +11316,14 @@ run;
contenttype='application/csv'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/csv)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=EXCEL %then %do;
/* suitable for XLS format */
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/vnd.ms-excel');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
@@ -11329,9 +11334,13 @@ run;
contenttype='application/vnd.ms-excel'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/vnd.ms-excel)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
run;
@@ -11340,15 +11349,21 @@ run;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="image/%lowcase(&contenttype)";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,image/%lowcase(&contenttype))
%end;
%end;
%else %if &contentype=HTML %then %do;
%if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
contenttype="text/html";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,text/html)
%end;
%end;
%else %if &contentype=TEXT %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/text');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
@@ -11359,9 +11374,13 @@ run;
contenttype='application/text'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/text)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
run;
@@ -11370,9 +11389,12 @@ run;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="font/%lowcase(&contenttype)";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,font/%lowcase(&contenttype))
%end;
%end;
%else %if &contentype=XLSX %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
@@ -11385,9 +11407,15 @@ run;
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type
,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=ZIP %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/zip');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
@@ -11398,6 +11426,10 @@ run;
contenttype='application/zip'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/zip)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %do;
%put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!;
@@ -13550,13 +13582,14 @@ run;
The macro is idempotent - if you run it twice, it will only create a folder
once.
usage:
Usage:
%mm_createfolder(path=/some/meta/folder)
@param [in] path= Name of the folder to create.
@param [in] mdebug= set DBG to 1 to disable DEBUG messages
@version 9.4
@author Allan Bowe
@@ -14032,7 +14065,7 @@ filename &frefout temp;
This macro is idempotent - if you run it twice, it will only create an STP
once.
usage (type 1 STP):
Usage (type 1 STP):
%mm_createstp(stpname=MyNewSTP
,filename=mySpecialProgram.sas
@@ -14051,7 +14084,8 @@ filename &frefout temp;
putlog (_all_)(=);
run;
usage (type 2 STP):
Usage (type 2 STP):
%mm_createstp(stpname=MyNewType2STP
,filename=mySpecialProgram.sas
,directory=SASEnvironment/SASCode/STPs
@@ -14094,8 +14128,9 @@ filename &frefout temp;
@li mf_verifymacvars.sas
@li mm_getdirectories.sas
@li mm_updatestpsourcecode.sas
@li mp_dropmembers.sas
@li mm_getservercontexts.sas
@li mp_abort.sas
@li mp_dropmembers.sas
<h4> Related Macros </h4>
@li mm_createwebservice.sas
@@ -15174,17 +15209,22 @@ run;
%mend mm_deletestp;
/**
@file mm_getauthinfo.sas
@brief extracts authentication info
@details usage:
@brief Extracts authentication info for each user in metadata
@details
Usage:
%mm_getauthinfo(outds=auths)
%mm_getauthinfo(outds=auths)
@param outds= the ONE LEVEL work dataset to create
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
@param [out] outds= (mm_getauthinfo) The output dataset to create
<h4> SAS Macros </h4>
@li mm_getobjects.sas
@li mf_getuniquefileref.sas
@li mf_getuniquename.sas
@li mm_getdetails.sas
@li mm_getobjects.sas
@version 9.4
@author Allan Bowe
@@ -15192,67 +15232,69 @@ run;
**/
%macro mm_getauthinfo(outds=mm_getauthinfo
,mdebug=0
)/*/STORE SOURCE*/;
%local prefix fileref;
%let prefix=%substr(%mf_getuniquename(),1,25);
%if %length(&outds)>30 %then %do;
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which
therefore needs to be 30 characters or less;
%return;
%end;
%if %index(&outds,'.')>0 %then %do;
%put %str(ERR)OR: Table &outds should be ONE LEVEL (no library);
%return;
%end;
%mm_getobjects(type=Login,outds=&outds.0)
%mm_getobjects(type=Login,outds=&prefix.0)
%local fileref;
%let fileref=%mf_getuniquefileref();
data _null_;
file &fileref;
set &outds.0 end=last;
set &prefix.0 end=last;
/* run macro */
str=cats('%mm_getdetails(uri=',id,",outattrs=&outds.d",_n_
,",outassocs=&outds.a",_n_,")");
str=cats('%mm_getdetails(uri=',id,",outattrs=&prefix.d",_n_
,",outassocs=&prefix.a",_n_,")");
put str;
/* transpose attributes */
str=cats("proc transpose data=&outds.d",_n_,"(drop=type) out=&outds.da"
str=cats("proc transpose data=&prefix.d",_n_,"(drop=type) out=&prefix.da"
,_n_,"(drop=_name_);var value;id name;run;");
put str;
/* add extra info to attributes */
str=cats("data &outds.da",_n_,";length login_id login_name $256; login_id="
,quote(trim(id)),";set &outds.da",_n_
str=cats("data &prefix.da",_n_,";length login_id login_name $256; login_id="
,quote(trim(id)),";set &prefix.da",_n_
,";login_name=trim(subpad(name,1,256));drop name;run;");
put str;
/* add extra info to associations */
str=cats("data &outds.a",_n_,";length login_id login_name $256; login_id="
str=cats("data &prefix.a",_n_,";length login_id login_name $256; login_id="
,quote(trim(id)),";login_name=",quote(trim(name))
,";set &outds.a",_n_,";run;");
,";set &prefix.a",_n_,";run;");
put str;
if last then do;
/* collate attributes */
str=cats("data &outds._logat; set &outds.da1-&outds.da",_n_,";run;");
str=cats("data &prefix._logat; set &prefix.da1-&prefix.da",_n_,";run;");
put str;
/* collate associations */
str=cats("data &outds._logas; set &outds.a1-&outds.a",_n_,";run;");
str=cats("data &prefix._logas; set &prefix.a1-&prefix.a",_n_,";run;");
put str;
/* tidy up */
str=cats("proc delete data=&outds.da1-&outds.da",_n_,";run;");
str=cats("proc delete data=&prefix.da1-&prefix.da",_n_,";run;");
put str;
str=cats("proc delete data=&outds.d1-&outds.d",_n_,";run;");
str=cats("proc delete data=&prefix.d1-&prefix.d",_n_,";run;");
put str;
str=cats("proc delete data=&outds.a1-&outds.a",_n_,";run;");
str=cats("proc delete data=&prefix.a1-&prefix.a",_n_,";run;");
put str;
end;
run;
%if &mdebug=1 %then %do;
data _null_;
infile &fileref;
if _n_=1 then putlog // "Now executing the following code:" //;
input; putlog _infile_;
run;
%end;
%inc &fileref;
filename &fileref clear;
/* get libraries */
proc sort data=&outds._logas(where=(assoc='Libraries')) out=&outds._temp;
proc sort data=&prefix._logas(where=(assoc='Libraries')) out=&prefix._temp;
by login_id;
data &outds._temp;
set &outds._temp;
data &prefix._temp;
set &prefix._temp;
by login_id;
length library_list $32767;
retain library_list;
@@ -15260,32 +15302,28 @@ data &outds._temp;
else library_list=catx(' !! ',library_list,name);
proc sql;
/* get auth domain */
create table &outds._dom as
create table &prefix._dom as
select login_id,name as domain
from &outds._logas
from &prefix._logas
where assoc='Domain';
create unique index login_id on &outds._dom(login_id);
create unique index login_id on &prefix._dom(login_id);
/* join it all together */
create table &outds._logins as
create table &outds as
select a.*
,c.domain
,b.library_list
from &outds._logat (drop=ishidden lockedby usageversion publictype) a
left join &outds._temp b
from &prefix._logat (drop=ishidden lockedby usageversion publictype) a
left join &prefix._temp b
on a.login_id=b.login_id
left join &outds._dom c
left join &prefix._dom c
on a.login_id=c.login_id;
drop table &outds._temp;
drop table &outds._logat;
drop table &outds._logas;
data _null_;
infile &fileref;
if _n_=1 then putlog // "Now executing the following code:" //;
input; putlog _infile_;
run;
%if &mdebug=0 %then %do;
proc datasets lib=work;
delete &prefix:;
run;
%end;
filename &fileref clear;
%mend mm_getauthinfo;/**
@file
@@ -18585,8 +18623,9 @@ run;
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@li mf_getuser.sas
@li mp_jsonout.sas
@li mfs_httpheader.sas
<h4> Related Macros </h4>
@li mv_webout.sas
@@ -18638,6 +18677,9 @@ run;
/* fix encoding */
OPTIONS NOBOMFILE;
/* set the header */
%mfs_httpheader(Content-type,application/json)
/* setup json */
data _null_;file &fref encoding='utf-8' termstr=lf;
put '{"SYSDATE" : "' "&SYSDATE" '"';
@@ -23094,10 +23136,10 @@ run;
%mend mv_jobwaitfor;/**
@file mv_registerclient.sas
@brief Register Client and Secret (admin task)
@details When building apps on SAS Viya, an client id and secret are sometimes
required. In order to generate them, filesystem access to the Consul Token
is needed (it is not enough to be in the SASAdministrator group in SAS
Environment Manager).
@details When building apps on SAS Viya, a client id and secret are usually
required. In order to generate them, the Consul Token is required. To access
this token, you need to be a system administrator (it is not enough to be in
the SASAdministrator group in SAS Environment Manager).
If you are registering a lot of clients / secrets, you may find it more
convenient to use the [Viya Token Generator]
@@ -23118,51 +23160,56 @@ run;
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* generate random client using consul token as input parameter;
%mv_registerclient(consul_token=12x34sa43v2345n234lasd)
%* generate random client details with all scopes;
%mv_registerclient(scopes=openid *)
%* specific client with just openid scope;
%mv_registerclient(client_id=YourClient
,client_secret=YourSecret
,scopes=openid
)
%* generate random client details with all scopes;
%mv_registerclient(scopes=openid *)
%* generate random client with 90/180 second access/refresh token expiry;
%mv_registerclient(scopes=openid *
,access_token_validity=90
,refresh_token_validity=180
)
@param client_id= The client name. Auto generated if blank.
@param client_secret= Client secret. Auto generated if client is blank.
@param scopes=(openid) List of space-seperated unquoted scopes
@param grant_type=(authorization_code|refresh_token) Valid values are
"password" or "authorization_code" (unquoted)
@param outds=(mv_registerclient) The dataset to contain the registered client
id and secret
@param access_token_validity=(DEFAULT) The duration of validity of the access
token in seconds. A value of DEFAULT will omit the entry (and use system
default)
@param refresh_token_validity=(DEFAULT) The duration of validity of the
@param [in,out] client_id= The client name. Auto generated if blank.
@param [in,out] client_secret= Client secret. Auto generated if client is
blank.
@param [in] consul_token= (0) Provide the actual consul token value here if
using Viya 4 or above.
@param [in] scopes= (openid) List of space-seperated unquoted scopes
@param [in] grant_type= (authorization_code|refresh_token) Valid values are
"password" or "authorization_code" (unquoted). Pipe seperated.
@param [out] outds=(mv_registerclient) The dataset to contain the registered
client id and secret
@param [in] access_token_validity= (DEFAULT) The access token duration in
seconds. A value of DEFAULT will omit the entry (and use system default)
@param [in] refresh_token_validity= (DEFAULT) The duration of validity of the
refresh token in seconds. A value of DEFAULT will omit the entry (and use
system default)
@param name= An optional, human readable name for the client
@param required_user_groups= A list of group names. If a user does not belong
to all the required groups, the user will not be authenticated and no tokens
are issued to this client for that user. If this field is not specified,
authentication and token issuance proceeds normally.
@param autoapprove= During the auth step the user can choose which scope to
apply. Setting this to true will autoapprove all the client scopes.
@param use_session= If true, access tokens issued to this client will be
@param [in] client_name= (DEFAULT) An optional, human readable name for the
client.
@param [in] required_user_groups= A list of group names. If a user does not
belong to all the required groups, the user will not be authenticated and no
tokens are issued to this client for that user. If this field is not
specified, authentication and token issuance proceeds normally.
@param [in] autoapprove= During the auth step the user can choose which scope
to apply. Setting this to true will autoapprove all the client scopes.
@param [in] use_session= If true, access tokens issued to this client will be
associated with an HTTP session and revoked upon logout or time-out.
@param outjson= (_null_) A dataset containing the lines of JSON submitted.
Useful for debugging.
@param [out] outjson= (_null_) A dataset containing the lines of JSON
submitted. Useful for debugging.
@version VIYA V.03.04
@author Allan Bowe, source: https://github.com/sasjs/core
<h4> SAS Macros </h4>
@li mp_abort.sas
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@@ -23174,6 +23221,7 @@ run;
%macro mv_registerclient(client_id=
,client_secret=
,consul_token=0
,client_name=DEFAULT
,scopes=openid
,grant_type=authorization_code|refresh_token
@@ -23185,33 +23233,40 @@ run;
,refresh_token_validity=DEFAULT
,outjson=_null_
);
%local consul_token fname1 fname2 fname3 libref access_token url tokloc;
%local fname1 fname2 fname3 libref access_token url tokloc;
%if client_name=DEFAULT %then %let client_name=
Generated by %mf_getuser() on %sysfunc(datetime(),datetime19.) using SASjs;
Generated by %mf_getuser() (&sysuserid) on %sysfunc(datetime(),datetime19.
) using SASjs;
options noquotelenmax;
/* first, get consul token needed to get client id / secret */
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
%if "&consul_token"="0" %then %do;
/* first, get consul token needed to get client id / secret */
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
%mp_abort(iftrue=(%sysfunc(fileexist(&tokloc))=0)
,mac=&sysmacroname
,msg=%str(Unable to access the consul token at &tokloc)
)
%if %sysfunc(fileexist(&tokloc))=0 %then %do;
%put &sysmacroname: unable to access the consul token at &tokloc;
%put Try passing the value in the consul= macro parameter;
%put See docs: https://core.sasjs.io/mv__registerclient_8sas.html;
%abort;
%end;
%let consul_token=0;
data _null_;
infile "&tokloc";
input token:$64.;
call symputx('consul_token',token);
run;
data _null_;
infile "&tokloc";
input token:$64.;
call symputx('consul_token',token);
run;
%mp_abort(iftrue=("&consul_token"="0")
,mac=&sysmacroname
,msg=%str(Unable to source the consul token from &tokloc)
)
%if "&consul_token"="0" %then %do;
%put &sysmacroname: Unable to source the consul token from &tokloc;
%put It seems your account (&sysuserid) does not have admin rights;
%put Please speak with your platform adminstrator;
%put Docs: https://core.sasjs.io/mv__registerclient_8sas.html;
%abort;
%end;
%end;
%local base_uri; /* location of rest apis */
%let base_uri=%mf_getplatform(VIYARESTAPI);
@@ -23224,6 +23279,9 @@ proc http method='POST' out=&fname1
headers "X-Consul-Token"="&consul_token";
run;
%put &=SYS_PROCHTTP_STATUS_CODE;
%put &=SYS_PROCHTTP_STATUS_PHRASE;
%let libref=%mf_getuniquelibref();
libname &libref JSON fileref=&fname1;

View File

@@ -22,6 +22,7 @@
<h4> SAS Macros </h4>
@li mf_getplatform.sas
@li mfs_httpheader.sas
@li mp_binarycopy.sas
@author Allan Bowe
@@ -55,7 +56,7 @@ data _null_;
run;
%if &contentype=CSV %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/csv');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
@@ -66,10 +67,14 @@ run;
contenttype='application/csv'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/csv)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=EXCEL %then %do;
/* suitable for XLS format */
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/vnd.ms-excel');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
@@ -80,9 +85,13 @@ run;
contenttype='application/vnd.ms-excel'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/vnd.ms-excel)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=GIF or &contentype=JPEG or &contentype=PNG %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type',"image/%lowcase(&contenttype)");
run;
@@ -91,15 +100,21 @@ run;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="image/%lowcase(&contenttype)";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,image/%lowcase(&contenttype))
%end;
%end;
%else %if &contentype=HTML %then %do;
%if &platform=SASVIYA %then %do;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI" name="_webout.json"
contenttype="text/html";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,text/html)
%end;
%end;
%else %if &contentype=TEXT %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/text');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
@@ -110,9 +125,13 @@ run;
contenttype='application/text'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/text)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=WOFF or &contentype=WOFF2 or &contentype=TTF %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type',"font/%lowcase(&contenttype)");
run;
@@ -121,9 +140,12 @@ run;
filename &outref filesrvc parenturi="&SYS_JES_JOB_URI"
contenttype="font/%lowcase(&contenttype)";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,font/%lowcase(&contenttype))
%end;
%end;
%else %if &contentype=XLSX %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
@@ -136,9 +158,15 @@ run;
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type
,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %if &contentype=ZIP %then %do;
%if (&platform=SASMETA and &streamweb=1) or &platform=SASJS %then %do;
%if (&platform=SASMETA and &streamweb=1) %then %do;
data _null_;
rc=stpsrv_header('Content-type','application/zip');
rc=stpsrv_header('Content-disposition',"attachment; filename=&outname");
@@ -149,6 +177,10 @@ run;
contenttype='application/zip'
contentdisp="attachment; filename=&outname";
%end;
%else %if &platform=SASJS %then %do;
%mfs_httpheader(Content-type,application/zip)
%mfs_httpheader(Content-disposition,%str(attachment; filename=&outname))
%end;
%end;
%else %do;
%put %str(ERR)OR: Content Type &contenttype NOT SUPPORTED by &sysmacroname!;

View File

@@ -12,13 +12,14 @@
The macro is idempotent - if you run it twice, it will only create a folder
once.
usage:
Usage:
%mm_createfolder(path=/some/meta/folder)
@param [in] path= Name of the folder to create.
@param [in] mdebug= set DBG to 1 to disable DEBUG messages
@version 9.4
@author Allan Bowe

View File

@@ -12,7 +12,7 @@
This macro is idempotent - if you run it twice, it will only create an STP
once.
usage (type 1 STP):
Usage (type 1 STP):
%mm_createstp(stpname=MyNewSTP
,filename=mySpecialProgram.sas
@@ -31,7 +31,8 @@
putlog (_all_)(=);
run;
usage (type 2 STP):
Usage (type 2 STP):
%mm_createstp(stpname=MyNewType2STP
,filename=mySpecialProgram.sas
,directory=SASEnvironment/SASCode/STPs
@@ -74,8 +75,9 @@
@li mf_verifymacvars.sas
@li mm_getdirectories.sas
@li mm_updatestpsourcecode.sas
@li mp_dropmembers.sas
@li mm_getservercontexts.sas
@li mp_abort.sas
@li mp_dropmembers.sas
<h4> Related Macros </h4>
@li mm_createwebservice.sas

View File

@@ -1,16 +1,21 @@
/**
@file mm_getauthinfo.sas
@brief extracts authentication info
@details usage:
@brief Extracts authentication info for each user in metadata
@details
Usage:
%mm_getauthinfo(outds=auths)
%mm_getauthinfo(outds=auths)
@param outds= the ONE LEVEL work dataset to create
@param [in] mdebug= (0) Set to 1 to enable DEBUG messages and preserve outputs
@param [out] outds= (mm_getauthinfo) The output dataset to create
<h4> SAS Macros </h4>
@li mm_getobjects.sas
@li mf_getuniquefileref.sas
@li mf_getuniquename.sas
@li mm_getdetails.sas
@li mm_getobjects.sas
@version 9.4
@author Allan Bowe
@@ -18,67 +23,69 @@
**/
%macro mm_getauthinfo(outds=mm_getauthinfo
,mdebug=0
)/*/STORE SOURCE*/;
%local prefix fileref;
%let prefix=%substr(%mf_getuniquename(),1,25);
%if %length(&outds)>30 %then %do;
%put %str(ERR)OR: Temp tables are created with the &outds prefix, which
therefore needs to be 30 characters or less;
%return;
%end;
%if %index(&outds,'.')>0 %then %do;
%put %str(ERR)OR: Table &outds should be ONE LEVEL (no library);
%return;
%end;
%mm_getobjects(type=Login,outds=&outds.0)
%mm_getobjects(type=Login,outds=&prefix.0)
%local fileref;
%let fileref=%mf_getuniquefileref();
data _null_;
file &fileref;
set &outds.0 end=last;
set &prefix.0 end=last;
/* run macro */
str=cats('%mm_getdetails(uri=',id,",outattrs=&outds.d",_n_
,",outassocs=&outds.a",_n_,")");
str=cats('%mm_getdetails(uri=',id,",outattrs=&prefix.d",_n_
,",outassocs=&prefix.a",_n_,")");
put str;
/* transpose attributes */
str=cats("proc transpose data=&outds.d",_n_,"(drop=type) out=&outds.da"
str=cats("proc transpose data=&prefix.d",_n_,"(drop=type) out=&prefix.da"
,_n_,"(drop=_name_);var value;id name;run;");
put str;
/* add extra info to attributes */
str=cats("data &outds.da",_n_,";length login_id login_name $256; login_id="
,quote(trim(id)),";set &outds.da",_n_
str=cats("data &prefix.da",_n_,";length login_id login_name $256; login_id="
,quote(trim(id)),";set &prefix.da",_n_
,";login_name=trim(subpad(name,1,256));drop name;run;");
put str;
/* add extra info to associations */
str=cats("data &outds.a",_n_,";length login_id login_name $256; login_id="
str=cats("data &prefix.a",_n_,";length login_id login_name $256; login_id="
,quote(trim(id)),";login_name=",quote(trim(name))
,";set &outds.a",_n_,";run;");
,";set &prefix.a",_n_,";run;");
put str;
if last then do;
/* collate attributes */
str=cats("data &outds._logat; set &outds.da1-&outds.da",_n_,";run;");
str=cats("data &prefix._logat; set &prefix.da1-&prefix.da",_n_,";run;");
put str;
/* collate associations */
str=cats("data &outds._logas; set &outds.a1-&outds.a",_n_,";run;");
str=cats("data &prefix._logas; set &prefix.a1-&prefix.a",_n_,";run;");
put str;
/* tidy up */
str=cats("proc delete data=&outds.da1-&outds.da",_n_,";run;");
str=cats("proc delete data=&prefix.da1-&prefix.da",_n_,";run;");
put str;
str=cats("proc delete data=&outds.d1-&outds.d",_n_,";run;");
str=cats("proc delete data=&prefix.d1-&prefix.d",_n_,";run;");
put str;
str=cats("proc delete data=&outds.a1-&outds.a",_n_,";run;");
str=cats("proc delete data=&prefix.a1-&prefix.a",_n_,";run;");
put str;
end;
run;
%if &mdebug=1 %then %do;
data _null_;
infile &fileref;
if _n_=1 then putlog // "Now executing the following code:" //;
input; putlog _infile_;
run;
%end;
%inc &fileref;
filename &fileref clear;
/* get libraries */
proc sort data=&outds._logas(where=(assoc='Libraries')) out=&outds._temp;
proc sort data=&prefix._logas(where=(assoc='Libraries')) out=&prefix._temp;
by login_id;
data &outds._temp;
set &outds._temp;
data &prefix._temp;
set &prefix._temp;
by login_id;
length library_list $32767;
retain library_list;
@@ -86,31 +93,27 @@ data &outds._temp;
else library_list=catx(' !! ',library_list,name);
proc sql;
/* get auth domain */
create table &outds._dom as
create table &prefix._dom as
select login_id,name as domain
from &outds._logas
from &prefix._logas
where assoc='Domain';
create unique index login_id on &outds._dom(login_id);
create unique index login_id on &prefix._dom(login_id);
/* join it all together */
create table &outds._logins as
create table &outds as
select a.*
,c.domain
,b.library_list
from &outds._logat (drop=ishidden lockedby usageversion publictype) a
left join &outds._temp b
from &prefix._logat (drop=ishidden lockedby usageversion publictype) a
left join &prefix._temp b
on a.login_id=b.login_id
left join &outds._dom c
left join &prefix._dom c
on a.login_id=c.login_id;
drop table &outds._temp;
drop table &outds._logat;
drop table &outds._logas;
data _null_;
infile &fileref;
if _n_=1 then putlog // "Now executing the following code:" //;
input; putlog _infile_;
run;
%if &mdebug=0 %then %do;
proc datasets lib=work;
delete &prefix:;
run;
%end;
filename &fileref clear;
%mend mm_getauthinfo;

2
package-lock.json generated
View File

@@ -7,7 +7,7 @@
"name": "@sasjs/core",
"license": "MIT",
"devDependencies": {
"@sasjs/cli": "3.6.0",
"@sasjs/cli": "^3.6.0",
"@sasjs/core": "4.4.7"
}
},

View File

@@ -33,7 +33,7 @@
"prepare": "git rev-parse --git-dir && git config core.hooksPath ./.git-hooks || true"
},
"devDependencies": {
"@sasjs/cli": "3.6.0",
"@sasjs/cli": "^3.6.0",
"@sasjs/core": "4.4.7"
}
}

View File

@@ -33,8 +33,9 @@
`,"$tablename":{"formats":{"col1":"$CHAR1"},"types":{"COL1":"C"}}`
<h4> SAS Macros </h4>
@li mp_jsonout.sas
@li mf_getuser.sas
@li mp_jsonout.sas
@li mfs_httpheader.sas
<h4> Related Macros </h4>
@li mv_webout.sas
@@ -86,6 +87,9 @@
/* fix encoding */
OPTIONS NOBOMFILE;
/* set the header */
%mfs_httpheader(Content-type,application/json)
/* setup json */
data _null_;file &fref encoding='utf-8' termstr=lf;
put '{"SYSDATE" : "' "&SYSDATE" '"';

View File

@@ -0,0 +1,21 @@
/**
@file
@brief Testing mm_getauthinfo macro
<h4> SAS Macros </h4>
@li mf_existds.sas
@li mm_getauthinfo.sas
@li mp_assertscope.sas
**/
%mp_assertscope(SNAPSHOT)
%mm_getauthinfo(outds=auths)
%mp_assertscope(COMPARE)
%mp_assert(
iftrue=(%mf_existds(work.auths)=1),
desc=Check if the auths dataset was created
)

View File

@@ -1,10 +1,10 @@
/**
@file mv_registerclient.sas
@brief Register Client and Secret (admin task)
@details When building apps on SAS Viya, an client id and secret are sometimes
required. In order to generate them, filesystem access to the Consul Token
is needed (it is not enough to be in the SASAdministrator group in SAS
Environment Manager).
@details When building apps on SAS Viya, a client id and secret are usually
required. In order to generate them, the Consul Token is required. To access
this token, you need to be a system administrator (it is not enough to be in
the SASAdministrator group in SAS Environment Manager).
If you are registering a lot of clients / secrets, you may find it more
convenient to use the [Viya Token Generator]
@@ -25,51 +25,56 @@
"https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* generate random client using consul token as input parameter;
%mv_registerclient(consul_token=12x34sa43v2345n234lasd)
%* generate random client details with all scopes;
%mv_registerclient(scopes=openid *)
%* specific client with just openid scope;
%mv_registerclient(client_id=YourClient
,client_secret=YourSecret
,scopes=openid
)
%* generate random client details with all scopes;
%mv_registerclient(scopes=openid *)
%* generate random client with 90/180 second access/refresh token expiry;
%mv_registerclient(scopes=openid *
,access_token_validity=90
,refresh_token_validity=180
)
@param client_id= The client name. Auto generated if blank.
@param client_secret= Client secret. Auto generated if client is blank.
@param scopes=(openid) List of space-seperated unquoted scopes
@param grant_type=(authorization_code|refresh_token) Valid values are
"password" or "authorization_code" (unquoted)
@param outds=(mv_registerclient) The dataset to contain the registered client
id and secret
@param access_token_validity=(DEFAULT) The duration of validity of the access
token in seconds. A value of DEFAULT will omit the entry (and use system
default)
@param refresh_token_validity=(DEFAULT) The duration of validity of the
@param [in,out] client_id= The client name. Auto generated if blank.
@param [in,out] client_secret= Client secret. Auto generated if client is
blank.
@param [in] consul_token= (0) Provide the actual consul token value here if
using Viya 4 or above.
@param [in] scopes= (openid) List of space-seperated unquoted scopes
@param [in] grant_type= (authorization_code|refresh_token) Valid values are
"password" or "authorization_code" (unquoted). Pipe seperated.
@param [out] outds=(mv_registerclient) The dataset to contain the registered
client id and secret
@param [in] access_token_validity= (DEFAULT) The access token duration in
seconds. A value of DEFAULT will omit the entry (and use system default)
@param [in] refresh_token_validity= (DEFAULT) The duration of validity of the
refresh token in seconds. A value of DEFAULT will omit the entry (and use
system default)
@param name= An optional, human readable name for the client
@param required_user_groups= A list of group names. If a user does not belong
to all the required groups, the user will not be authenticated and no tokens
are issued to this client for that user. If this field is not specified,
authentication and token issuance proceeds normally.
@param autoapprove= During the auth step the user can choose which scope to
apply. Setting this to true will autoapprove all the client scopes.
@param use_session= If true, access tokens issued to this client will be
@param [in] client_name= (DEFAULT) An optional, human readable name for the
client.
@param [in] required_user_groups= A list of group names. If a user does not
belong to all the required groups, the user will not be authenticated and no
tokens are issued to this client for that user. If this field is not
specified, authentication and token issuance proceeds normally.
@param [in] autoapprove= During the auth step the user can choose which scope
to apply. Setting this to true will autoapprove all the client scopes.
@param [in] use_session= If true, access tokens issued to this client will be
associated with an HTTP session and revoked upon logout or time-out.
@param outjson= (_null_) A dataset containing the lines of JSON submitted.
Useful for debugging.
@param [out] outjson= (_null_) A dataset containing the lines of JSON
submitted. Useful for debugging.
@version VIYA V.03.04
@author Allan Bowe, source: https://github.com/sasjs/core
<h4> SAS Macros </h4>
@li mp_abort.sas
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@@ -81,6 +86,7 @@
%macro mv_registerclient(client_id=
,client_secret=
,consul_token=0
,client_name=DEFAULT
,scopes=openid
,grant_type=authorization_code|refresh_token
@@ -92,33 +98,40 @@
,refresh_token_validity=DEFAULT
,outjson=_null_
);
%local consul_token fname1 fname2 fname3 libref access_token url tokloc;
%local fname1 fname2 fname3 libref access_token url tokloc;
%if client_name=DEFAULT %then %let client_name=
Generated by %mf_getuser() on %sysfunc(datetime(),datetime19.) using SASjs;
Generated by %mf_getuser() (&sysuserid) on %sysfunc(datetime(),datetime19.
) using SASjs;
options noquotelenmax;
/* first, get consul token needed to get client id / secret */
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
%if "&consul_token"="0" %then %do;
/* first, get consul token needed to get client id / secret */
%let tokloc=/etc/SASSecurityCertificateFramework/tokens/consul/default;
%let tokloc=%mf_loc(VIYACONFIG)&tokloc/client.token;
%mp_abort(iftrue=(%sysfunc(fileexist(&tokloc))=0)
,mac=&sysmacroname
,msg=%str(Unable to access the consul token at &tokloc)
)
%if %sysfunc(fileexist(&tokloc))=0 %then %do;
%put &sysmacroname: unable to access the consul token at &tokloc;
%put Try passing the value in the consul= macro parameter;
%put See docs: https://core.sasjs.io/mv__registerclient_8sas.html;
%abort;
%end;
%let consul_token=0;
data _null_;
infile "&tokloc";
input token:$64.;
call symputx('consul_token',token);
run;
data _null_;
infile "&tokloc";
input token:$64.;
call symputx('consul_token',token);
run;
%mp_abort(iftrue=("&consul_token"="0")
,mac=&sysmacroname
,msg=%str(Unable to source the consul token from &tokloc)
)
%if "&consul_token"="0" %then %do;
%put &sysmacroname: Unable to source the consul token from &tokloc;
%put It seems your account (&sysuserid) does not have admin rights;
%put Please speak with your platform adminstrator;
%put Docs: https://core.sasjs.io/mv__registerclient_8sas.html;
%abort;
%end;
%end;
%local base_uri; /* location of rest apis */
%let base_uri=%mf_getplatform(VIYARESTAPI);
@@ -131,6 +144,9 @@ proc http method='POST' out=&fname1
headers "X-Consul-Token"="&consul_token";
run;
%put &=SYS_PROCHTTP_STATUS_CODE;
%put &=SYS_PROCHTTP_STATUS_PHRASE;
%let libref=%mf_getuniquelibref();
libname &libref JSON fileref=&fname1;