@sassoftware/viya-serverjs 0.5.0 → 0.5.2

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/src/iService.js CHANGED
@@ -27,15 +27,15 @@ let NodeCache = require("node-cache-promise");
27
27
  let Vision = require('@hapi/vision');
28
28
  let inert = require('@hapi/inert');
29
29
  let selfsigned = require('selfsigned');
30
- import { response } from '@hapi/inert/lib/file';
30
+
31
31
  import setupAuth from './plugins/setupAuth';
32
32
 
33
33
  let os = require('os');
34
34
 
35
- function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, userInfo) {
35
+ function iService(userRouteTable, useDefault, asset, allAppEnv, serverMode, userInfo) {
36
36
  // process.env.APPHOST_ADDR = process.env.APPHOST;
37
37
  const init = async () => {
38
-
38
+
39
39
  if (process.env.APPHOST === '*') {
40
40
  process.env.APPHOST = os.hostname();
41
41
  }
@@ -48,7 +48,7 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
48
48
  }
49
49
  let isSameSite = 'None';
50
50
  let isSecure = false;
51
-
51
+
52
52
  if (process.env.SAMESITE != null) {
53
53
  let [s1, s2] = process.env.SAMESITE.split(',');
54
54
  isSameSite = s1;
@@ -57,7 +57,7 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
57
57
  isSecure = false;
58
58
  }
59
59
  }
60
-
60
+
61
61
 
62
62
  let sConfig = {
63
63
  port: process.env.APPPORT,
@@ -65,19 +65,19 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
65
65
 
66
66
  state: {
67
67
  isSameSite: isSameSite,
68
- isSecure : isSecure,
68
+ isSecure: isSecure,
69
69
 
70
70
  },
71
-
71
+
72
72
 
73
73
  routes: {
74
74
  payload: {
75
75
  maxBytes: maxBytes
76
76
  },
77
77
  cors: {
78
- origin : ['*'],
78
+ origin: ['*'],
79
79
  credentials: true,
80
-
80
+
81
81
  "headers": ["Accept", "Authorization", "Content-Type", "If-None-Match", "Accept-language"]
82
82
  /*
83
83
  'Access-Control-Allow-Methods': ['GET', 'POST', 'OPTIONS'],
@@ -85,13 +85,13 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
85
85
  additionalExposedHeaders : ['location'],
86
86
  */
87
87
  }
88
-
88
+
89
89
  },
90
90
  };
91
91
  if (process.env.HAPIDEBUG === 'YES') {
92
92
  sConfig.debug = { request: '*' };
93
93
  }
94
- debug(JSON.stringify(sConfig, null,4));
94
+ debug(JSON.stringify(sConfig, null, 4));
95
95
  if (process.env.HTTPS === 'true') {
96
96
  sConfig.tls = await getCertificates();
97
97
  debug('Setup of SSL certificates completed');
@@ -99,7 +99,7 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
99
99
  debug('Running with no SSL certificates');
100
100
  }
101
101
  if (asset !== null) {
102
- sConfig.routes.files= { relativeTo: asset };
102
+ sConfig.routes.files = { relativeTo: asset };
103
103
  }
104
104
 
105
105
  debug2(
@@ -117,10 +117,10 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
117
117
  */
118
118
 
119
119
  let nodeCacheOptions = {
120
- stdTTL : 24*60*60*1000,
121
- checkPeriod : 3600,
120
+ stdTTL: 24 * 60 * 60 * 1000,
121
+ checkPeriod: 3600,
122
122
  errorOnMissing: true,
123
- useClones : false,
123
+ useClones: false,
124
124
  deleteOnExpire: true,
125
125
  };
126
126
  let storeCache = new NodeCache(nodeCacheOptions);
@@ -128,9 +128,9 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
128
128
 
129
129
  // common plugins
130
130
  let visionOptions = {
131
- engines : { html: require('handlebars') },
131
+ engines: { html: require('handlebars') },
132
132
  relativeTo: __dirname,
133
- path : '.',
133
+ path: '.',
134
134
  };
135
135
  await hapiServer.register(Vision);
136
136
  hapiServer.views(visionOptions);
@@ -140,7 +140,7 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
140
140
  }
141
141
  // register H202 for proxy handling
142
142
  // https://hapi.dev/module/h2o2/api/?v=10.0.1
143
-
143
+
144
144
  await hapiServer.register(H202);
145
145
  /*
146
146
  await hapiServer.register({
@@ -151,85 +151,87 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
151
151
  },
152
152
  });
153
153
  */
154
-
154
+
155
+ //
155
156
  // setup authentication related plugins
156
-
157
+
157
158
  let options = {
158
- serverMode : serverMode,
159
- authFlow : process.env.AUTHFLOW,
160
- host : process.env.VIYA_SERVER,
161
- isSameSite : isSameSite,
162
- isSecure : isSecure,
163
- ns : (allAppEnv.LOGONPAYLOAD != null) ? allAppEnv.LOGONPAYLOAD.ns : null,
164
- nsHost : (allAppEnv.LOGONPAYLOAD != null) ? allAppEnv.LOGONPAYLOAD.nsHost : null,
165
- redirect : process.env.REDIRECT,
166
- clientId : process.env.CLIENTID,
167
- clientSecret : process.env.CLIENTSECRET,
168
- pkce : allAppEnv.LOGONPAYLOAD.pkce,
169
- redirectTo : `/${process.env.APPNAME}/logon`,
170
- allAppEnv : allAppEnv,
171
- useHapiCookie : true,
172
- appName : process.env.APPNAME,
173
- appHost : process.env.APPHOST,
174
- appPort : process.env.APPPORT,
159
+ serverMode: serverMode,
160
+ authFlow: process.env.AUTHFLOW,
161
+ host: process.env.VIYA_SERVER,
162
+ useLogon: (process.env.USELOGON != null && process.env.USELOGON.toUpperCase() === 'FALSE') ? false : true,
163
+ isSameSite: isSameSite,
164
+ isSecure: isSecure,
165
+ ns: (allAppEnv.LOGONPAYLOAD != null) ? allAppEnv.LOGONPAYLOAD.ns : null,
166
+ nsHost: (allAppEnv.LOGONPAYLOAD != null) ? allAppEnv.LOGONPAYLOAD.nsHost : null,
167
+ redirect: process.env.REDIRECT,
168
+ clientId: process.env.CLIENTID,
169
+ clientSecret: process.env.CLIENTSECRET,
170
+ pkce: allAppEnv.LOGONPAYLOAD.pkce,
171
+ redirectTo: `/${process.env.APPNAME}/logon`,
172
+ allAppEnv: allAppEnv,
173
+ useHapiCookie: true,
174
+ appName: process.env.APPNAME,
175
+ appHost: process.env.APPHOST,
176
+ appPort: process.env.APPPORT,
175
177
  userRouteTable: userRouteTable,
176
- useDefault : useDefault, /* not used - left here for potential reuse */
177
- userInfo : userInfo,
178
- https : process.env.HTTPS,
179
- authDefault : false, /* set later in setDefaultRoutes */
180
- authLogon : false /* set later in setDefaultRoutes */
178
+ useDefault: useDefault, /* not used - left here for potential reuse */
179
+ userInfo: userInfo,
180
+ https: process.env.HTTPS,
181
+ authDefault: false, /* set later in setDefaultRoutes */
182
+ authLogon: false /* set later in setDefaultRoutes */
181
183
 
182
184
  };
183
-
184
- debug2('Options',options);
185
+
186
+ debug2('Options', options);
185
187
  if (process.env.AUTHFLOW != null) {
186
- await setupAuth(hapiServer, options);
187
- if (process.env.PREAUTH === 'YES') {
188
+ await setupAuth(hapiServer, options);
189
+ if (process.env.PREAUTH === 'YES') {
188
190
  console.log('Preauth enabled');
189
191
  hapiServer.ext('onPreAuth', (request, h) => {
190
192
  debugger;
191
193
  if (!request.auth.isAuthenticated && !request.path.startsWith(`/login`)) {
192
- const redirectTo = `${request.path}?${new URLSearchParams(request.query).toString()}`;
193
- console.log('Redirect to login', {redirectTo});
194
- debugger;
195
- return h.redirect(`/login`).takeover();
194
+ const redirectTo = `${request.path}?${new URLSearchParams(request.query).toString()}`;
195
+ console.log('Redirect to login', { redirectTo });
196
+ debugger;
197
+ return h.redirect(`/login`).takeover();
196
198
  }
197
199
  return h.continue;
198
200
  });
199
201
  }
200
202
  }
201
203
  console.log('Plugin', process.env.PLUGIN);
202
-
203
- if (process.env.PLUGIN === 'hapi-swagger' && serverMode ==='api') {
204
- let swaggerOptions = {
204
+
205
+ if (process.env.PLUGIN === 'hapi-swagger' && serverMode === 'api') {
206
+ let swaggerOptions = {
205
207
  "info": {
206
- "title" : `API for ${process.env.APPNAME}`,
207
- "version" : "0.0.1",
208
+ "title": `API for ${process.env.APPNAME}`,
209
+ "version": "0.0.1",
208
210
  "description": "This document was auto-generated at run time"
209
211
  },
210
- "schemes" : ["http", "https"],
211
- "cors" : true,
212
- "debug" : true,
213
- "jsonPath" : `/${options.appName}/swagger.json`,
214
- "jsonRoutePath" : `/${options.appName}/swagger.json`,
215
- "documentationPage": true,
216
- "documentationPath": `/${options.appName}/documentation`,
217
- "swaggerUI" : true,
218
- "swaggerUIPath" : `/${options.appName}/swaggerui`,
219
- auth : options.authDefault
220
- };
212
+ "schemes": ["http", "https"],
213
+ "cors": true,
214
+ "debug": true,
215
+ "jsonPath": `/${options.appName}/swagger.json`,
216
+ "jsonRoutePath": `/${options.appName}/swagger.json`,
217
+ "documentationPage": true,
218
+ "documentationPath": `/${options.appName}/documentation`,
219
+ "swaggerUI": true,
220
+ "swaggerUIPath": `/${options.appName}/swaggerui`,
221
+ auth: options.authDefault
222
+ };
221
223
 
222
224
  if (userInfo != null) {
223
- let override = userInfo(options, 'SWAGGEROPTIONS');
224
- swaggerOptions = {...swaggerOptions, ...override};
225
+ let override = userInfo(options, 'SWAGGEROPTIONS');
226
+ swaggerOptions = { ...swaggerOptions, ...override };
225
227
  }
226
-
227
- debug('Swagger Options:' ,swaggerOptions);
228
+
229
+ debug('Swagger Options:', swaggerOptions);
228
230
  await hapiServer.register({ plugin: serverMode, options: swaggerOptions });
229
231
  } else if (process.env.PLUGIN == 'hapi-openapi' && serverMode === 'api') {
230
232
  console.log('hapi-openapi', 'coming soon');
231
- }
232
-
233
+ }
234
+
233
235
 
234
236
  //
235
237
  // Start server
@@ -245,7 +247,7 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
245
247
  options.serverMode === 'app'
246
248
  ? `Visit ${hh}/${process.env.APPNAME} to access application`
247
249
  : `Visit ${hh}/${process.env.APPNAME}/api to access swagger`;
248
- console.log('\x1b[1m%s\x1b[0m',msg);
250
+ console.log('\x1b[1m%s\x1b[0m', msg);
249
251
  console.log('NOTE: If running in container use the exported port');
250
252
  process.env.APPSERVER = `${hh}/${process.env.APPNAME}`;
251
253
  process.env.HEALTH = 'true';
@@ -259,100 +261,121 @@ function iService (userRouteTable, useDefault, asset, allAppEnv, serverMode, use
259
261
  init();
260
262
  }
261
263
 
262
- async function getCertificates () {
264
+ async function getCertificates() {
263
265
 
264
266
  let options = null;
265
267
  let tlsdir = process.env.SSLCERT;
266
- if (tlsdir != null && tlsdir.trim().length > 0) {
268
+ console.log('Reading SSL certificates from ', tlsdir);
269
+ if (tlsdir != null && tlsdir.trim().length > 0) {
267
270
  options = readTLS(tlsdir);
268
- options.rejectUnauthorized= true;
271
+ options.rejectUnauthorized = true;
269
272
  } else {
270
273
  console.log('No SSL certificates found, generating self-signed certificates');
271
274
  options = await getTls();
272
- options.rejectUnauthorized= false;
275
+ options.rejectUnauthorized = false;
273
276
  }
274
277
  return options;
275
278
  }
276
279
 
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
-
280
+ function readTLS(tlsdir) {
281
+ console.log("[Note] Using TLS dir: " + tlsdir);
282
+ if (fs.existsSync(tlsdir) === false) {
283
+ console.log("[Warning] Specified TLS dir does not exist: " + tlsdir);
284
+ return null;
285
+ }
286
+
287
+ let listOfFiles = fs.readdirSync(tlsdir);
288
+ console.log("[Note] TLS/SSL files found: " + listOfFiles);
289
+ let options = {};
290
+ for (let i = 0; i < listOfFiles.length; i++) {
291
+ let fname = listOfFiles[i];
292
+ let name = tlsdir + '/' + listOfFiles[i];
293
+ let key = fname.split('.')[0];
294
+ options[key] = fs.readFileSync(name, { encoding: 'utf8' });
295
+ }
296
+ console.log('TLS FILES', Object.keys(options));
297
+ return options;
298
+
296
299
  }
297
300
 
298
- async function getTls () {
301
+ async function getTls() {
299
302
  let options = {
300
- keySize : 2048,
301
- days : 360,
302
- algorithm : "sha256",
303
+ keySize: 2048,
304
+ days: 360,
305
+ algorithm: "sha256",
303
306
  clientCertificate: true,
304
- extensions : {},
307
+ extensions: {},
305
308
  };
306
309
  let subjt = process.env.TLS_CREATE.replaceAll('"', '').trim();
307
- let subj = subjt.split(',');
308
-
310
+ let subj = subjt.split(',');
311
+
309
312
  let d = {};
310
313
  subj.map(c => {
311
314
  let r = c.split(':');
312
- d[ r[ 0 ] ] = r[ 1 ];
313
- return { value: r[ 1 ] };
315
+ d[r[0]] = r[1];
316
+ return { value: r[1] };
314
317
  });
315
318
 
316
319
  // TLS_CREATE=C:US,ST:NC,L:Cary,O:SAS Institute,OU:STO,CN:localhost,ALT:na.sas.com
317
320
  let attr = [
318
321
  {
319
- name : 'commonName',
322
+ name: 'commonName',
320
323
  value: d.CN /*process.env.APPHOST*/,
321
324
  },
322
325
  {
323
- name : 'countryName',
326
+ name: 'countryName',
324
327
  value: d.C
325
328
  }, {
326
329
  shortName: 'ST',
327
- value : d.ST
330
+ value: d.ST
328
331
  }, {
329
- name : 'localityName',
332
+ name: 'localityName',
330
333
  value: d.L,
331
334
  }, {
332
- name : 'organizationName',
335
+ name: 'organizationName',
333
336
  value: d.O
334
337
  },
335
338
  {
336
339
  shortName: 'OU',
337
- value : d.OU
340
+ value: d.OU
338
341
  }
339
342
  ];
343
+ /*
344
+ options.extensions.altNames = [
345
+ // { type: 6, value: `http://${process.env.APPHOST}:${process.env.APPPORT}/${process.env.APPNAME}` },
346
+ { type: 6, value: `https://${process.env.APPHOST}:${process.env.APPPORT}/${process.env.APPNAME}` },
347
+ { type: 6, value: `https://${process.env.APPHOST}:${process.env.APPPORT}/${process.env.APPNAME}/api` },
348
+ { type: 6, value: `https://${process.env.APPHOST}:${process.env.APPPORT}/${process.env.APPNAME}/logon` },
349
+ { type: 6, value: `https://${process.env.APPHOST}/${process.env.APPNAME}` },
350
+ { type: 6, value: `https://${process.env.APPHOST}/${process.env.APPNAME}/api` },
351
+ { type: 6, value: `https://${process.env.APPHOST}/${process.env.APPNAME}/logon` },
352
+ ];
353
+
354
+ options.extensions.altNames = [
355
+ { type: 2, value: 'localhost' }, // DNS
356
+ { type: 7, ip: '127.0.0.1' }, // IPv4
357
+ { type: 7, ip: '::1' } // IPv6
358
+ ];
359
+ */
360
+ options.extensions = [
361
+ {
362
+ name: 'subjectAltName',
363
+ altNames: [
364
+ { type: 2, value: 'localhost' }, // DNS
365
+ { type: 7, ip: '127.0.0.1' }, // IPv4
366
+ { type: 7, ip: '::1' } // IPv6
367
+
368
+ ]
369
+ }
340
370
 
341
- options.extensions.altNames = [
342
- // { type: 6, value: `http://${process.env.APPHOST}:${process.env.APPPORT}/${process.env.APPNAME}` },
343
- { type: 6, value: `https://${process.env.APPHOST}:${process.env.APPPORT}/${process.env.APPNAME}` },
344
- { type: 6, value: `https://${process.env.APPHOST}:${process.env.APPPORT}/${process.env.APPNAME}/api` },
345
- { type: 6, value: `https://${process.env.APPHOST}:${process.env.APPPORT}/${process.env.APPNAME}/logon` },
346
- { type: 6, value: `https://${process.env.APPHOST}/${process.env.APPNAME}` },
347
- { type: 6, value: `https://${process.env.APPHOST}/${process.env.APPNAME}/api` },
348
- { type: 6, value: `https://${process.env.APPHOST}/${process.env.APPNAME}/logon` },
349
371
  ];
350
- debug('tls options ', JSON.stringify(options, null,4));
372
+ console.log('tls options ', JSON.stringify(options, null, 4));
351
373
  let pems = selfsigned.generate(attr, options);
352
374
  let tls = {
353
375
  cert: pems.cert,
354
- key : pems.private
376
+ key: pems.private
355
377
  };
378
+ console.log('Self-signed certificates created', tls);
356
379
  return tls;
357
380
 
358
381
 
@@ -19,7 +19,7 @@
19
19
 
20
20
  async function setContext (req,h){
21
21
  let credentials = req.auth.credentials;
22
-
22
+ console.log('+++++++++++++++++++setContext', credentials != null);
23
23
  let context = {
24
24
  path : req.path,
25
25
  params : req.params,
@@ -29,6 +29,7 @@ async function setContext (req,h){
29
29
  token : (credentials != null) ? `bearer ${credentials.token}` : null,
30
30
  host : process.env.VIYA_SERVER
31
31
  };
32
+
32
33
  return context;
33
34
  }
34
35
  export default setContext;
@@ -73,15 +73,14 @@ module.exports = function setDefaultRoutes(server, options) {
73
73
  path: `${appName}/logon`,
74
74
  options: {
75
75
 
76
- auth: (options.authFlow === "server") ?
77
- { mode: "try", strategy: "sas" } : null,
76
+ auth: /*authLogon*/ (options.authFlow === "server") ? { mode: "try", strategy: "sas" } : null,
78
77
  //https://futurestud.io/tutorials/hapi-redirect-to-previous-page-after-login
79
78
  // set auth to null on all protected routes
80
79
  plugins: {
81
80
  "hapi-auth-cookie": { redirectTo: false },
82
81
  },
83
82
  handler: async (req, h) => {
84
- debug('logonhandler', req.auth.credentials);
83
+ console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>in logon');
85
84
  return await logon(req, h);
86
85
  }
87
86
  },
@@ -91,10 +90,14 @@ module.exports = function setDefaultRoutes(server, options) {
91
90
  path: `${appName}`,
92
91
 
93
92
  options: {
94
- auth: (process.env.USELOGON === 'YES') ? null : options.serverMode === "app" ? authLogon : authDefault,
95
- handler: getAppb,
96
- },
97
- },
93
+ //auth: (process.env.USELOGON.toUpperCase() === 'TRUE') ? null : options.serverMode === "app" ? authLogon : authDefault,
94
+ auth: null,
95
+ handler: async (req,h) => {
96
+ console.log(`>>>>>>>>>>>>>>>>>>>>>>>in ${appName}`);
97
+ return getAppb(req, h);
98
+ }
99
+ }
100
+ },
98
101
 
99
102
  {
100
103
  method: ["GET"],
@@ -254,6 +257,7 @@ module.exports = function setDefaultRoutes(server, options) {
254
257
  // now set pre for all default routes
255
258
  defaultTable.forEach((r) => {
256
259
  r.options.pre = [{method: setContext, assign: 'context'}];
260
+ console.log,('Setting pre for route', r.path,r.options.pre);
257
261
  });
258
262
  let routeTables =
259
263
  uTable !== null ? defaultTable.concat(uTable) : defaultTable;
@@ -25,11 +25,7 @@ function setupUserRoutes (u, options) {
25
25
  let ux = (typeof u === 'function') ? u() : u;
26
26
  let routes = ux.map(rx => {
27
27
  //let rx = {...r};
28
- /* change it to options */
29
- if (rx.config != null) {
30
- rx.options = {...rx.config};
31
- delete rx.config;
32
- }
28
+
33
29
  if (rx.options.pre == null) {
34
30
  rx.options.pre = [{method: setContext, assign: 'context'}];
35
31
  } else{
@@ -41,7 +37,7 @@ function setupUserRoutes (u, options) {
41
37
  } else if (rx.options.auth === 'logon') {
42
38
  rx.options.auth = options.authLogon;
43
39
  }
44
-
40
+ console.log('route auth', rx.options.auth);
45
41
  return rx;
46
42
  });
47
43
  return routes;
package/testca.js ADDED
@@ -0,0 +1,10 @@
1
+ const https = require('https');
2
+ const url = 'https://localhost:8080/mcp'; // your mkcert HTTPS endpoint
3
+
4
+ https.get(url, { rejectUnauthorized: true }, (res) => {
5
+ console.log('✅ SUCCESS: mkcert rootCA is trusted by Node!');
6
+ console.log('Status:', res.statusCode);
7
+ }).on('error', (err) => {
8
+ console.error('❌ FAIL: mkcert rootCA NOT trusted');
9
+ console.error('Error:', err.message);
10
+ });