1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-15 16:14:36 +00:00
Files
core/viya/mv_registerclient.sas

236 lines
7.6 KiB
SAS

/**
@file mv_registerclient.sas
@brief Register Client and Secret (admin task)
@details When building apps on SAS Viya, an client id and secret is required.
This macro will obtain the Consul Token and use that to call the Web Service.
more info: https://developer.sas.com/reference/auth/#register
and: http://proc-x.com/2019/01/authentication-to-sas-viya-a-couple-of-approaches/
The default viyaroot location is /opt/sas/viya/config
Usage:
%* compile macros;
filename mc url "https://raw.githubusercontent.com/sasjs/core/main/all.sas";
%inc mc;
%* 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= list of space-seperated unquoted scopes (default is openid)
@param grant_type= valid values are "password" or "authorization_code" (unquoted)
@param outds= the dataset to contain the registered client id and secret
@param access_token_validity= 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= The duration of validity of the refresh token
in seconds. A value of DEFAULT will omit the entry (and use system default)
@param name= A 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
associated with an HTTP session and revoked upon logout or time-out.
@param outjson= A dataset containing the lines of JSON submitted. Useful
for debugging. Default= _null_.
@version VIYA V.03.04
@author Allan Bowe
@source https://github.com/sasjs/core
<h4> Dependencies </h4>
@li mp_abort.sas
@li mf_getplatform.sas
@li mf_getuniquefileref.sas
@li mf_getuniquelibref.sas
@li mf_loc.sas
@li mf_getquotedstr.sas
@li mf_getuser.sas
**/
%macro mv_registerclient(client_id=
,client_secret=
,client_name=DEFAULT
,scopes=openid
,grant_type=authorization_code|refresh_token
,required_user_groups=
,autoapprove=
,use_session=
,outds=mv_registerclient
,access_token_validity=DEFAULT
,refresh_token_validity=DEFAULT
,outjson=_null_
);
%local consul_token fname1 fname2 fname3 libref access_token url;
%if client_name=DEFAULT %then %let client_name=
Generated by %mf_getuser() on %sysfunc(datetime(),datetime19.) using SASjs;
options noquotelenmax;
/* first, get consul token needed to get client id / secret */
data _null_;
infile "%mf_loc(VIYACONFIG)/etc/SASSecurityCertificateFramework/tokens/consul/default/client.token";
input token:$64.;
call symputx('consul_token',token);
run;
%local base_uri; /* location of rest apis */
%let base_uri=%mf_getplatform(VIYARESTAPI);
/* request the client details */
%let fname1=%mf_getuniquefileref();
proc http method='POST' out=&fname1
url="&base_uri/SASLogon/oauth/clients/consul?callback=false%str(&)serviceId=app";
headers "X-Consul-Token"="&consul_token";
run;
%let libref=%mf_getuniquelibref();
libname &libref JSON fileref=&fname1;
/* extract the token */
data _null_;
set &libref..root;
call symputx('access_token',access_token,'l');
run;
/**
* register the new client
*/
%let fname2=%mf_getuniquefileref();
%if x&client_id.x=xx %then %do;
%let client_id=client_%sysfunc(ranuni(0),hex16.);
%let client_secret=secret_%sysfunc(ranuni(0),hex16.);
%end;
%let scopes=%sysfunc(coalescec(&scopes,openid));
%let scopes=%mf_getquotedstr(&scopes,QUOTE=D,indlm=|);
%let grant_type=%mf_getquotedstr(&grant_type,QUOTE=D,indlm=|);
%let required_user_groups=%mf_getquotedstr(&required_user_groups,QUOTE=D,indlm=|);
data _null_;
file &fname2;
length clientid clientsecret clientname scope grant_types reqd_groups
autoapprove $256.;
clientid='"client_id":'!!quote(trim(symget('client_id')));
clientsecret=',"client_secret":'!!quote(trim(symget('client_secret')));
clientname=',"name":'!!quote(trim(symget('client_name')));
scope=',"scope":['!!symget('scopes')!!']';
grant_types=symget('grant_type');
if grant_types = '""' then grant_types ='';
grant_types=cats(',"authorized_grant_types": [',grant_types,']');
reqd_groups=symget('required_user_groups');
if reqd_groups = '""' then reqd_groups ='';
else reqd_groups=cats(',"required_user_groups":[',reqd_groups,']');
autoapprove=trim(symget('autoapprove'));
if not missing(autoapprove) then autoapprove=cats(',"autoapprove":',autoapprove);
use_session=trim(symget('use_session'));
if not missing(use_session) then use_session=cats(',"use_session":',use_session);
put '{' clientid ;
put clientsecret ;
put clientname;
put scope;
put grant_types;
if not missing(reqd_groups) then put reqd_groups;
put autoapprove;
put use_session;
%if &access_token_validity ne DEFAULT %then %do;
put ',"access_token_validity":' "&access_token_validity";
%end;
%if &refresh_token_validity ne DEFAULT %then %do;
put ',"refresh_token_validity":' "&refresh_token_validity";
%end;
put ',"redirect_uri": "urn:ietf:wg:oauth:2.0:oob"';
put '}';
run;
%let fname3=%mf_getuniquefileref();
proc http method='POST' in=&fname2 out=&fname3
url="&base_uri/SASLogon/oauth/clients";
headers "Content-Type"="application/json"
"Authorization"="Bearer &access_token";
run;
/* show response */
%local err;
%let err=NONE;
data _null_;
infile &fname3;
input;
if _infile_=:'{"err'!!'or":' then do;
length message $32767;
message=scan(_infile_,-2,'"');
call symputx('err',message,'l');
end;
run;
%if "&err" ne "NONE" %then %do;
%put %str(ERR)OR: &err;
%end;
/* prepare url */
%if %index(%superq(grant_type),authorization_code) %then %do;
data _null_;
if symexist('_baseurl') then do;
url=symget('_baseurl');
if subpad(url,length(url)-9,9)='SASStudio'
then url=substr(url,1,length(url)-11);
else url="&systcpiphostname";
end;
else url="&systcpiphostname";
call symputx('url',url);
run;
%end;
%put Please provide the following details to the developer:;
%put ;
%put CLIENT_ID=&client_id;
%put CLIENT_SECRET=&client_secret;
%put GRANT_TYPE=&grant_type;
%put;
%if %index(%superq(grant_type),authorization_code) %then %do;
/* cannot use base_uri here as it includes the protocol which may be incorrect externally */
%put NOTE: The developer must also register below and select 'openid' to get the grant code:;
%put NOTE- ;
%put NOTE- &url/SASLogon/oauth/authorize?client_id=&client_id%str(&)response_type=code;
%put NOTE- ;
%end;
data &outds;
client_id=symget('client_id');
client_secret=symget('client_secret');
error=symget('err');
run;
data &outjson;
infile &fname2;
input;
line=_infile_;
run;
/* clear refs */
filename &fname1 clear;
filename &fname2 clear;
filename &fname3 clear;
libname &libref clear;
%mend;