@sassoftware/viya-serverjs 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env CHANGED
@@ -1,24 +1,37 @@
1
1
  APPHOST=localhost
2
2
  # APPENTRY=index.html
3
3
  APPLOC=./public
4
- # VIYA_SERVER=
5
- # VIYA_SERVER=none
4
+ # VIYA_SERVER=your viya server
5
+
6
6
  APPPORT=8080
7
7
  APPNAME=viyaapp
8
- AUTHFLOW=server
8
+ AUTHFLOW=code
9
9
  CLIENTID=viyaapp
10
10
  CLIENTSECRET=jellico
11
+
11
12
  REDIRECT=
12
13
  # APPDIR=./appDir
13
14
  HTTPS=true
14
- VIYA_SERVER=https://viya-00m2kebi2b.engage.sas.com
15
15
 
16
- // browsers do not accept self-signed certs from localhost
16
+
17
+ # Most modern browsers do not accept self-signed certs from localhost
18
+ # Options:
19
+
20
+ # 1. provide signed certificates for localhost
21
+ # 2. Use libraries like mkcert to create temporary trusted certs for localhost
22
+ # 3. Use the app server as a proxy to the Viya server to avoid CORS issues.
23
+ # This requires that the app redirect all Viya API calls to the app server proxy endpoint
24
+ # Users of restaf can simply set the APPENV_PROXY env to TRUE to enable this behavior
25
+ # 4. set USETOKEN to TRUE and use the token in the APPENV object to make the calls
26
+ // either use the proper ssl/tsl certs or use the "proxy" method
27
+ // to avoid CORS issues with self-signed certs
17
28
  // so run all apps thru the proxy and call Viya from there
29
+ APPENV_PROXY=false
30
+
18
31
 
19
- APPENV_PROXYSERVER=https://localhost:8080/viyaapp/proxy
20
- USETOKEN=YES
21
- SHOWENV=YES
32
+ # APPENV_PROXYSERVER=true
33
+ # USETOKEN=true
34
+ SHOWENV=true
22
35
 
23
36
  APPENV_XYZ=AA
24
37
  APPENV_BAD=
package/.env.server CHANGED
@@ -1,12 +1,14 @@
1
1
  APPHOST=localhost
2
2
  APPENTRY=index.html
3
3
  APPLOC=./public
4
- VIYA_SERVER=
5
- APPENV_PROXYSERVER=https://localhost:8080/appBuilder/proxy
4
+ # viya-server set preset
5
+ # VIYA_SERVER=
6
+ APPENV_PROXYSERVER=https://localhost:8080/viyaapp/proxy
7
+
6
8
  APPPORT=8080
7
- APPNAME=appBuilder
9
+ APPNAME=viyaapp
8
10
  AUTHFLOW=server
9
- CLIENTID=appbuilder
11
+ CLIENTID=viyaapp
10
12
  CLIENTSECRET=jellico
11
13
  REDIRECT=/new
12
14
  # APPDIR=./appDir
package/Dockerfile CHANGED
@@ -11,6 +11,7 @@ ENV APPHOST=0.0.0.0
11
11
  ENV PORT=8080
12
12
  EXPOSE 8080
13
13
  ENV HTTPS=true
14
+ ENV VIYA_SERVER=
14
15
  # ENV APPSERVERLEVEL=v2
15
16
  #######################################################################
16
17
  # You can override these(but in container leave APPHOST as shown below)
@@ -20,20 +21,19 @@ ENV HTTPS=true
20
21
  # ENV APPPORT=8080
21
22
 
22
23
  ENV APPNAME=viyaapp
23
- # ENV AUTHFLOW=
24
+ ENV AUTHFLOW=server
24
25
  ENV CLIENTID=viyaapp
25
- ENV CLIENTSECRET=jellico
26
+ ENV CLIENTSECRET=
26
27
  # ENV HAPIDEBUG=NO
27
28
  # ENV LOGLEVEL=info
28
29
  # ENV USETOKEN=YES
29
30
 
31
+ # specify ssl/tls cert and key in a folder
32
+ # example below
33
+ ENV SSLCERT=c:/Users/kumar/.tls
30
34
  #sample setup for creating a temporary cert and key
31
35
  ENV TLS_CREATE="C:US,ST:NC,L:Cary,O:SAS Institute,OU:STO,CN:localhost"
32
36
 
33
- # You can specify your own cet and key
34
- # ENV TLS_CRT=./tls/tls.crt
35
- # ENV TLS_KEY=./tls/tls.key
36
-
37
37
  # Samesite specification
38
38
  ENV SAMESITE=None,secure
39
39
 
package/README.md CHANGED
@@ -1,99 +1,67 @@
1
- # `Application servers for use with SAS Viya`
1
+ # `Application server for use with SAS Viya`
2
2
 
3
- This package has two servers:
3
+ viya-serverjs is a app server designed to support user written SAS Viya applications. The applications can be written using any framework.
4
4
 
5
- 1. viya-appserverjs - Use this for developing an app server for web applications(see packages/appjs)
5
+ Key features:
6
6
 
7
- 2. viya-apiserverjs - Use this to develop rest api servers(see packages/apijs)
7
+ 1. Handles authentication
8
+ 2. Extendable with additional end points
8
9
 
9
10
  ## Usage
10
11
 
11
- Specify it as a dependency in your package.json just as you do with other dependencies
12
-
13
12
  Use npx command to start the server
14
13
 
15
14
  ```sh
16
- npx @sassoftware/viyaappserverjs
15
+ npx @sassoftware/viya-serverjs
17
16
  ```
18
17
 
19
18
  ## `Basic configuration`
20
19
 
21
- 1. Set the default settings in Dockerfile. This will ensure these are set when you build containers.
22
- 2. The defaults can be overriden using environment variables.
20
+ Configure the server using a .env file
23
21
 
24
22
  ### `Sample env file`
25
23
 
26
- When running on a non-docker environment, you can use a .env
24
+ >[Note] Sample values shown below. See Advanced section for other ways to configure the server
27
25
 
28
26
  ```env
29
- VIYA_SERVER=<your viya server>
30
- APPHOST=localhost < can also be dns name of your server. ex: viyaiscool.unx.sas.com>
31
- APPPORT=5000 <any port of your choice>
32
- APPNAME=viyaapp
33
-
34
- CLIENTID=viyaapp
35
- CLIENTSECRET=secret
36
- ```
37
-
38
- ### `Sample Dockerfile`
39
27
 
40
- ```env
41
- FROM node:12.16.1-alpine
42
- LABEL maintainer="your email"
43
- WORKDIR /usr/src/app
44
- COPY . .
45
- RUN npm install
46
- # RUN npm run build (if you have to build something)
47
- EXPOSE 8080
48
- ENV APPHOST=0.0.0.0
28
+ # Base setup
29
+ # With the configuration below the app server url will be
30
+ # https://localhost:8080/viyaapp
31
+ #
32
+ APPHOST=localhost
33
+ APPPORT=8080
34
+ HTTPS=true
35
+ APPNAME=viyaapp
49
36
 
50
- AUTHFLOW=code
37
+ # Most modern browsers will reject self-signed certs from localhost
38
+ # And SAS Viya might also refuse connection.
39
+ # Supply your own SSL/TLS values in a folder. All files in this folder will be used.
40
+ # Options:
41
+ # 1. provide signed certificates for localhost
42
+ # 2. Use libraries like mkcert to create temporary trusted certs for localhost
43
+ # 3. For other options see the Advanced Section
44
+ SSLCERT=./tls
51
45
 
52
- # The following are defaults. Override them as needed
53
- # APPLOC - where the file specified in APPENTRY is
54
- # APPENTRY - the main entry of the application
55
- ENV APPLOC=./public
56
- ENV APPENTRY=index.html
57
- # if your app takes advantage of appenv.js to pass configuration to the web application
58
- # ENV APPENV=appenv.js
46
+ # AUTHENTICATION
47
+ VIYA_SERVER=<viya servrer url>
59
48
 
60
- # See notes below on running with SSL enabled
61
- ENV TLS_CREATE="C:US,ST:NC,L:Cary,O:yourcompany,OU:STO,CN:localhost"
62
- ENV SAMESITE=None,secure
49
+ # By default it uses authorization_code flow
63
50
 
64
- # It is better to set this before invoking the server
65
- ENV NODE_TLS_REJECT_UNAUTHORIZED=0
51
+ CLIENTID=<clientid>
52
+ CLIENTSECRET=<clientSecret if present>
53
+ AUTHFLOW=code|pkce
66
54
 
67
- # set this to YES if you want access to the authentication token in the app
68
- ENV USETOKEN=NO
55
+ ##########################
56
+ # Read the Advanced Section in the README before turning on these options
57
+ #
58
+ APPENV_PROXY=false
59
+ USETOKEN=false
69
60
 
70
- CMD ["npx", "@sassoftware/viya-appserverjs"]
61
+ APPENV_A=somevalue
62
+ APPENV_B=somevalue
71
63
 
72
64
  ```
73
65
 
74
- ### `Running with SSL enabled -- Recommended`
75
-
76
- This is the recommended setting. This will also make browsers like Chrome run with the SAMESITE settings set to Default - your users will thank you.
77
-
78
- Make sure you specify the VIYA_SERVER with a protocol of https.
79
66
 
80
- ### `TLS certificates`
81
67
 
82
- - Option 1: Let server create a temporary unsigned certificate
83
-
84
- ```env
85
- ENV TLS_CREATE=C:US,ST:NC,L:Cary,O:YourCompany,OU:yourgroup,CN:localhost
86
- ```
87
-
88
- - Option 2: Provide your own key and certificate key
89
-
90
- ```env
91
- ENV TLS_KEY=../certs/self/key.pem
92
- ENV TLS_CERT=../certs/self/certificate.pem
93
- ```
94
-
95
- - Option 3: Provide key and certificate as a pfx file
96
-
97
- ```env
98
- ENV TLS_PFX=../certs/sascert/sascert2.pfx
99
- ```
@@ -25,7 +25,6 @@ function _setCookies() {
25
25
  case 0:
26
26
  debugger;
27
27
  credentials = req.auth.credentials;
28
- console.log('setcookies credentials', credentials);
29
28
  debug('setcookie', credentials);
30
29
  if (!(credentials != null && req.auth.error != null)) {
31
30
  _context.n = 1;
package/lib/iService.js CHANGED
@@ -193,6 +193,7 @@ function iService(userRouteTable, useDefault, asset, allAppEnv, serverMode, user
193
193
  redirect: process.env.REDIRECT,
194
194
  clientId: process.env.CLIENTID,
195
195
  clientSecret: process.env.CLIENTSECRET,
196
+ pkce: allAppEnv.LOGONPAYLOAD.pkce,
196
197
  redirectTo: "/".concat(process.env.APPNAME, "/logon"),
197
198
  allAppEnv: allAppEnv,
198
199
  useHapiCookie: true,
@@ -320,22 +321,8 @@ function _getCertificates() {
320
321
  _context2.n = 1;
321
322
  break;
322
323
  }
323
- console.log('ssl CERTIFICATES', tlsdir);
324
- if (fs.existsSync("".concat(tlsdir, "/key.pem")) === true) {
325
- options = {};
326
- options.key = fs.readFileSync("".concat(tlsdir, "/key.pem"), {
327
- encoding: 'utf8'
328
- });
329
- options.cert = fs.readFileSync("".concat(tlsdir, "/crt.pem"), {
330
- encoding: 'utf8'
331
- });
332
- if (fs.existsSync("".concat(tlsdir, "/ca.pem")) === true) {
333
- options.ca = fs.readFileSync("".concat(tlsdir, "/ca.pem"), {
334
- encoding: 'utf8'
335
- });
336
- }
337
- options.rejectUnauthorized = true;
338
- }
324
+ options = readTLS(tlsdir);
325
+ options.rejectUnauthorized = true;
339
326
  _context2.n = 3;
340
327
  break;
341
328
  case 1:
@@ -352,6 +339,26 @@ function _getCertificates() {
352
339
  }));
353
340
  return _getCertificates.apply(this, arguments);
354
341
  }
342
+ function readTLS(tlsdir) {
343
+ console.log("[Note] Using TLS dir: " + tlsdir);
344
+ if (fs.existsSync(tlsdir) === false) {
345
+ console.log("[Warning] Specified TLS dir does not exist: " + tlsdir);
346
+ return null;
347
+ }
348
+ var listOfFiles = fs.readdirSync(tlsdir);
349
+ console.log("[Note] TLS/SSL files found: " + listOfFiles);
350
+ var options = {};
351
+ for (var i = 0; i < listOfFiles.length; i++) {
352
+ var fname = listOfFiles[i];
353
+ var name = tlsdir + '/' + listOfFiles[i];
354
+ var key = fname.split('.')[0];
355
+ options[key] = fs.readFileSync(name, {
356
+ encoding: 'utf8'
357
+ });
358
+ }
359
+ console.log('TLS FILES', Object.keys(options));
360
+ return options;
361
+ }
355
362
  function getTls() {
356
363
  return _getTls.apply(this, arguments);
357
364
  }
package/lib/index.js CHANGED
@@ -29,7 +29,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default":
29
29
  var debug = require("debug")("startup");
30
30
  module.exports = function core(uTable, useDefault, serverMode, customize, swaggerfcn) {
31
31
  var argv = (0, _yargs["default"])((0, _helpers.hideBin)(process.argv)).argv;
32
- var env = argv.env == null ? null : argv.env;
32
+ var env = argv.env == null ? '.env' : argv.env;
33
33
  var appenv = argv.appenv == null ? null : argv.appenv;
34
34
  var docker = argv.docker == null ? null : argv.docker;
35
35
  //process.env.SERVERMODE = serverMode !== null ? "api" : "app";
@@ -95,20 +95,11 @@ function getAllEnv(userData) {
95
95
  console.log('Note: setting host to null');
96
96
  host = null;
97
97
  }
98
-
99
- /*
100
- if (process.env.AUTHTYPE != null) {
101
- process.env.AUTHFLOW = process.env.AUTHTYPE;
102
- }
103
- */
104
-
105
98
  var authflow = trimit("AUTHFLOW");
106
- if (authflow === "authorization_code" || authflow === "code") {
99
+ var pkce = authflow === "pkce" ? true : false;
100
+ if (authflow === "authorization_code" || authflow === "code" || authflow === "server" || authflow === "null" || authflow === "pkce") {
107
101
  authflow = "server";
108
102
  }
109
- if (authflow === null) {
110
- host = null;
111
- }
112
103
  if (host === null) {
113
104
  authflow = null;
114
105
  console.log('Note: setting authflow to null');
@@ -119,7 +110,7 @@ function getAllEnv(userData) {
119
110
  var clientID = trimit("CLIENTID");
120
111
 
121
112
  // eslint-disable-next-line no-unused-vars
122
- var clientSecret = trimit("CLIENTSECRET");
113
+ //let clientSecret = trimit("CLIENTSECRET");
123
114
  var keepAlive = trimit("KEEPALIVE");
124
115
  var appName = trimit("APPNAME");
125
116
  var ns = trimit("NAMESPACE");
@@ -130,6 +121,7 @@ function getAllEnv(userData) {
130
121
  host: host,
131
122
  clientID: clientID,
132
123
  appName: appName,
124
+ pkce: pkce,
133
125
  keepAlive: null,
134
126
  useToken: process.env.USETOKEN,
135
127
  ns: ns,
@@ -186,6 +178,7 @@ function getAllEnv(userData) {
186
178
  }
187
179
  }
188
180
  }
181
+ userData.APPNAME = l.appName;
189
182
  env = {
190
183
  LOGONPAYLOAD: l,
191
184
  APPENV: userData
@@ -79,11 +79,13 @@ function _iSASauth() {
79
79
  provider: provider,
80
80
  password: uuid.v4(),
81
81
  clientId: options.clientId,
82
- clientSecret: options.clientSecret,
82
+ clientSecret: options.clientSecret == null ? '' : options.clientSecret,
83
83
  // isSameSite : options.isSameSite,
84
84
  isSecure: options.isSecure
85
85
  };
86
- // console.log('SASAuth options', bellAuthOptions);
86
+ if (options.pkce === true) {
87
+ bellAuthOptions.pkce = 'S256';
88
+ }
87
89
  debug('belloptions', bellAuthOptions);
88
90
  server.log('SASAuth', bellAuthOptions);
89
91
  _context2.n = 1;
@@ -51,7 +51,6 @@ module.exports = /*#__PURE__*/function () {
51
51
  } else {
52
52
  sid = session.sid;
53
53
  }
54
- console.log('appcookie sid', sid);
55
54
  if (!(sid != null)) {
56
55
  _context.n = 3;
57
56
  break;
@@ -2,6 +2,8 @@
2
2
 
3
3
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
4
4
  var _handlers = require("../handlers");
5
+ var _setContext = _interopRequireDefault(require("./setContext"));
6
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
5
7
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6
8
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
9
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
@@ -43,7 +45,7 @@ module.exports = function setDefaultRoutes(server, options) {
43
45
  strategy: "sas"
44
46
  };
45
47
  }
46
- var getAppb = _handlers.getApp.bind(null, process.env.USETOKEN === "YES" ? options : null);
48
+ var getAppb = _handlers.getApp.bind(null, process.env.USETOKEN != null && process.env.USETOKEN.toUpperCase() === "TRUE" ? options : null);
47
49
  console.log("Default strategy", authDefault);
48
50
  console.log("Logon strategy", authLogon);
49
51
  options.authDefault = authDefault;
@@ -271,6 +273,13 @@ module.exports = function setDefaultRoutes(server, options) {
271
273
  };
272
274
  debug(pr);
273
275
  defaultTable.push(pr);
276
+ // now set pre for all default routes
277
+ defaultTable.forEach(function (r) {
278
+ r.options.pre = [{
279
+ method: _setContext["default"],
280
+ assign: 'context'
281
+ }];
282
+ });
274
283
  var routeTables = uTable !== null ? defaultTable.concat(uTable) : defaultTable;
275
284
  server.route(routeTables);
276
285
  };
@@ -51,6 +51,7 @@ function setupUserRoutes(u, options) {
51
51
  assign: 'context'
52
52
  }]);
53
53
  }
54
+ console.log(rx.options.pre);
54
55
  if (rx.options.auth === true) {
55
56
  rx.options.auth = options.authDefault;
56
57
  } else if (rx.options.auth === 'logon') {
package/mcpServer.js ADDED
@@ -0,0 +1,364 @@
1
+ /*
2
+ * Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ let core = require('./lib/index.js');
6
+ debugger;
7
+ core(getCustomHandler, true, 'app', null);
8
+
9
+ function getCustomHandler() {
10
+ let appName = `/${process.env.APPNAME}`; /* does not have to be this - your choice */
11
+ debugger;
12
+ let routes = [
13
+ {
14
+ method: ["GET"],
15
+ path: "/help",
16
+ options: {
17
+ files: {
18
+ relativeTo: "./public",
19
+ },
20
+ handler: async (req, h) => {
21
+ debugger;
22
+ let hf = 'help.html';
23
+ return h.file(hf);
24
+ },
25
+ auth: false,
26
+ description: "Help",
27
+ notes: "Help",
28
+ tags: ["app"],
29
+ },
30
+ },
31
+ {
32
+ method: ["GET"],
33
+ path: `${appName}/new`,
34
+ options: {
35
+ files: {
36
+ relativeTo: "./public",
37
+ },
38
+ handler: async (req, h) => {
39
+ console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>in new');
40
+ return h.file('index.html');
41
+ },
42
+ // auth: 'logon',
43
+ description: "Create new application",
44
+ notes: "Index file created from env data",
45
+ tags: ["app"],
46
+ },
47
+ }
48
+
49
+ ];
50
+ return routes;
51
+ }
52
+ function customize(key, options) {
53
+ let info = {
54
+ swaggerOptions: {
55
+ info: {
56
+ title: "Test API",
57
+ version: "0.0.1",
58
+ description: "This document was auto-generated at run time",
59
+ },
60
+ documentationPage: true,
61
+ documentationPath: `/${process.env.APPNAME}/documentation`,
62
+ swaggerUI: true,
63
+ swaggerUIPath: `/${process.env.APPNAME}/swaggerui`,
64
+ schemes: ["https", "http"],
65
+ cors: true,
66
+ auth: options.authDefault,
67
+ },
68
+ APPENV: {
69
+ x: 1,
70
+ y: 2,
71
+ },
72
+ };
73
+ let r = info[key];
74
+ return r == null ? {} : r;
75
+ }
76
+
77
+ function getIndex() {
78
+
79
+ let template = `
80
+ <html lang="en">
81
+ <head>
82
+ <meta charset="UTF-8" />
83
+
84
+ <script
85
+ crossorigin
86
+ src="https://unpkg.com/react@16/umd/react.production.min.js"
87
+ ></script>
88
+ <script
89
+ crossorigin
90
+ src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"
91
+ ></script>
92
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
93
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.26.0/polyfill.min.js"></script>
94
+ <script src="https://unpkg.com/@sassoftware/restaf@5.2.4/dist/restaf.js"></script>
95
+ <script src="https://unpkg.com/@sassoftware/restaflib@5.2.4/dist/restaflib.js"></script>
96
+
97
+ <style>
98
+ .container {
99
+ display: flex;
100
+ flex-direction: column;
101
+ flex-wrap: nowrap;
102
+ min-height: 800px;
103
+ }
104
+ .elabel {
105
+ display: inline-block;
106
+
107
+ clear: left;
108
+ width: 250px;
109
+ text-align: right;
110
+ }
111
+ .einput {
112
+ display: inline-block;
113
+ }
114
+ .div1 {
115
+ border: 1px solid black;
116
+ background: lightskyblue;
117
+ }
118
+ .div2 {
119
+ border: 1px solid black;
120
+ background: lightskyblue;
121
+ height: 200px;
122
+ }
123
+ </style>
124
+
125
+ <script>
126
+ let LOGONPAYLOAD = {
127
+ host: "${process.env.VIYA_SERVER}",
128
+ authType: "server",
129
+ appName: "${process.env.APPNAME}",
130
+ };
131
+ </script>
132
+
133
+ <script>
134
+ debugger;
135
+ let store = restaf.initStore({
136
+ casProxy: true});
137
+ debugger; console.log(store.config);
138
+
139
+ let session = null;
140
+ let servers = null;
141
+ let services = null;
142
+ let files = null;
143
+ let reports = null;
144
+ let compute = null;
145
+
146
+ function setup() {
147
+ debugger;
148
+ document.getElementById('output').innerHTML = '...initializing';
149
+
150
+ initSession()
151
+ .then(r => {
152
+ document.getElementById('output').innerHTML = 'ready';
153
+ keepAlive();
154
+ })
155
+ .catch(e => {
156
+
157
+ console.log(e);
158
+ });
159
+ }
160
+
161
+
162
+ async function initSession() {
163
+
164
+ console.log(LOGONPAYLOAD);
165
+ debugger;
166
+ let msg = await store.logon(LOGONPAYLOAD);
167
+ console.log(msg);
168
+
169
+ // let { identities } = await store.addServices('identities');
170
+ let name = 'user';
171
+ // if (identities.links('currentUser') != null) {
172
+ // let c = await store.apiCall(identities.links('currentUser'));
173
+ // name = c.items('id');
174
+ // }
175
+ console.log(name);
176
+ debugger;
177
+ /*
178
+ services = await store.addServices(
179
+ 'files', 'compute', 'casManagement'
180
+ );
181
+ console.log(services.casManagement.links().toJS())
182
+ */
183
+ debugger;
184
+ return 'done';
185
+ }
186
+ function runit(type) {
187
+
188
+
189
+ document.getElementById('output').innerHTML = '...running';
190
+ let testcase;
191
+ switch (type) {
192
+ case 'files': {
193
+ testcase = SASfileService;
194
+ break;
195
+ }
196
+ case 'compute': {
197
+ testcase = dsCompute;
198
+ break;
199
+ }
200
+ case 'cas': {
201
+ testcase = runCas;
202
+ break;
203
+ }
204
+
205
+ case 'spre': {
206
+ testcase= spre;
207
+
208
+ break;
209
+ }
210
+ default: {
211
+ testcase = SASfileService;
212
+ break;
213
+ }
214
+ }
215
+
216
+ testcase(store)
217
+ .then(r => {
218
+ document.getElementById(
219
+ 'output'
220
+ ).innerHTML = JSON.stringify(r, null, 4);
221
+ })
222
+ .catch(err => {
223
+
224
+ document.getElementById(
225
+ 'output'
226
+ ).innerHTML = JSON.stringify(err, null, 4);
227
+ });
228
+ }
229
+ async function noaction() {
230
+ r = {msg: 'redirects completed'};
231
+ return r;
232
+ }
233
+ async function spre(store) {
234
+ let p = {
235
+ method: 'GET',
236
+ url : 'http://localhost:3000/api/test',
237
+ withCredentials: true
238
+ }
239
+ let r = await store.request(p);
240
+ return r.data;
241
+ }
242
+ async function runCas(store) {
243
+
244
+ let {casManagement} = await store.addServices('casManagement');
245
+ let servers = await store.apiCall(
246
+ casManagement.links('servers')
247
+ );
248
+ let serverName = servers.itemsList(0);
249
+ let session = await store.apiCall(
250
+ servers.itemsCmd(serverName, 'createSession')
251
+ );
252
+ let payload = {
253
+ action: 'builtins.echo',
254
+ data: { code: { x: 1 } }
255
+ };
256
+ console.log(JSON.stringify(session.links("execute"), null, 4));
257
+ let r = await store.runAction(session, payload);
258
+ console.log('echo completed');
259
+ await store.apiCall(session.links('delete'));
260
+ return r.items();
261
+ }
262
+
263
+ async function SASfileService(store) {
264
+ debugger;
265
+ let content;
266
+ try {
267
+ debugger;
268
+ let {files} = await store.addServices('files');
269
+ debugger;
270
+ console.log(JSON.stringify(files.links(), null, 4));
271
+ //console.log('items - should be an array of files(empty array is ok)')
272
+ // console.log(files.items().toJS());
273
+ let payload = {
274
+ data: { x: 1, y: 'This was saved earlier in the step' },
275
+ headers: { 'content-type': 'application/json' }
276
+ };
277
+ let createCmd = files.links('create');
278
+ let newFile = await store.apiCall(createCmd, payload);
279
+ debugger;
280
+ console.log(JSON.stringify(newFile.links('content'), null, 4));
281
+ content = await store.apiCall(newFile.links('content'));
282
+
283
+ } catch(err) {
284
+ console.log(JSON.stringify(err, null, 4));
285
+ debugger;
286
+ }
287
+ console.log(content);
288
+ return content.items();
289
+ }
290
+ async function dsCompute(store) {
291
+ let log = null;
292
+ debugger;
293
+ let {compute} = await store.addServices('compute');
294
+ let servers = await store.apiCall(compute.links('servers'));
295
+
296
+ let contexts = await store.apiCall(compute.links('contexts'));
297
+
298
+ // lookup the name of the first context and then use it to get the associated createSession restafLink
299
+ let createSession = contexts.itemsCmd(
300
+ contexts.itemsList(0),
301
+ 'createSession'
302
+ );
303
+ let session = await store.apiCall(createSession);
304
+
305
+ // Now run a simple data step in that session
306
+ let payload = {
307
+ data: {
308
+ code: ["data _null_; do i = 1 to 100; x=1; end; run; "]
309
+ }
310
+ };
311
+
312
+ // Now execute the data step and wait for completion
313
+ let job = await store.apiCall(
314
+ session.links('execute'),
315
+ payload
316
+ );
317
+ let status = await store.jobState(job, null, 5, 2);
318
+
319
+ if (status.data === 'running') {
320
+ throw "ERROR: Job did not complete in allotted time";
321
+ } else {
322
+ switch (status.data) {
323
+ case 'warning':
324
+ console.log("Warning: check your log for warnings");
325
+ break;
326
+ case 'error':
327
+ throw "Please correct errors and rerun program";
328
+ default:
329
+ log = await store.apiCall(status.job.links('log'));
330
+ break;
331
+ }
332
+ }
333
+ return log === null ? status : log.items();
334
+ }
335
+ </script>
336
+ </head>
337
+ <body onload="setup()">
338
+ <h1 id="head">Hi</h1>
339
+ <div>
340
+
341
+ <button onclick="runit('files')">
342
+ Press to make a call to file service
343
+ </button>
344
+ <br />
345
+ <br />
346
+ <button onclick="runit('compute')">
347
+ Press to make a call compute service
348
+ </button>
349
+ <br />
350
+ <br />
351
+ <button onclick="runit('cas')">
352
+ Press to make a call to cas echo
353
+ </button>
354
+ <br />
355
+ <br />
356
+
357
+ <div>
358
+ <pre id="output"></pre>
359
+ </div>
360
+ </body>
361
+ </html>
362
+ `;
363
+ return template;
364
+ }
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@sassoftware/viya-serverjs",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Easy to use app server for SAS Viya applications",
5
5
  "author": "Deva Kumaraswamy <deva.kumar@sas.com>",
6
6
  "license": "Apache-2.0",
7
7
  "main": "./lib/index.js",
8
8
  "bin": {
9
- "@sassoftware/viya-appserverjs": "cli.js"
9
+ "@sassoftware/viya-serverjs": "cli.js"
10
10
  },
11
11
  "keywords": [
12
12
  "restaf",
package/public/index.html CHANGED
@@ -66,10 +66,16 @@
66
66
  <script>
67
67
  debugger;
68
68
  console.log(JSON.stringify(APPENV, null, 4));
69
+ let proxyServer = null;
70
+ if (APPENV.PROXY.toUpperCase() === 'TRUE') {
71
+ proxyServer = `${window.location.protocol}//${window.location.host}/${LOGONPAYLOAD.appName}/proxy`;
72
+
73
+ }
74
+ console.log('Proxy Server = ' + proxyServer);
69
75
  let store = restaf.initStore({
70
76
  casProxy: true,
71
77
  options: {
72
- proxyServer: APPENV.PROXYSERVER,
78
+ proxyServer: proxyServer,
73
79
  httpOptions: null
74
80
  }
75
81
  });
package/server.js CHANGED
@@ -45,6 +45,7 @@ function getCustomHandler() {
45
45
  tags: ["app"],
46
46
  },
47
47
  }
48
+
48
49
  ];
49
50
  return routes;
50
51
  }
@@ -8,7 +8,6 @@ let debug = require('debug')('setcookies');
8
8
  async function setCookies (req, h, options) {
9
9
  debugger;
10
10
  let credentials = req.auth.credentials;
11
- console.log('setcookies credentials', credentials);
12
11
  debug('setcookie', credentials);
13
12
 
14
13
  if (credentials != null && req.auth.error != null) {
package/src/iService.js CHANGED
@@ -165,6 +165,7 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
165
165
  redirect : process.env.REDIRECT,
166
166
  clientId : process.env.CLIENTID,
167
167
  clientSecret : process.env.CLIENTSECRET,
168
+ pkce : allAppEnv.LOGONPAYLOAD.pkce,
168
169
  redirectTo : `/${process.env.APPNAME}/logon`,
169
170
  allAppEnv : allAppEnv,
170
171
  useHapiCookie : true,
@@ -176,7 +177,7 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
176
177
  userInfo : userInfo,
177
178
  https : process.env.HTTPS,
178
179
  authDefault : false, /* set later in setDefaultRoutes */
179
- authLogon : false /* set later in setDefaultRoutes */
180
+ authLogon : false /* set later in setDefaultRoutes */
180
181
 
181
182
  };
182
183
 
@@ -263,16 +264,8 @@ async function getCertificates () {
263
264
  let options = null;
264
265
  let tlsdir = process.env.SSLCERT;
265
266
  if (tlsdir != null && tlsdir.trim().length > 0) {
266
- console.log('ssl CERTIFICATES', tlsdir);
267
- if (fs.existsSync(`${tlsdir}/key.pem`) === true) {
268
- options = {};
269
- options.key = fs.readFileSync(`${tlsdir}/key.pem`, { encoding: 'utf8' });
270
- options.cert = fs.readFileSync(`${tlsdir}/crt.pem`, { encoding: 'utf8' });
271
- if (fs.existsSync(`${tlsdir}/ca.pem`) === true) {
272
- options.ca = fs.readFileSync(`${tlsdir}/ca.pem`, { encoding: 'utf8' });
273
- }
274
- options.rejectUnauthorized= true;
275
- }
267
+ options = readTLS(tlsdir);
268
+ options.rejectUnauthorized= true;
276
269
  } else {
277
270
  console.log('No SSL certificates found, generating self-signed certificates');
278
271
  options = await getTls();
@@ -281,6 +274,27 @@ async function getCertificates () {
281
274
  return options;
282
275
  }
283
276
 
277
+ function readTLS (tlsdir) {
278
+ console.log("[Note] Using TLS dir: " + tlsdir);
279
+ if (fs.existsSync(tlsdir) === false) {
280
+ console.log("[Warning] Specified TLS dir does not exist: " + tlsdir);
281
+ return null;
282
+ }
283
+
284
+ let listOfFiles = fs.readdirSync(tlsdir);
285
+ console.log("[Note] TLS/SSL files found: " + listOfFiles);
286
+ let options = {};
287
+ for(let i=0; i < listOfFiles.length; i++) {
288
+ let fname = listOfFiles[i];
289
+ let name = tlsdir + '/' + listOfFiles[i];
290
+ let key = fname.split('.')[0];
291
+ options[key] = fs.readFileSync(name, { encoding: 'utf8' });
292
+ }
293
+ console.log('TLS FILES', Object.keys(options));
294
+ return options;
295
+
296
+ }
297
+
284
298
  async function getTls () {
285
299
  let options = {
286
300
  keySize : 2048,
package/src/index.js CHANGED
@@ -33,7 +33,7 @@ module.exports = function core(
33
33
  swaggerfcn
34
34
  ) {
35
35
  let argv = yargs(hideBin(process.argv)).argv;
36
- let env = argv.env == null ? null : argv.env;
36
+ let env = argv.env == null ? '.env' : argv.env;
37
37
  let appenv = argv.appenv == null ? null : argv.appenv;
38
38
  let docker = argv.docker == null ? null : argv.docker;
39
39
  //process.env.SERVERMODE = serverMode !== null ? "api" : "app";
@@ -124,20 +124,15 @@ function getAllEnv(userData) {
124
124
  host = null;
125
125
  }
126
126
 
127
- /*
128
- if (process.env.AUTHTYPE != null) {
129
- process.env.AUTHFLOW = process.env.AUTHTYPE;
130
- }
131
- */
132
127
 
133
128
  let authflow = trimit("AUTHFLOW");
134
- if (authflow === "authorization_code" || authflow === "code") {
129
+ let pkce = (authflow === "pkce") ? true : false;
130
+ if (authflow === "authorization_code" || authflow === "code" || authflow === "server" ||
131
+ authflow === "null" || authflow === "pkce") {
135
132
  authflow = "server";
133
+
136
134
  }
137
135
 
138
- if (authflow === null) {
139
- host = null;
140
- }
141
136
 
142
137
  if (host === null) {
143
138
  authflow = null;
@@ -151,7 +146,7 @@ function getAllEnv(userData) {
151
146
  let clientID = trimit("CLIENTID");
152
147
 
153
148
  // eslint-disable-next-line no-unused-vars
154
- let clientSecret = trimit("CLIENTSECRET");
149
+ //let clientSecret = trimit("CLIENTSECRET");
155
150
  let keepAlive = trimit("KEEPALIVE");
156
151
  let appName = trimit("APPNAME");
157
152
  let ns = trimit("NAMESPACE");
@@ -164,6 +159,7 @@ function getAllEnv(userData) {
164
159
  host: host,
165
160
  clientID: clientID,
166
161
  appName: appName,
162
+ pkce: pkce,
167
163
 
168
164
  keepAlive: null,
169
165
  useToken: process.env.USETOKEN,
@@ -230,7 +226,7 @@ for (let key in process.env) {
230
226
  }
231
227
  }
232
228
  }
233
-
229
+ userData.APPNAME = l.appName;
234
230
  env = {
235
231
  LOGONPAYLOAD: l,
236
232
  APPENV: userData,
@@ -65,11 +65,15 @@ async function iSASauth (server, options) {
65
65
  provider : provider,
66
66
  password : uuid.v4(),
67
67
  clientId : options.clientId,
68
- clientSecret: options.clientSecret,
68
+ clientSecret: (options.clientSecret == null) ? '' : options.clientSecret,
69
69
  // isSameSite : options.isSameSite,
70
70
  isSecure : options.isSecure
71
71
  };
72
- // console.log('SASAuth options', bellAuthOptions);
72
+
73
+ if (options.pkce === true) {
74
+ bellAuthOptions.pkce = 'S256';
75
+ }
76
+
73
77
  debug('belloptions', bellAuthOptions);
74
78
  server.log('SASAuth',bellAuthOptions);
75
79
  await server.register(bell);
@@ -32,7 +32,6 @@ module.exports = async function appCookie (server, options){
32
32
  } else {
33
33
  sid = session.sid;
34
34
  }
35
- console.log('appcookie sid', sid);
36
35
  if (sid != null) {
37
36
  credentials = await req.server.app.cache.get(sid);
38
37
  }
@@ -16,6 +16,7 @@
16
16
  *
17
17
  */
18
18
 
19
+
19
20
  import {
20
21
  getApp,
21
22
  getApp2,
@@ -29,6 +30,7 @@ import {
29
30
  reactDev,
30
31
  proxyMapUri,
31
32
  } from "../handlers";
33
+ import setContext from './setContext';
32
34
  let debug = require("debug")("routes");
33
35
  module.exports = function setDefaultRoutes(server, options) {
34
36
  debug("setDefaultRoutes");
@@ -51,7 +53,7 @@ module.exports = function setDefaultRoutes(server, options) {
51
53
  }
52
54
  let getAppb = getApp.bind(
53
55
  null,
54
- process.env.USETOKEN === "YES" ? options : null
56
+ (process.env.USETOKEN != null && process.env.USETOKEN.toUpperCase() === "TRUE") ? options : null
55
57
  );
56
58
 
57
59
  console.log("Default strategy", authDefault);
@@ -249,7 +251,10 @@ module.exports = function setDefaultRoutes(server, options) {
249
251
  };
250
252
  debug(pr);
251
253
  defaultTable.push(pr);
252
-
254
+ // now set pre for all default routes
255
+ defaultTable.forEach((r) => {
256
+ r.options.pre = [{method: setContext, assign: 'context'}];
257
+ });
253
258
  let routeTables =
254
259
  uTable !== null ? defaultTable.concat(uTable) : defaultTable;
255
260
  server.route(routeTables);
@@ -35,6 +35,7 @@ function setupUserRoutes (u, options) {
35
35
  } else{
36
36
  rx.options.pre.push([{method: setContext, assign: 'context'}]);
37
37
  }
38
+ console.log(rx.options.pre);
38
39
  if (rx.options.auth === true) {
39
40
  rx.options.auth = options.authDefault;
40
41
  } else if (rx.options.auth === 'logon') {
package/.env.proxy DELETED
@@ -1,32 +0,0 @@
1
- APPHOST=localhost
2
- # APPENTRY=index.html
3
- APPENTRY=indexProxy.html
4
- APPLOC=./public
5
- VIYA_SERVER=
6
- APPENV_PROXYSERVER=https://localhost:8080/appBuilder/proxy
7
-
8
- APPPORT=8080
9
- APPNAME=appBuilder
10
- AUTHFLOW=server
11
- CLIENTID=appbuilder
12
- CLIENTSECRET=jellico
13
- REDIRECT=
14
-
15
- # APPDIR=./appDir
16
- # HTTPS=true
17
- USELOGON=YES
18
-
19
- USETOKEN=YES
20
- # SHOWENV=YES
21
- # APPENV=
22
- APPENV_XYZ=AA
23
- APPENV_BAD=
24
-
25
- # APPENV_PUPID=NSHOST=https://sas-logon-app.viya.svc.cluster.local
26
-
27
- # NAMESPACE=viya
28
- # TLS_CERT=./certs/tls.crt
29
- # TLS_KEY=./certs/tls.key
30
-
31
-
32
-
package/tls/viyatls.sh DELETED
@@ -1,3 +0,0 @@
1
- kubectl cp $(kubectl get pod | grep "sas-consul-server-0" | awk -F" " '{print $1}'):security/ca.crt ./ca.crt
2
- kubectl cp $(kubectl get pod | grep "sas-consul-server-0" | awk -F" " '{print $1}'):security/tls.crt ./tls.crt
3
- kubectl cp $(kubectl get pod | grep "sas-consul-server-0" | awk -F" " '{print $1}'):security/tls.key ./tls.key