@steedos/accounts 2.2.52-beta.7 → 2.2.53-beta.3

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.
Files changed (99) hide show
  1. package/LICENSE.txt +2 -5
  2. package/lib/core/index.js +34 -28
  3. package/lib/core/index.js.map +1 -1
  4. package/lib/database-mongo/mongo.js +105 -101
  5. package/lib/database-mongo/mongo.js.map +1 -1
  6. package/lib/index.js +10 -20
  7. package/lib/index.js.map +1 -1
  8. package/lib/mail.js +5 -5
  9. package/lib/mail.js.map +1 -1
  10. package/lib/password/accounts-password.js +55 -42
  11. package/lib/password/accounts-password.js.map +1 -1
  12. package/lib/password/index.js +1 -1
  13. package/lib/password/index.js.map +1 -1
  14. package/lib/password/utils/encryption.js +3 -3
  15. package/lib/rest-express/endpoints/authorize.js +2 -2
  16. package/lib/rest-express/endpoints/get-user.js +2 -2
  17. package/lib/rest-express/endpoints/impersonate.js +2 -2
  18. package/lib/rest-express/endpoints/initServer.js +2 -2
  19. package/lib/rest-express/endpoints/login.js +2 -2
  20. package/lib/rest-express/endpoints/logout.js +2 -2
  21. package/lib/rest-express/endpoints/oauth/provider-callback.js +3 -3
  22. package/lib/rest-express/endpoints/oauth/provider-callback.js.map +1 -1
  23. package/lib/rest-express/endpoints/password/change-password.js +2 -2
  24. package/lib/rest-express/endpoints/password/register.js +2 -2
  25. package/lib/rest-express/endpoints/password/reset.js +4 -4
  26. package/lib/rest-express/endpoints/password/two-factor.js +6 -6
  27. package/lib/rest-express/endpoints/password/verify-email.js +6 -6
  28. package/lib/rest-express/endpoints/password/verify.js +4 -4
  29. package/lib/rest-express/endpoints/put-user-name.js +2 -2
  30. package/lib/rest-express/endpoints/refresh-access-token.js +2 -2
  31. package/lib/rest-express/endpoints/service-authenticate.js +2 -2
  32. package/lib/rest-express/endpoints/spaces.js +2 -2
  33. package/lib/rest-express/endpoints/steedos/create-tenant.js +2 -2
  34. package/lib/rest-express/endpoints/steedos/get-tenant.js +2 -4
  35. package/lib/rest-express/endpoints/steedos/get-tenant.js.map +1 -1
  36. package/lib/rest-express/endpoints/steedos/settings.js +9 -4
  37. package/lib/rest-express/endpoints/steedos/settings.js.map +1 -1
  38. package/lib/rest-express/endpoints/update-session.js +2 -2
  39. package/lib/rest-express/express-middleware.js +1 -1
  40. package/lib/rest-express/express-middleware.js.map +1 -1
  41. package/lib/rest-express/user-loader.js +2 -2
  42. package/lib/rest-express/utils/steedos-auth.js +2 -2
  43. package/lib/rest-express/utils/steedos-auth.js.map +1 -1
  44. package/lib/rest-express/utils/users.js +4 -4
  45. package/lib/server/accounts-server.js +56 -30
  46. package/lib/server/accounts-server.js.map +1 -1
  47. package/lib/server/utils/email.js +2 -2
  48. package/lib/types/index.js +14 -14
  49. package/lib/types/index.js.map +1 -1
  50. package/package.json +20 -20
  51. package/src/core/index.ts +12 -5
  52. package/src/database-mongo/mongo.ts +29 -26
  53. package/src/index.ts +6 -16
  54. package/src/mail.ts +3 -3
  55. package/src/password/accounts-password.ts +18 -6
  56. package/src/rest-express/endpoints/steedos/get-tenant.ts +0 -2
  57. package/src/rest-express/endpoints/steedos/settings.ts +15 -1
  58. package/src/server/accounts-server.ts +26 -3
  59. package/src/server/types/jwt-data.ts +10 -1
  60. package/src/types/types/connection-informations.ts +9 -0
  61. package/lib/oauth2/client.js +0 -1
  62. package/lib/oauth2/client.js.map +0 -1
  63. package/lib/oauth2/config.js +0 -14
  64. package/lib/oauth2/config.js.map +0 -1
  65. package/lib/oauth2/consent.js +0 -192
  66. package/lib/oauth2/consent.js.map +0 -1
  67. package/lib/oauth2/login.js +0 -166
  68. package/lib/oauth2/login.js.map +0 -1
  69. package/lib/oauth2/logout.js +0 -60
  70. package/lib/oauth2/logout.js.map +0 -1
  71. package/lib/oauth2/stub/oidc-cert.js +0 -67
  72. package/lib/oauth2/stub/oidc-cert.js.map +0 -1
  73. package/lib/saml-idp/config.js +0 -82
  74. package/lib/saml-idp/config.js.map +0 -1
  75. package/lib/saml-idp/connectedApps.js +0 -20
  76. package/lib/saml-idp/connectedApps.js.map +0 -1
  77. package/lib/saml-idp/express-middleware.js +0 -684
  78. package/lib/saml-idp/express-middleware.js.map +0 -1
  79. package/lib/saml-idp/index.js +0 -13
  80. package/lib/saml-idp/index.js.map +0 -1
  81. package/lib/saml-idp/simpleProfileMapper.js +0 -75
  82. package/lib/saml-idp/simpleProfileMapper.js.map +0 -1
  83. package/src/oauth2/client.ts +0 -0
  84. package/src/oauth2/config.ts +0 -15
  85. package/src/oauth2/consent.ts +0 -208
  86. package/src/oauth2/login.ts +0 -179
  87. package/src/oauth2/logout.ts +0 -66
  88. package/src/oauth2/stub/oidc-cert.ts +0 -86
  89. package/src/saml-idp/config.js +0 -85
  90. package/src/saml-idp/connectedApps.ts +0 -16
  91. package/src/saml-idp/express-middleware.js +0 -811
  92. package/src/saml-idp/index.ts +0 -14
  93. package/src/saml-idp/simpleProfileMapper.js +0 -82
  94. package/src/saml-idp/views/error.hbs +0 -11
  95. package/src/saml-idp/views/layout.hbs +0 -72
  96. package/src/saml-idp/views/samlresponse.hbs +0 -20
  97. package/src/saml-idp/views/settings.hbs +0 -175
  98. package/src/saml-idp/views/user.hbs +0 -261
  99. package/views/oauth2/consent.pug +0 -141
@@ -1,811 +0,0 @@
1
-
2
- /**
3
- * Module dependencies.
4
- */
5
-
6
- const chalk = require('chalk'),
7
- express = require('express'),
8
- os = require('os'),
9
- fs = require('fs'),
10
- http = require('http'),
11
- https = require('https'),
12
- path = require('path'),
13
- extend = require('extend'),
14
- hbs = require('hbs'),
15
- logger = require('morgan'),
16
- bodyParser = require('body-parser'),
17
- session = require('express-session'),
18
- yargs = require('yargs/yargs'),
19
- xmlFormat = require('xml-formatter'),
20
- samlp = require('samlp'),
21
- Parser = require('xmldom').DOMParser,
22
- SessionParticipants = require('samlp/lib/sessionParticipants'),
23
- SimpleProfileMapper = require('./simpleProfileMapper.js');
24
-
25
- /**
26
- * Globals
27
- */
28
-
29
- const IDP_PATHS = {
30
- SSO: '/idp/sso',
31
- SLO: '/idp/slo',
32
- METADATA: '/metadata',
33
- SIGN_IN: '/signin',
34
- SIGN_OUT: '/signout',
35
- SETTINGS: '/settings'
36
- }
37
- const CERT_OPTIONS = [
38
- 'cert',
39
- 'key',
40
- 'encryptionCert',
41
- 'encryptionPublicKey',
42
- 'httpsPrivateKey',
43
- 'httpsCert',
44
- ];
45
- const WILDCARD_ADDRESSES = ['0.0.0.0', '::'];
46
- const UNDEFINED_VALUE = 'None';
47
- const CRYPT_TYPES = {
48
- certificate: /-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----/,
49
- 'RSA private key': /-----BEGIN RSA PRIVATE KEY-----\n[^-]*\n-----END RSA PRIVATE KEY-----/,
50
- 'public key': /-----BEGIN PUBLIC KEY-----\n[^-]*\n-----END PUBLIC KEY-----/,
51
- };
52
- const KEY_CERT_HELP_TEXT = dedent(chalk`
53
- To generate a key/cert pair for the IdP, run the following command:
54
-
55
- {gray openssl req -x509 -new -newkey rsa:2048 -nodes \
56
- -subj '/C=US/ST=California/L=San Francisco/O=JankyCo/CN=Test Identity Provider' \
57
- -keyout idp-private-key.pem \
58
- -out idp-public-cert.pem -days 7300}`
59
- );
60
-
61
- function matchesCertType(value, type) {
62
- return CRYPT_TYPES[type] && CRYPT_TYPES[type].test(value);
63
- }
64
-
65
- function resolveFilePath(filePath) {
66
-
67
- if (filePath.startsWith('saml-idp/')) {
68
- // Allows file path options to files included in this package, like config.js
69
- const resolvedPath = require.resolve(filePath.replace(/^saml\-idp\//, `${__dirname}/`));
70
- return fs.existsSync(resolvedPath) && resolvedPath;
71
- }
72
- var possiblePath;
73
- if (fs.existsSync(filePath)) {
74
- return filePath;
75
- }
76
- if (filePath.startsWith('~/')) {
77
- possiblePath = path.resolve(process.env.HOME, filePath.slice(2));
78
- if (fs.existsSync(possiblePath)) {
79
- return possiblePath;
80
- } else {
81
- // for ~/ paths, don't try to resolve further
82
- return filePath;
83
- }
84
- }
85
- return ['.', __dirname]
86
- .map(base => path.resolve(base, filePath))
87
- .find(possiblePath => fs.existsSync(possiblePath));
88
- }
89
-
90
- function makeCertFileCoercer(type, description, helpText) {
91
- return function certFileCoercer(value) {
92
- if (matchesCertType(value, type)) {
93
- return value;
94
- }
95
-
96
- const filePath = resolveFilePath(value);
97
- if (filePath) {
98
- return fs.readFileSync(filePath)
99
- }
100
- throw new Error(
101
- chalk`{red Invalid / missing {bold ${description}}} - {yellow not a valid crypt key/cert or file path}${helpText ? '\n' + helpText : ''}`
102
- )
103
- };
104
- }
105
-
106
- function getHashCode(str) {
107
- var hash = 0;
108
- if (str.length == 0) return hash;
109
- for (i = 0; i < str.length; i++) {
110
- char = str.charCodeAt(i);
111
- hash = ((hash<<5)-hash)+char;
112
- hash = hash & hash; // Convert to 32bit integer
113
- }
114
- return hash;
115
- }
116
-
117
- function dedent(str) {
118
- // Reduce the indentation of all lines by the indentation of the first line
119
- const match = str.match(/^\n?( +)/);
120
- if (!match) {
121
- return str;
122
- }
123
- const indentRe = new RegExp(`\n${match[1]}`, 'g');
124
- return str.replace(indentRe, '\n').replace(/^\n/, '');
125
- }
126
-
127
- function formatOptionValue(key, value) {
128
- if (typeof value === 'string') {
129
- return value;
130
- }
131
- if (CERT_OPTIONS.includes(key)) {
132
- return chalk`${
133
- value.toString()
134
- .replace(/-----.+?-----|\n/g, '')
135
- .substring(0, 80)
136
- }{white …}`;
137
- }
138
- if (!value && value !== false) {
139
- return UNDEFINED_VALUE;
140
- }
141
- if (typeof value === 'function') {
142
- const lines = `${value}`.split('\n');
143
- return lines[0].slice(0, -2);
144
- }
145
- return `${JSON.stringify(value)}`;
146
- }
147
-
148
- function prettyPrintXml(xml, indent) {
149
- // This works well, because we format the xml before applying the replacements
150
- const prettyXml = xmlFormat(xml, {indentation: ' '})
151
- // Matches `<{prefix}:{name} .*?>`
152
- .replace(/<(\/)?((?:[\w]+)(?::))?([\w]+)(.*?)>/g, chalk`<{green $1$2{bold $3}}$4>`)
153
- // Matches ` {attribute}="{value}"
154
- .replace(/ ([\w:]+)="(.+?)"/g, chalk` {white $1}={cyan "$2"}`);
155
- if (indent) {
156
- return prettyXml.replace(/(^|\n)/g, `$1${' '.repeat(indent)}`);
157
- }
158
- return prettyXml;
159
- }
160
-
161
- /**
162
- * Arguments
163
- */
164
- function processArgs(args, options) {
165
- var baseArgv;
166
-
167
- if (options) {
168
- baseArgv = yargs(args).config(options);
169
- } else {
170
- baseArgv = yargs(args);
171
- }
172
- return baseArgv
173
- .usage('\nSimple IdP for SAML 2.0 WebSSO & SLO Profile\n\n' +
174
- 'Launches an IdP web server that mints SAML assertions or logout responses for a Service Provider (SP)\n\n' +
175
- 'Usage:\n\t$0 --acsUrl {url} --audience {uri}')
176
- .alias({h: 'help'})
177
- .options({
178
- host: {
179
- description: 'IdP Web Server Listener Host',
180
- required: false,
181
- default: 'localhost'
182
- },
183
- port: {
184
- description: 'IdP Web Server Listener Port',
185
- required: true,
186
- alias: 'p',
187
- default: 7000
188
- },
189
- cert: {
190
- description: 'IdP Signature PublicKey Certificate',
191
- required: true,
192
- default: './idp-public-cert.pem',
193
- coerce: makeCertFileCoercer('certificate', 'IdP Signature PublicKey Certificate', KEY_CERT_HELP_TEXT)
194
- },
195
- key: {
196
- description: 'IdP Signature PrivateKey Certificate',
197
- required: true,
198
- default: './idp-private-key.pem',
199
- coerce: makeCertFileCoercer('RSA private key', 'IdP Signature PrivateKey Certificate', KEY_CERT_HELP_TEXT)
200
- },
201
- issuer: {
202
- description: 'IdP Issuer URI',
203
- required: true,
204
- alias: 'iss',
205
- default: 'urn:example:idp'
206
- },
207
- acsUrl: {
208
- description: 'SP Assertion Consumer URL',
209
- required: true,
210
- alias: 'acs'
211
- },
212
- sloUrl: {
213
- description: 'SP Single Logout URL',
214
- required: false,
215
- alias: 'slo'
216
- },
217
- audience: {
218
- description: 'SP Audience URI',
219
- required: true,
220
- alias: 'aud'
221
- },
222
- serviceProviderId: {
223
- description: 'SP Issuer/Entity URI',
224
- required: false,
225
- alias: 'spId',
226
- string: true
227
- },
228
- relayState: {
229
- description: 'Default SAML RelayState for SAMLResponse',
230
- required: false,
231
- alias: 'rs'
232
- },
233
- disableRequestAcsUrl: {
234
- description: 'Disables ability for SP AuthnRequest to specify Assertion Consumer URL',
235
- required: false,
236
- boolean: true,
237
- alias: 'static',
238
- default: false
239
- },
240
- encryptAssertion: {
241
- description: 'Encrypts assertion with SP Public Key',
242
- required: false,
243
- boolean: true,
244
- alias: 'enc',
245
- default: false
246
- },
247
- encryptionCert: {
248
- description: 'SP Certificate (pem) for Assertion Encryption',
249
- required: false,
250
- string: true,
251
- alias: 'encCert',
252
- coerce: makeCertFileCoercer('certificate', 'Encryption cert')
253
- },
254
- encryptionPublicKey: {
255
- description: 'SP RSA Public Key (pem) for Assertion Encryption ' +
256
- '(e.g. openssl x509 -pubkey -noout -in sp-cert.pem)',
257
- required: false,
258
- string: true,
259
- alias: 'encKey',
260
- coerce: makeCertFileCoercer('public key', 'Encryption public key')
261
- },
262
- httpsPrivateKey: {
263
- description: 'Web Server TLS/SSL Private Key (pem)',
264
- required: false,
265
- string: true,
266
- coerce: makeCertFileCoercer('RSA private key')
267
- },
268
- httpsCert: {
269
- description: 'Web Server TLS/SSL Certificate (pem)',
270
- required: false,
271
- string: true,
272
- coerce: makeCertFileCoercer('certificate')
273
- },
274
- https: {
275
- description: 'Enables HTTPS Listener (requires httpsPrivateKey and httpsCert)',
276
- required: true,
277
- boolean: true,
278
- default: false
279
- },
280
- signResponse: {
281
- description: 'Enables signing of responses',
282
- required: false,
283
- boolean: true,
284
- default: true,
285
- alias: 'signResponse'
286
- },
287
- configFile: {
288
- description: 'Path to a SAML attribute config file',
289
- required: true,
290
- default: 'saml-idp/config.js',
291
- alias: 'conf'
292
- },
293
- rollSession: {
294
- description: 'Create a new session for every authn request instead of reusing an existing session',
295
- required: false,
296
- boolean: true,
297
- default: false
298
- },
299
- authnContextClassRef: {
300
- description: 'Authentication Context Class Reference',
301
- required: false,
302
- string: true,
303
- default: 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport',
304
- alias: 'acr'
305
- },
306
- authnContextDecl: {
307
- description: 'Authentication Context Declaration (XML FilePath)',
308
- required: false,
309
- string: true,
310
- alias: 'acd',
311
- coerce: function (value) {
312
- const filePath = resolveFilePath(value);
313
- if (filePath) {
314
- return fs.readFileSync(filePath, 'utf8')
315
- }
316
- }
317
- }
318
- })
319
- .example('$0 --acsUrl http://acme.okta.com/auth/saml20/exampleidp --audience https://www.okta.com/saml2/service-provider/spf5aFRRXFGIMAYXQPNV', '')
320
- .check(function(argv, aliases) {
321
- if (argv.encryptAssertion) {
322
- if (argv.encryptionPublicKey === undefined) {
323
- return 'encryptionPublicKey argument is also required for assertion encryption';
324
- }
325
- if (argv.encryptionCert === undefined) {
326
- return 'encryptionCert argument is also required for assertion encryption';
327
- }
328
- }
329
- return true;
330
- })
331
- .check(function(argv, aliases) {
332
- if (argv.config) {
333
- return true;
334
- }
335
- const configFilePath = resolveFilePath(argv.configFile);
336
-
337
- if (!configFilePath) {
338
- return 'SAML attribute config file path "' + argv.configFile + '" is not a valid path.\n';
339
- }
340
- try {
341
- argv.config = require(configFilePath);
342
- } catch (error) {
343
- return 'Encountered an exception while loading SAML attribute config file "' + configFilePath + '".\n' + error;
344
- }
345
- return true;
346
- })
347
- .wrap(baseArgv.terminalWidth());
348
- }
349
-
350
- const app = express.Router();
351
-
352
- function _runServer(argv) {
353
-
354
- const blocks = {};
355
-
356
- console.log(dedent(chalk`
357
- Listener Port:
358
- {cyan ${argv.host}:${argv.port}}
359
- HTTPS Enabled:
360
- {cyan ${argv.https}}
361
-
362
- {bold [{yellow Identity Provider}]}
363
-
364
- Issuer URI:
365
- {cyan ${argv.issuer}}
366
- Sign Response Message:
367
- {cyan ${argv.signResponse}}
368
- Encrypt Assertion:
369
- {cyan ${argv.encryptAssertion}}
370
- Authentication Context Class Reference:
371
- {cyan ${argv.authnContextClassRef || UNDEFINED_VALUE}}
372
- Authentication Context Declaration:
373
- {cyan ${argv.authnContextDecl || UNDEFINED_VALUE}}
374
- Default RelayState:
375
- {cyan ${argv.relayState || UNDEFINED_VALUE}}
376
-
377
- {bold [{yellow Service Provider}]}
378
-
379
- serviceProviderId URI:
380
- {cyan ${argv.serviceProviderId || UNDEFINED_VALUE}}
381
- Audience URI:
382
- {cyan ${argv.audience || UNDEFINED_VALUE}}
383
- ACS URL:
384
- {cyan ${argv.acsUrl || UNDEFINED_VALUE}}
385
- SLO URL:
386
- {cyan ${argv.sloUrl || UNDEFINED_VALUE}}
387
- Trust ACS URL in Request:
388
- {cyan ${!argv.disableRequestAcsUrl}}
389
- `));
390
-
391
-
392
- /**
393
- * IdP Configuration
394
- */
395
-
396
- const idpOptions = {
397
- issuer: argv.issuer,
398
- serviceProviderId: argv.serviceProviderId || argv.audience,
399
- cert: argv.cert,
400
- key: argv.key,
401
- audience: argv.audience,
402
- recipient: argv.acsUrl,
403
- destination: argv.acsUrl,
404
- acsUrl: argv.acsUrl,
405
- sloUrl: argv.sloUrl,
406
- RelayState: argv.relayState,
407
- allowRequestAcsUrl: !argv.disableRequestAcsUrl,
408
- digestAlgorithm: 'sha256',
409
- signatureAlgorithm: 'rsa-sha256',
410
- signResponse: argv.signResponse,
411
- encryptAssertion: argv.encryptAssertion,
412
- encryptionCert: argv.encryptionCert,
413
- encryptionPublicKey: argv.encryptionPublicKey,
414
- encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',
415
- keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p',
416
- lifetimeInSeconds: 3600,
417
- authnContextClassRef: argv.authnContextClassRef,
418
- authnContextDecl: argv.authnContextDecl,
419
- includeAttributeNameFormat: true,
420
- profileMapper: SimpleProfileMapper.fromMetadata(argv.config.metadata),
421
- postEndpointPath: IDP_PATHS.SSO,
422
- redirectEndpointPath: IDP_PATHS.SSO,
423
- logoutEndpointPaths: argv.sloUrl ?
424
- {
425
- redirect: IDP_PATHS.SLO,
426
- post: IDP_PATHS.SLO
427
- } : {},
428
- getUserFromRequest: function(req) { return req.user; },
429
- getPostURL: function (audience, authnRequestDom, req, callback) {
430
- return callback(null, (req.authnRequest && req.authnRequest.acsUrl) ?
431
- req.authnRequest.acsUrl :
432
- argv.acsUrl);
433
- },
434
- transformAssertion: function(assertionDom) {
435
- if (argv.authnContextDecl) {
436
- var declDoc;
437
- try {
438
- declDoc = new Parser().parseFromString(argv.authnContextDecl);
439
- } catch(err){
440
- console.log('Unable to parse Authentication Context Declaration XML', err);
441
- }
442
- if (declDoc) {
443
- const authnContextDeclEl = assertionDom.createElementNS('urn:oasis:names:tc:SAML:2.0:assertion', 'saml:AuthnContextDecl');
444
- authnContextDeclEl.appendChild(declDoc.documentElement);
445
- const authnContextEl = assertionDom.getElementsByTagName('saml:AuthnContext')[0];
446
- authnContextEl.appendChild(authnContextDeclEl);
447
- }
448
- }
449
- },
450
- responseHandler: function(response, opts, req, res, next) {
451
- console.log(dedent(chalk`
452
- Sending SAML Response to {cyan ${opts.postUrl}} =>
453
- {bold RelayState} =>
454
- {cyan ${opts.RelayState || UNDEFINED_VALUE}}
455
- {bold SAMLResponse} =>`
456
- ));
457
-
458
- console.log(prettyPrintXml(response.toString(), 4));
459
-
460
- res.render('samlresponse', {
461
- AcsUrl: opts.postUrl,
462
- SAMLResponse: response.toString('base64'),
463
- RelayState: opts.RelayState
464
- });
465
- }
466
- }
467
-
468
- /**
469
- * App Environment
470
- */
471
-
472
- // app.set('host', process.env.HOST || argv.host);
473
- // app.set('port', process.env.PORT || argv.port);
474
- // app.set('views', path.join(__dirname, 'views'));
475
-
476
- /**
477
- * View Engine
478
- */
479
-
480
- // app.set('view engine', 'hbs');
481
- // app.set('view options', { layout: 'layout' })
482
- // app.engine('handlebars', hbs.__express);
483
-
484
- // Register Helpers
485
- hbs.registerHelper('extend', function(name, context) {
486
- var block = blocks[name];
487
- if (!block) {
488
- block = blocks[name] = [];
489
- }
490
-
491
- block.push(context.fn(this));
492
- });
493
-
494
- hbs.registerHelper('block', function(name) {
495
- const val = (blocks[name] || []).join('\n');
496
- // clear the block
497
- blocks[name] = [];
498
- return val;
499
- });
500
-
501
-
502
- hbs.registerHelper('select', function(selected, options) {
503
- return options.fn(this).replace(
504
- new RegExp(' value=\"' + selected + '\"'), '$& selected="selected"');
505
- });
506
-
507
- hbs.registerHelper('getProperty', function(attribute, context) {
508
- return context[attribute];
509
- });
510
-
511
- hbs.registerHelper('serialize', function(context) {
512
- return new Buffer(JSON.stringify(context)).toString('base64');
513
- });
514
-
515
- /**
516
- * Middleware
517
- */
518
-
519
- app.use(logger(':date> :method :url - {:referrer} => :status (:response-time ms)', {
520
- skip: function (req, res)
521
- {
522
- return req.path.startsWith('/bower_components') || req.path.startsWith('/css')
523
- }
524
- }));
525
- app.use(bodyParser.urlencoded({extended: true}));
526
- app.use(express.static(path.join(__dirname, 'public')));
527
- app.use(session({
528
- secret: 'The universe works on a math equation that never even ever really ends in the end',
529
- resave: false,
530
- saveUninitialized: true,
531
- name: 'idp_sid',
532
- cookie: { maxAge: 60 * 60 * 1000 }
533
- }));
534
-
535
- /**
536
- * View Handlers
537
- */
538
-
539
- const showUser = function (req, res, next) {
540
- res.render('user', {
541
- user: req.user,
542
- participant: req.participant,
543
- metadata: req.metadata,
544
- authnRequest: req.authnRequest,
545
- idp: req.idp.options,
546
- paths: IDP_PATHS
547
- });
548
- }
549
-
550
- /**
551
- * Shared Handlers
552
- */
553
-
554
- const parseSamlRequest = function(req, res, next) {
555
- samlp.parseRequest(req, function(err, data) {
556
- if (err) {
557
- return res.render('error', {
558
- message: 'SAML AuthnRequest Parse Error: ' + err.message,
559
- error: err
560
- });
561
- };
562
- if (data) {
563
- req.authnRequest = {
564
- relayState: req.query.RelayState || req.body.RelayState,
565
- id: data.id,
566
- issuer: data.issuer,
567
- destination: data.destination,
568
- acsUrl: data.assertionConsumerServiceURL,
569
- forceAuthn: data.forceAuthn === 'true'
570
- };
571
- console.log('Received AuthnRequest => \n', req.authnRequest);
572
- }
573
- if (req.user != undefined){
574
- return showUser(req, res, next);
575
- } else {
576
- console.log('Redirect to login => \n');
577
- res.redirect("/accounts/a/#/login?redirect_uri="+ encodeURIComponent(req.originalUrl));
578
- return res.end();
579
- }
580
- })
581
- };
582
-
583
- const getSessionIndex = function(req) {
584
- if (req && req.session) {
585
- return Math.abs(getHashCode(req.session.id)).toString();
586
- }
587
- }
588
-
589
- const getParticipant = function(req) {
590
- return {
591
- serviceProviderId: req.idp.options.serviceProviderId,
592
- sessionIndex: getSessionIndex(req),
593
- nameId: req.user.username,
594
- nameIdFormat: req.user.nameIdFormat,
595
- serviceProviderLogoutURL: req.idp.options.sloUrl
596
- }
597
- }
598
-
599
- const parseLogoutRequest = function(req, res, next) {
600
- if (!req.idp.options.sloUrl) {
601
- return res.render('error', {
602
- message: 'SAML Single Logout Service URL not defined for Service Provider'
603
- });
604
- };
605
-
606
- console.log('Processing SAML SLO request for participant => \n', req.participant);
607
-
608
- return samlp.logout({
609
- issuer: req.idp.options.issuer,
610
- cert: req.idp.options.cert,
611
- key: req.idp.options.key,
612
- digestAlgorithm: req.idp.options.digestAlgorithm,
613
- signatureAlgorithm: req.idp.options.signatureAlgorithm,
614
- sessionParticipants: new SessionParticipants(
615
- [
616
- req.participant
617
- ]),
618
- clearIdPSession: function(callback) {
619
- console.log('Destroying session ' + req.session.id + ' for participant', req.participant);
620
- req.session.destroy();
621
- callback();
622
- }
623
- })(req, res, next);
624
- }
625
-
626
- /**
627
- * Routes
628
- */
629
-
630
- app.use(function(req, res, next){
631
- if (argv.rollSession) {
632
- req.session.regenerate(function(err) {
633
- return next();
634
- });
635
- } else {
636
- next()
637
- }
638
- });
639
-
640
- app.use(function(req, res, next){
641
- //req.user = argv.config.user;
642
- req.metadata = argv.config.metadata;
643
- req.idp = { options: idpOptions };
644
- if (req.user)
645
- req.participant = getParticipant(req);
646
- next();
647
- });
648
-
649
- app.get(['/', '/idp', IDP_PATHS.SSO], parseSamlRequest);
650
- app.post(['/', '/idp', IDP_PATHS.SSO], parseSamlRequest);
651
-
652
- app.get(IDP_PATHS.SLO, parseLogoutRequest);
653
- app.post(IDP_PATHS.SLO, parseLogoutRequest);
654
-
655
- app.post(IDP_PATHS.SIGN_IN, function(req, res) {
656
- const authOptions = extend({}, req.idp.options);
657
- Object.keys(req.body).forEach(function(key) {
658
- var buffer;
659
- if (key === '_authnRequest') {
660
- buffer = new Buffer(req.body[key], 'base64');
661
- req.authnRequest = JSON.parse(buffer.toString('utf8'));
662
-
663
- // Apply AuthnRequest Params
664
- authOptions.inResponseTo = req.authnRequest.id;
665
- if (req.idp.options.allowRequestAcsUrl && req.authnRequest.acsUrl) {
666
- authOptions.acsUrl = req.authnRequest.acsUrl;
667
- authOptions.recipient = req.authnRequest.acsUrl;
668
- authOptions.destination = req.authnRequest.acsUrl;
669
- authOptions.forceAuthn = req.authnRequest.forceAuthn;
670
- }
671
- if (req.authnRequest.relayState) {
672
- authOptions.RelayState = req.authnRequest.relayState;
673
- }
674
- } else {
675
- req.user[key] = req.body[key];
676
- }
677
- });
678
-
679
- if (!authOptions.encryptAssertion) {
680
- delete authOptions.encryptionCert;
681
- delete authOptions.encryptionPublicKey;
682
- }
683
-
684
- // Set Session Index
685
- authOptions.sessionIndex = getSessionIndex(req);
686
-
687
- // Keep calm and Single Sign On
688
- console.log(dedent(chalk`
689
- Generating SAML Response using =>
690
- {bold User} => ${Object.entries(req.user).map(([key, value]) => chalk`
691
- ${key}: {cyan ${value}}`
692
- ).join('')}
693
- {bold SAMLP Options} => ${Object.entries(authOptions).map(([key, value]) => chalk`
694
- ${key}: {cyan ${formatOptionValue(key, value)}}`
695
- ).join('')}
696
- `));
697
- samlp.auth(authOptions)(req, res);
698
- })
699
-
700
- app.get(IDP_PATHS.METADATA, function(req, res, next) {
701
- samlp.metadata(req.idp.options)(req, res);
702
- });
703
-
704
- app.post(IDP_PATHS.METADATA, function(req, res, next) {
705
- if (req.body && req.body.attributeName && req.body.name) {
706
- var attributeExists = false;
707
- const attribute = {
708
- id: req.body.attributeName,
709
- optional: true,
710
- name: req.body.name,
711
- description: req.body.description || '',
712
- multiValue: req.body.valueType === 'multi'
713
- };
714
-
715
- req.metadata.forEach(function(entry) {
716
- if (entry.id === req.body.attributeName) {
717
- entry = attribute;
718
- attributeExists = true;
719
- }
720
- });
721
-
722
- if (!attributeExists) {
723
- req.metadata.push(attribute);
724
- }
725
- console.log("Updated SAML Attribute Metadata => \n", req.metadata)
726
- res.status(200).end();
727
- }
728
- });
729
-
730
- app.get(IDP_PATHS.SIGN_OUT, function(req, res, next) {
731
- if (req.idp.options.sloUrl) {
732
- console.log('Initiating SAML SLO request for user: ' + req.user.username +
733
- ' with sessionIndex: ' + getSessionIndex(req));
734
- res.redirect(IDP_PATHS.SLO);
735
- } else {
736
- console.log('SAML SLO is not enabled for SP, destroying IDP session');
737
- req.session.destroy(function(err) {
738
- if (err) {
739
- throw err;
740
- }
741
- res.redirect('back');
742
- })
743
- }
744
- });
745
-
746
- app.get([IDP_PATHS.SETTINGS], function(req, res, next) {
747
- res.render('settings', {
748
- idp: req.idp.options
749
- });
750
- });
751
-
752
- app.post([IDP_PATHS.SETTINGS], function(req, res, next) {
753
- Object.keys(req.body).forEach(function(key) {
754
- switch(req.body[key].toLowerCase()){
755
- case "true": case "yes": case "1":
756
- req.idp.options[key] = true;
757
- break;
758
- case "false": case "no": case "0":
759
- req.idp.options[key] = false;
760
- break;
761
- default:
762
- req.idp.options[key] = req.body[key];
763
- break;
764
- }
765
-
766
- if (req.body[key].match(/^\d+$/)) {
767
- req.idp.options[key] = parseInt(req.body[key], '10');
768
- }
769
- });
770
-
771
- console.log('Updated IdP Configuration => \n', req.idp.options);
772
- res.redirect('/');
773
- });
774
-
775
- // catch 404 and forward to error handler
776
- app.use(function(req, res, next) {
777
- const err = new Error('Route Not Found');
778
- err.status = 404;
779
- next(err);
780
- });
781
-
782
- // development error handler
783
- app.use(function(err, req, res, next) {
784
- if (err) {
785
- res.status(err.status || 500);
786
- res.render('error', {
787
- message: err.message,
788
- error: err
789
- });
790
- }
791
- });
792
-
793
- }
794
-
795
- function runServer(options) {
796
- const args = processArgs([], options);
797
- return _runServer(args.argv);
798
- }
799
-
800
- function main () {
801
- const args = processArgs(process.argv.slice(2));
802
- _runServer(args.argv);
803
- }
804
-
805
-
806
- module.exports = {
807
- samlIdp: {
808
- run: runServer,
809
- expressMiddleware: app
810
- }
811
- };