samlesa 2.12.113 → 2.14.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.
Potentially problematic release.
This version of samlesa might be problematic. Click here for more details.
- package/build/index.js +18 -54
- package/build/index.js.map +1 -1
- package/build/src/api.js +18 -24
- package/build/src/api.js.map +1 -1
- package/build/src/binding-post.js +337 -365
- package/build/src/binding-post.js.map +1 -1
- package/build/src/binding-redirect.js +312 -340
- package/build/src/binding-redirect.js.map +1 -1
- package/build/src/binding-simplesign.js +201 -229
- package/build/src/binding-simplesign.js.map +1 -1
- package/build/src/entity-idp.js +119 -127
- package/build/src/entity-idp.js.map +1 -1
- package/build/src/entity-sp.js +88 -96
- package/build/src/entity-sp.js.map +1 -1
- package/build/src/entity.js +193 -225
- package/build/src/entity.js.map +1 -1
- package/build/src/extractor.js +361 -369
- package/build/src/extractor.js.map +1 -1
- package/build/src/flow.js +313 -320
- package/build/src/flow.js.map +1 -1
- package/build/src/libsaml.js +693 -721
- package/build/src/libsaml.js.map +1 -1
- package/build/src/metadata-idp.js +119 -127
- package/build/src/metadata-idp.js.map +1 -1
- package/build/src/metadata-sp.js +223 -231
- package/build/src/metadata-sp.js.map +1 -1
- package/build/src/metadata.js +138 -166
- package/build/src/metadata.js.map +1 -1
- package/build/src/types.js +4 -11
- package/build/src/types.js.map +1 -1
- package/build/src/urn.js +204 -212
- package/build/src/urn.js.map +1 -1
- package/build/src/utility.js +277 -292
- package/build/src/utility.js.map +1 -1
- package/build/src/validator.js +24 -27
- package/build/src/validator.js.map +1 -1
- package/package.json +13 -7
- package/types/api.d.ts +15 -0
- package/types/api.d.ts.map +1 -0
- package/types/binding-post.d.ts +48 -0
- package/types/binding-post.d.ts.map +1 -0
- package/types/binding-redirect.d.ts +54 -0
- package/types/binding-redirect.d.ts.map +1 -0
- package/types/binding-simplesign.d.ts +41 -0
- package/types/binding-simplesign.d.ts.map +1 -0
- package/types/entity-idp.d.ts +38 -0
- package/types/entity-idp.d.ts.map +1 -0
- package/types/entity-sp.d.ts +38 -0
- package/types/entity-sp.d.ts.map +1 -0
- package/types/entity.d.ts +100 -0
- package/types/entity.d.ts.map +1 -0
- package/types/extractor.d.ts +26 -0
- package/types/extractor.d.ts.map +1 -0
- package/types/flow.d.ts +7 -0
- package/types/flow.d.ts.map +1 -0
- package/types/index.d.ts +11 -10
- package/types/index.d.ts.map +1 -0
- package/types/libsaml.d.ts +208 -0
- package/types/libsaml.d.ts.map +1 -0
- package/types/metadata-idp.d.ts +25 -0
- package/types/metadata-idp.d.ts.map +1 -0
- package/types/metadata-sp.d.ts +37 -0
- package/types/metadata-sp.d.ts.map +1 -0
- package/types/metadata.d.ts +58 -0
- package/types/metadata.d.ts.map +1 -0
- package/types/src/api.d.ts +15 -13
- package/types/src/api.d.ts.map +1 -0
- package/types/src/binding-post.d.ts +48 -47
- package/types/src/binding-post.d.ts.map +1 -0
- package/types/src/binding-redirect.d.ts +54 -53
- package/types/src/binding-redirect.d.ts.map +1 -0
- package/types/src/binding-simplesign.d.ts +41 -40
- package/types/src/binding-simplesign.d.ts.map +1 -0
- package/types/src/entity-idp.d.ts +38 -37
- package/types/src/entity-idp.d.ts.map +1 -0
- package/types/src/entity-sp.d.ts +38 -36
- package/types/src/entity-sp.d.ts.map +1 -0
- package/types/src/entity.d.ts +100 -101
- package/types/src/entity.d.ts.map +1 -0
- package/types/src/extractor.d.ts +26 -25
- package/types/src/extractor.d.ts.map +1 -0
- package/types/src/flow.d.ts +7 -6
- package/types/src/flow.d.ts.map +1 -0
- package/types/src/libsaml.d.ts +208 -209
- package/types/src/libsaml.d.ts.map +1 -0
- package/types/src/metadata-idp.d.ts +25 -24
- package/types/src/metadata-idp.d.ts.map +1 -0
- package/types/src/metadata-sp.d.ts +37 -36
- package/types/src/metadata-sp.d.ts.map +1 -0
- package/types/src/metadata.d.ts +58 -59
- package/types/src/metadata.d.ts.map +1 -0
- package/types/src/types.d.ts +128 -129
- package/types/src/types.d.ts.map +1 -0
- package/types/src/urn.d.ts +195 -194
- package/types/src/urn.d.ts.map +1 -0
- package/types/src/utility.d.ts +133 -134
- package/types/src/utility.d.ts.map +1 -0
- package/types/src/validator.d.ts +4 -3
- package/types/src/validator.d.ts.map +1 -0
- package/types/types.d.ts +128 -0
- package/types/types.d.ts.map +1 -0
- package/types/urn.d.ts +195 -0
- package/types/urn.d.ts.map +1 -0
- package/types/utility.d.ts +133 -0
- package/types/utility.d.ts.map +1 -0
- package/types/validator.d.ts +4 -0
- package/types/validator.d.ts.map +1 -0
- package/.editorconfig +0 -19
- package/.github/FUNDING.yml +0 -1
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/samlify.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/.pre-commit.sh +0 -15
- package/.snyk +0 -8
- package/.travis.yml +0 -29
- package/Makefile +0 -25
- package/index.d.ts +0 -10
- package/index.js +0 -19
- package/index.js.map +0 -1
- package/index.ts +0 -28
- package/qodana.yaml +0 -29
- package/src/.idea/modules.xml +0 -8
- package/src/.idea/src.iml +0 -12
- package/src/.idea/vcs.xml +0 -6
- package/src/api.ts +0 -36
- package/src/binding-post.ts +0 -348
- package/src/binding-redirect.ts +0 -356
- package/src/binding-simplesign.ts +0 -238
- package/src/entity-idp.ts +0 -153
- package/src/entity-sp.ts +0 -114
- package/src/entity.ts +0 -243
- package/src/extractor.ts +0 -392
- package/src/flow.ts +0 -467
- package/src/libsaml.ts +0 -895
- package/src/metadata-idp.ts +0 -146
- package/src/metadata-sp.ts +0 -268
- package/src/metadata.ts +0 -166
- package/src/types.ts +0 -153
- package/src/urn.ts +0 -211
- package/src/utility.ts +0 -319
- package/src/validator.ts +0 -39
- package/tsconfig.json +0 -38
- package/tslint.json +0 -35
- package/types.d.ts +0 -2
package/build/src/libsaml.js
CHANGED
|
@@ -1,722 +1,694 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
'rsa-sha1': '
|
|
56
|
-
'rsa-sha256': '
|
|
57
|
-
'rsa-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
'
|
|
61
|
-
'
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
*/
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
'
|
|
91
|
-
};
|
|
92
|
-
/**
|
|
93
|
-
* @desc Default
|
|
94
|
-
* @type {
|
|
95
|
-
*/
|
|
96
|
-
const
|
|
97
|
-
context: '<
|
|
98
|
-
};
|
|
99
|
-
/**
|
|
100
|
-
* @desc Default
|
|
101
|
-
* @type {
|
|
102
|
-
*/
|
|
103
|
-
const
|
|
104
|
-
context: '<samlp:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* @
|
|
129
|
-
* @
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
* @
|
|
141
|
-
* @
|
|
142
|
-
*/
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
*
|
|
508
|
-
* @param
|
|
509
|
-
* @
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
return
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
});
|
|
695
|
-
},
|
|
696
|
-
/**
|
|
697
|
-
* @desc Check if the xml string is valid and bounded
|
|
698
|
-
*/
|
|
699
|
-
async isValidXml(input) {
|
|
700
|
-
// check if global api contains the validate function
|
|
701
|
-
const { validate } = (0, api_js_1.getContext)();
|
|
702
|
-
/**
|
|
703
|
-
* user can write a validate function that always returns
|
|
704
|
-
* a resolved promise and skip the validator even in
|
|
705
|
-
* production, user will take the responsibility if
|
|
706
|
-
* they intend to skip the validation
|
|
707
|
-
*/
|
|
708
|
-
if (!validate) {
|
|
709
|
-
// otherwise, an error will be thrown
|
|
710
|
-
return Promise.reject('Your application is potentially vulnerable because no validation function found. Please read the documentation on how to setup the validator. (https://github.com/tngan/samlify#installation)');
|
|
711
|
-
}
|
|
712
|
-
try {
|
|
713
|
-
return await validate(input);
|
|
714
|
-
}
|
|
715
|
-
catch (e) {
|
|
716
|
-
throw e;
|
|
717
|
-
}
|
|
718
|
-
},
|
|
719
|
-
};
|
|
720
|
-
};
|
|
721
|
-
exports.default = libSaml();
|
|
1
|
+
/**
|
|
2
|
+
* @file SamlLib.js
|
|
3
|
+
* @author tngan
|
|
4
|
+
* @desc A simple library including some common functions
|
|
5
|
+
*/
|
|
6
|
+
import xml from 'xml';
|
|
7
|
+
import { createSign, createPrivateKey, createVerify } from 'node:crypto';
|
|
8
|
+
import utility, { flattenDeep, isString } from './utility.js';
|
|
9
|
+
import { algorithms, wording, namespace } from './urn.js';
|
|
10
|
+
import { select } from 'xpath';
|
|
11
|
+
import { SignedXml } from 'xml-crypto';
|
|
12
|
+
import * as xmlenc from 'xml-encryption';
|
|
13
|
+
import camelCase from 'camelcase';
|
|
14
|
+
import { getContext } from './api.js';
|
|
15
|
+
import xmlEscape from 'xml-escape';
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import { DOMParser } from '@xmldom/xmldom';
|
|
18
|
+
const signatureAlgorithms = algorithms.signature;
|
|
19
|
+
const digestAlgorithms = algorithms.digest;
|
|
20
|
+
const certUse = wording.certUse;
|
|
21
|
+
const urlParams = wording.urlParams;
|
|
22
|
+
/**
|
|
23
|
+
* 算法名称映射表 (兼容 X.509 和 SAML 规范)
|
|
24
|
+
*/
|
|
25
|
+
function mapSignAlgorithm(algorithm) {
|
|
26
|
+
const algorithmMap = {
|
|
27
|
+
'rsa-sha1': 'RSA-SHA1',
|
|
28
|
+
'rsa-sha256': 'RSA-SHA256',
|
|
29
|
+
'rsa-sha384': 'RSA-SHA384',
|
|
30
|
+
'rsa-sha512': 'RSA-SHA512',
|
|
31
|
+
'ecdsa-sha256': 'ECDSA-SHA256',
|
|
32
|
+
'ecdsa-sha384': 'ECDSA-SHA384',
|
|
33
|
+
'ecdsa-sha512': 'ECDSA-SHA512'
|
|
34
|
+
};
|
|
35
|
+
return algorithmMap[algorithm.toLowerCase()] || algorithm;
|
|
36
|
+
}
|
|
37
|
+
const libSaml = () => {
|
|
38
|
+
/**
|
|
39
|
+
* @desc helper function to get back the query param for redirect binding for SLO/SSO
|
|
40
|
+
* @type {string}
|
|
41
|
+
*/
|
|
42
|
+
function getQueryParamByType(type) {
|
|
43
|
+
if ([urlParams.logoutRequest, urlParams.samlRequest].indexOf(type) !== -1) {
|
|
44
|
+
return 'SAMLRequest';
|
|
45
|
+
}
|
|
46
|
+
if ([urlParams.logoutResponse, urlParams.samlResponse].indexOf(type) !== -1) {
|
|
47
|
+
return 'SAMLResponse';
|
|
48
|
+
}
|
|
49
|
+
throw new Error('ERR_UNDEFINED_QUERY_PARAMS');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
*
|
|
53
|
+
*/
|
|
54
|
+
const nrsaAliasMapping = {
|
|
55
|
+
'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'pkcs1-sha1',
|
|
56
|
+
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'pkcs1-sha256',
|
|
57
|
+
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'pkcs1-sha512',
|
|
58
|
+
};
|
|
59
|
+
const nrsaAliasMappingForNode = {
|
|
60
|
+
'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'RSA-SHA1',
|
|
61
|
+
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'RSA-SHA256',
|
|
62
|
+
'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'RSA-SHA512',
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* @desc Default login request template
|
|
66
|
+
* @type {LoginRequestTemplate}
|
|
67
|
+
*/
|
|
68
|
+
const defaultLoginRequestTemplate = {
|
|
69
|
+
context: '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="{AssertionConsumerServiceURL}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:NameIDPolicy Format="{NameIDFormat}" AllowCreate="{AllowCreate}"/></samlp:AuthnRequest>',
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* @desc Default logout request template
|
|
73
|
+
* @type {LogoutRequestTemplate}
|
|
74
|
+
*/
|
|
75
|
+
const defaultLogoutRequestTemplate = {
|
|
76
|
+
context: '<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}"><saml:Issuer>{Issuer}</saml:Issuer><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID></samlp:LogoutRequest>',
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* @desc Default AttributeStatement template
|
|
80
|
+
* @type {AttributeStatementTemplate}
|
|
81
|
+
*/
|
|
82
|
+
const defaultAttributeStatementTemplate = {
|
|
83
|
+
context: '<saml:AttributeStatement>{Attributes}</saml:AttributeStatement>',
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* @desc Default Attribute template
|
|
87
|
+
* @type {AttributeTemplate}
|
|
88
|
+
*/
|
|
89
|
+
const defaultAttributeTemplate = {
|
|
90
|
+
context: '<saml:Attribute Name="{Name}" NameFormat="{NameFormat}">{AttributeValues}</saml:Attribute>',
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* @desc Default AttributeValue template
|
|
94
|
+
* @type {AttributeTemplate}
|
|
95
|
+
*/
|
|
96
|
+
const defaultAttributeValueTemplate = {
|
|
97
|
+
context: '<saml:AttributeValue xmlns:xs="{ValueXmlnsXs}" xmlns:xsi="{ValueXmlnsXsi}" xsi:type="{ValueXsiType}">{Value}</saml:AttributeValue>',
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* @desc Default login response template
|
|
101
|
+
* @type {LoginResponseTemplate}
|
|
102
|
+
*/
|
|
103
|
+
const defaultLoginResponseTemplate = {
|
|
104
|
+
context: '<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status><saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{AssertionID}" Version="2.0" IssueInstant="{IssueInstant}"><saml:Issuer>{Issuer}</saml:Issuer><saml:Subject><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="{SubjectConfirmationDataNotOnOrAfter}" Recipient="{SubjectRecipient}" InResponseTo="{InResponseTo}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="{ConditionsNotBefore}" NotOnOrAfter="{ConditionsNotOnOrAfter}"><saml:AudienceRestriction><saml:Audience>{Audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions>{AuthnStatement}{AttributeStatement}</saml:Assertion></samlp:Response>',
|
|
105
|
+
attributes: [],
|
|
106
|
+
additionalTemplates: {
|
|
107
|
+
'attributeStatementTemplate': defaultAttributeStatementTemplate,
|
|
108
|
+
'attributeTemplate': defaultAttributeTemplate
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* @desc Default logout response template
|
|
113
|
+
* @type {LogoutResponseTemplate}
|
|
114
|
+
*/
|
|
115
|
+
const defaultLogoutResponseTemplate = {
|
|
116
|
+
context: '<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status></samlp:LogoutResponse>',
|
|
117
|
+
};
|
|
118
|
+
function getSigningSchemeForNode(sigAlg) {
|
|
119
|
+
if (sigAlg) {
|
|
120
|
+
const algAlias = nrsaAliasMappingForNode[sigAlg];
|
|
121
|
+
if (!(algAlias === undefined)) {
|
|
122
|
+
return algAlias;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return nrsaAliasMappingForNode[signatureAlgorithms.RSA_SHA256];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* @private
|
|
129
|
+
* @desc Get the digest algorithms by signature algorithms
|
|
130
|
+
* @param {string} sigAlg signature algorithm
|
|
131
|
+
* @return {string/undefined} digest algorithm
|
|
132
|
+
*/
|
|
133
|
+
function getDigestMethod(sigAlg) {
|
|
134
|
+
return digestAlgorithms[sigAlg];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* @public
|
|
138
|
+
* @desc Create XPath
|
|
139
|
+
* @param {string/object} local parameters to create XPath
|
|
140
|
+
* @param {boolean} isExtractAll define whether returns whole content according to the XPath
|
|
141
|
+
* @return {string} xpath
|
|
142
|
+
*/
|
|
143
|
+
function createXPath(local, isExtractAll) {
|
|
144
|
+
if (isString(local)) {
|
|
145
|
+
return isExtractAll === true ? "//*[local-name(.)='" + local + "']/text()" : "//*[local-name(.)='" + local + "']";
|
|
146
|
+
}
|
|
147
|
+
return "//*[local-name(.)='" + local.name + "']/@" + local.attr;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* @private
|
|
151
|
+
* @desc Tag normalization
|
|
152
|
+
* @param {string} prefix prefix of the tag
|
|
153
|
+
* @param {content} content normalize it to capitalized camel case
|
|
154
|
+
* @return {string}
|
|
155
|
+
*/
|
|
156
|
+
function tagging(prefix, content) {
|
|
157
|
+
const camelContent = camelCase(content, { locale: 'en-us' });
|
|
158
|
+
return prefix + camelContent.charAt(0).toUpperCase() + camelContent.slice(1);
|
|
159
|
+
}
|
|
160
|
+
function escapeTag(replacement) {
|
|
161
|
+
return (_match, quote) => {
|
|
162
|
+
const text = (replacement === null || replacement === undefined) ? '' : String(replacement);
|
|
163
|
+
// not having a quote means this interpolation isn't for an attribute, and so does not need escaping
|
|
164
|
+
return quote ? `${quote}${xmlEscape(text)}` : text;
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
createXPath,
|
|
169
|
+
getQueryParamByType,
|
|
170
|
+
defaultLoginRequestTemplate,
|
|
171
|
+
defaultLoginResponseTemplate,
|
|
172
|
+
defaultAttributeStatementTemplate,
|
|
173
|
+
defaultAttributeTemplate,
|
|
174
|
+
defaultLogoutRequestTemplate,
|
|
175
|
+
defaultLogoutResponseTemplate,
|
|
176
|
+
defaultAttributeValueTemplate,
|
|
177
|
+
/**
|
|
178
|
+
* @desc Replace the tag (e.g. {tag}) inside the raw XML
|
|
179
|
+
* @param {string} rawXML raw XML string used to do keyword replacement
|
|
180
|
+
* @param {array} tagValues tag values
|
|
181
|
+
* @return {string}
|
|
182
|
+
*/
|
|
183
|
+
replaceTagsByValue(rawXML, tagValues) {
|
|
184
|
+
Object.keys(tagValues).forEach(t => {
|
|
185
|
+
rawXML = rawXML.replace(new RegExp(`("?)\\{${t}\\}`, 'g'), escapeTag(tagValues[t]));
|
|
186
|
+
});
|
|
187
|
+
return rawXML;
|
|
188
|
+
},
|
|
189
|
+
/**
|
|
190
|
+
* @desc Helper function to build the AttributeStatement tag
|
|
191
|
+
* @param {LoginResponseAttribute} attributes an array of attribute configuration
|
|
192
|
+
* @param {AttributeTemplate} attributeTemplate the attribute tag template to be used
|
|
193
|
+
* @param {AttributeStatementTemplate} attributeStatementTemplate the attributeStatement tag template to be used
|
|
194
|
+
* @return {string}
|
|
195
|
+
*/
|
|
196
|
+
/* attributeStatementBuilder(
|
|
197
|
+
attributes: LoginResponseAttribute[],
|
|
198
|
+
attributeTemplate: AttributeTemplate = defaultAttributeTemplate,
|
|
199
|
+
attributeStatementTemplate: AttributeStatementTemplate = defaultAttributeStatementTemplate
|
|
200
|
+
): string {
|
|
201
|
+
const attr = attributes.map(({name, nameFormat, valueTag, valueXsiType, type, valueXmlnsXs, valueXmlnsXsi}) => {
|
|
202
|
+
const defaultValueXmlnsXs = 'http://www.w3.org/2001/XMLSchema';
|
|
203
|
+
const defaultValueXmlnsXsi = 'http://www.w3.org/2001/XMLSchema-instance';
|
|
204
|
+
let attributeLine = attributeTemplate.context;
|
|
205
|
+
if (attributeLine && typeof attributeLine === 'function') {
|
|
206
|
+
// 安全调用
|
|
207
|
+
// @ts-ignore
|
|
208
|
+
return attributeLine({
|
|
209
|
+
name,
|
|
210
|
+
nameFormat,
|
|
211
|
+
valueTag,
|
|
212
|
+
valueXsiType,
|
|
213
|
+
type,
|
|
214
|
+
valueXmlnsXs: valueXmlnsXs ?? defaultValueXmlnsXs,
|
|
215
|
+
valueXmlnsXsi: valueXmlnsXsi ?? defaultValueXmlnsXsi
|
|
216
|
+
})
|
|
217
|
+
} else {
|
|
218
|
+
attributeLine = attributeLine.replace('{Name}', name);
|
|
219
|
+
attributeLine = attributeLine.replace('{NameFormat}', nameFormat);
|
|
220
|
+
attributeLine = attributeLine.replace('{ValueXmlnsXs}', valueXmlnsXs ? valueXmlnsXs : defaultValueXmlnsXs);
|
|
221
|
+
attributeLine = attributeLine.replace('{ValueXmlnsXsi}', valueXmlnsXsi ? valueXmlnsXsi : defaultValueXmlnsXsi);
|
|
222
|
+
attributeLine = attributeLine.replace('{ValueXsiType}', valueXsiType);
|
|
223
|
+
attributeLine = attributeLine.replace('{Value}', `{${tagging('attr', valueTag)}}`);
|
|
224
|
+
return attributeLine;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
}).join('');
|
|
228
|
+
return attributeStatementTemplate.context.replace('{Attributes}', attr);
|
|
229
|
+
},*/
|
|
230
|
+
/** For Test */
|
|
231
|
+
attributeStatementBuilder(attributeData) {
|
|
232
|
+
// 构建 XML 元素数组
|
|
233
|
+
// 构建 XML 结构
|
|
234
|
+
const attributeStatement = {
|
|
235
|
+
'saml:AttributeStatement': [
|
|
236
|
+
// 命名空间声明(在 AttributeStatement 上定义)
|
|
237
|
+
{},
|
|
238
|
+
// 遍历生成多个 Attribute
|
|
239
|
+
...attributeData.map(attr => ({
|
|
240
|
+
'saml:Attribute ': [
|
|
241
|
+
// Attribute 属性
|
|
242
|
+
{
|
|
243
|
+
_attr: {
|
|
244
|
+
Name: attr.Name,
|
|
245
|
+
NameFormat: attr.NameFormat
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
// 遍历生成多个 AttributeValue
|
|
249
|
+
...attr.valueArray.map((valueObj) => ({
|
|
250
|
+
'saml:AttributeValue ': [
|
|
251
|
+
// 数据类型(根据 ValueType)
|
|
252
|
+
{
|
|
253
|
+
_attr: attr.ValueType === 1
|
|
254
|
+
? { 'xsi:type': 'xs:string' }
|
|
255
|
+
: {}
|
|
256
|
+
},
|
|
257
|
+
// 值内容
|
|
258
|
+
valueObj.value
|
|
259
|
+
]
|
|
260
|
+
}))
|
|
261
|
+
]
|
|
262
|
+
}))
|
|
263
|
+
]
|
|
264
|
+
};
|
|
265
|
+
// 生成 XML(关闭自动声明头)
|
|
266
|
+
const xmlString = xml([attributeStatement], { declaration: false });
|
|
267
|
+
return xmlString.trim();
|
|
268
|
+
},
|
|
269
|
+
/**
|
|
270
|
+
* @desc Construct the XML signature for POST binding
|
|
271
|
+
* @param {string} rawSamlMessage request/response xml string
|
|
272
|
+
* @param {string} referenceTagXPath reference uri
|
|
273
|
+
* @param {string} privateKey declares the private key
|
|
274
|
+
* @param {string} passphrase passphrase of the private key [optional]
|
|
275
|
+
* @param {string|buffer} signingCert signing certificate
|
|
276
|
+
* @param {string} signatureAlgorithm signature algorithm
|
|
277
|
+
* @param {string[]} transformationAlgorithms canonicalization and transformation Algorithms
|
|
278
|
+
* @return {string} base64 encoded string
|
|
279
|
+
*/
|
|
280
|
+
constructSAMLSignature(opts) {
|
|
281
|
+
const { rawSamlMessage, referenceTagXPath, privateKey, privateKeyPass, signatureAlgorithm = signatureAlgorithms.RSA_SHA512, transformationAlgorithms = [
|
|
282
|
+
'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
|
|
283
|
+
'http://www.w3.org/2001/10/xml-exc-c14n#',
|
|
284
|
+
], signingCert, signatureConfig, isBase64Output = true, isMessageSigned = false, } = opts;
|
|
285
|
+
const sig = new SignedXml();
|
|
286
|
+
// Add assertion sections as reference
|
|
287
|
+
const digestAlgorithm = getDigestMethod(signatureAlgorithm);
|
|
288
|
+
if (referenceTagXPath) {
|
|
289
|
+
sig.addReference({
|
|
290
|
+
xpath: referenceTagXPath,
|
|
291
|
+
transforms: transformationAlgorithms,
|
|
292
|
+
digestAlgorithm: digestAlgorithm
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
if (isMessageSigned) {
|
|
296
|
+
sig.addReference({
|
|
297
|
+
// reference to the root node
|
|
298
|
+
xpath: '/*',
|
|
299
|
+
transforms: transformationAlgorithms,
|
|
300
|
+
digestAlgorithm
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
sig.signatureAlgorithm = signatureAlgorithm;
|
|
304
|
+
sig.publicCert = this.getKeyInfo(signingCert, signatureConfig).getKey();
|
|
305
|
+
sig.getKeyInfoContent = this.getKeyInfo(signingCert, signatureConfig).getKeyInfo;
|
|
306
|
+
sig.privateKey = utility.readPrivateKey(privateKey, privateKeyPass, true);
|
|
307
|
+
sig.canonicalizationAlgorithm = 'http://www.w3.org/2001/10/xml-exc-c14n#';
|
|
308
|
+
if (signatureConfig) {
|
|
309
|
+
sig.computeSignature(rawSamlMessage, signatureConfig);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
sig.computeSignature(rawSamlMessage);
|
|
313
|
+
}
|
|
314
|
+
return isBase64Output !== false ? utility.base64Encode(sig.getSignedXml()) : sig.getSignedXml();
|
|
315
|
+
},
|
|
316
|
+
/**
|
|
317
|
+
* @desc Verify the XML signature
|
|
318
|
+
* @param {string} xml xml
|
|
319
|
+
* @param {SignatureVerifierOptions} opts cert declares the X509 certificate
|
|
320
|
+
* @return {[boolean, string | null]} - A tuple where:
|
|
321
|
+
* - The first element is `true` if the signature is valid, `false` otherwise.
|
|
322
|
+
* - The second element is the cryptographically authenticated assertion node as a string, or `null` if not found.
|
|
323
|
+
*/
|
|
324
|
+
// tslint:disable-next-line:no-shadowed-variable
|
|
325
|
+
verifySignature(xml, opts) {
|
|
326
|
+
const { dom } = getContext();
|
|
327
|
+
const doc = dom.parseFromString(xml);
|
|
328
|
+
const docParser = new DOMParser();
|
|
329
|
+
// In order to avoid the wrapping attack, we have changed to use absolute xpath instead of naively fetching the signature element
|
|
330
|
+
// message signature (logout response / saml response)
|
|
331
|
+
const messageSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Signature']";
|
|
332
|
+
// assertion signature (logout response / saml response)
|
|
333
|
+
const assertionSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']/*[local-name(.)='Signature']";
|
|
334
|
+
// check if there is a potential malicious wrapping signature
|
|
335
|
+
const wrappingElementsXPath = "/*[contains(local-name(), 'Response')]/*[local-name(.)='Assertion']/*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']//*[local-name(.)='Assertion' or local-name(.)='Signature']";
|
|
336
|
+
// select the signature node
|
|
337
|
+
let selection = [];
|
|
338
|
+
const messageSignatureNode = select(messageSignatureXpath, doc);
|
|
339
|
+
const assertionSignatureNode = select(assertionSignatureXpath, doc);
|
|
340
|
+
const wrappingElementNode = select(wrappingElementsXPath, doc);
|
|
341
|
+
selection = selection.concat(messageSignatureNode);
|
|
342
|
+
selection = selection.concat(assertionSignatureNode);
|
|
343
|
+
// try to catch potential wrapping attack
|
|
344
|
+
if (wrappingElementNode.length !== 0) {
|
|
345
|
+
throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
|
|
346
|
+
}
|
|
347
|
+
// guarantee to have a signature in saml response
|
|
348
|
+
if (selection.length === 0) {
|
|
349
|
+
throw new Error('ERR_ZERO_SIGNATURE');
|
|
350
|
+
}
|
|
351
|
+
// need to refactor later on
|
|
352
|
+
for (const signatureNode of selection) {
|
|
353
|
+
const sig = new SignedXml();
|
|
354
|
+
let verified = false;
|
|
355
|
+
sig.signatureAlgorithm = opts.signatureAlgorithm;
|
|
356
|
+
if (!opts.keyFile && !opts.metadata) {
|
|
357
|
+
throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
|
|
358
|
+
}
|
|
359
|
+
if (opts.keyFile) {
|
|
360
|
+
sig.publicCert = fs.readFileSync(opts.keyFile);
|
|
361
|
+
}
|
|
362
|
+
if (opts.metadata) {
|
|
363
|
+
const certificateNode = select(".//*[local-name(.)='X509Certificate']", signatureNode);
|
|
364
|
+
// certificate in metadata
|
|
365
|
+
let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
|
|
366
|
+
// flattens the nested array of Certificates from each KeyDescriptor
|
|
367
|
+
if (Array.isArray(metadataCert)) {
|
|
368
|
+
metadataCert = flattenDeep(metadataCert);
|
|
369
|
+
}
|
|
370
|
+
else if (typeof metadataCert === 'string') {
|
|
371
|
+
metadataCert = [metadataCert];
|
|
372
|
+
}
|
|
373
|
+
// normalise the certificate string
|
|
374
|
+
metadataCert = metadataCert.map(utility.normalizeCerString);
|
|
375
|
+
// no certificate in node response nor metadata
|
|
376
|
+
if (certificateNode.length === 0 && metadataCert.length === 0) {
|
|
377
|
+
throw new Error('NO_SELECTED_CERTIFICATE');
|
|
378
|
+
}
|
|
379
|
+
// certificate node in response
|
|
380
|
+
if (certificateNode.length !== 0) {
|
|
381
|
+
const x509CertificateData = certificateNode[0].firstChild.data;
|
|
382
|
+
const x509Certificate = utility.normalizeCerString(x509CertificateData);
|
|
383
|
+
if (metadataCert.length >= 1 &&
|
|
384
|
+
!metadataCert.find(cert => cert.trim() === x509Certificate.trim())) {
|
|
385
|
+
// keep this restriction for rolling certificate usage
|
|
386
|
+
// to make sure the response certificate is one of those specified in metadata
|
|
387
|
+
throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
|
|
388
|
+
}
|
|
389
|
+
sig.publicCert = this.getKeyInfo(x509Certificate).getKey();
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
// Select first one from metadata
|
|
393
|
+
sig.publicCert = this.getKeyInfo(metadataCert[0]).getKey();
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
sig.loadSignature(signatureNode);
|
|
397
|
+
doc.removeChild(signatureNode);
|
|
398
|
+
verified = sig.checkSignature(doc.toString());
|
|
399
|
+
// immediately throw error when any one of the signature is failed to get verified
|
|
400
|
+
if (!verified) {
|
|
401
|
+
throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
|
|
402
|
+
}
|
|
403
|
+
// attempt is made to get the signed Reference as a string();
|
|
404
|
+
// note, we don't have access to the actual signedReferences API unfortunately
|
|
405
|
+
// mainly a sanity check here for SAML. (Although ours would still be secure, if multiple references are used)
|
|
406
|
+
if (!(sig.getSignedReferences().length >= 1)) {
|
|
407
|
+
throw new Error('NO_SIGNATURE_REFERENCES');
|
|
408
|
+
}
|
|
409
|
+
const signedVerifiedXML = sig.getSignedReferences()[0];
|
|
410
|
+
const rootNode = docParser.parseFromString(signedVerifiedXML, 'text/xml').documentElement;
|
|
411
|
+
// process the verified signature:
|
|
412
|
+
// case 1, rootSignedDoc is a response:
|
|
413
|
+
if (rootNode.localName === 'Response') {
|
|
414
|
+
// try getting the Xml from the first assertion
|
|
415
|
+
const EncryptedAssertions = select("./*[local-name()='EncryptedAssertion']", rootNode);
|
|
416
|
+
const assertions = select("./*[local-name()='Assertion']", rootNode);
|
|
417
|
+
// now we can process the assertion as an assertion
|
|
418
|
+
if (EncryptedAssertions.length === 1) {
|
|
419
|
+
return [true, EncryptedAssertions[0].toString()];
|
|
420
|
+
}
|
|
421
|
+
if (assertions.length === 1) {
|
|
422
|
+
return [true, assertions[0].toString()];
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else if (rootNode.localName === 'Assertion') {
|
|
426
|
+
return [true, rootNode.toString()];
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
return [true, null]; // signature is valid. But there is no assertion node here. It could be metadata node, hence return null
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// something has gone seriously wrong if we are still here
|
|
433
|
+
throw new Error('ERR_ZERO_SIGNATURE');
|
|
434
|
+
// response must be signed, either entire document or assertion
|
|
435
|
+
// default we will take the assertion section under root
|
|
436
|
+
/* if (messageSignatureNode.length === 1) {
|
|
437
|
+
const node = select("/!*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/!*[local-name(.)='Assertion']", doc);
|
|
438
|
+
if (node.length === 1) {
|
|
439
|
+
assertionNode = node[0].toString();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (assertionSignatureNode.length === 1) {
|
|
444
|
+
const verifiedAssertionInfo = extract(assertionSignatureNode[0].toString(), [{
|
|
445
|
+
key: 'refURI',
|
|
446
|
+
localPath: ['Signature', 'SignedInfo', 'Reference'],
|
|
447
|
+
attributes: ['URI']
|
|
448
|
+
}]);
|
|
449
|
+
// get the assertion supposed to be the one should be verified
|
|
450
|
+
const desiredAssertionInfo = extract(doc.toString(), [{
|
|
451
|
+
key: 'id',
|
|
452
|
+
localPath: ['~Response', 'Assertion'],
|
|
453
|
+
attributes: ['ID']
|
|
454
|
+
}]);
|
|
455
|
+
// 5.4.2 References
|
|
456
|
+
// SAML assertions and protocol messages MUST supply a value for the ID attribute on the root element of
|
|
457
|
+
// the assertion or protocol message being signed. The assertion’s or protocol message's root element may
|
|
458
|
+
// or may not be the root element of the actual XML document containing the signed assertion or protocol
|
|
459
|
+
// message (e.g., it might be contained within a SOAP envelope).
|
|
460
|
+
// Signatures MUST contain a single <ds:Reference> containing a same-document reference to the ID
|
|
461
|
+
// attribute value of the root element of the assertion or protocol message being signed. For example, if the
|
|
462
|
+
// ID attribute value is "foo", then the URI attribute in the <ds:Reference> element MUST be "#foo".
|
|
463
|
+
if (verifiedAssertionInfo.refURI !== `#${desiredAssertionInfo.id}`) {
|
|
464
|
+
throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
|
|
465
|
+
}
|
|
466
|
+
const verifiedDoc = extract(doc.toString(), [{
|
|
467
|
+
key: 'assertion',
|
|
468
|
+
localPath: ['~Response', 'Assertion'],
|
|
469
|
+
attributes: [],
|
|
470
|
+
context: true
|
|
471
|
+
}]);
|
|
472
|
+
assertionNode = verifiedDoc.assertion.toString();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return [verified, assertionNode];*/
|
|
476
|
+
},
|
|
477
|
+
/**
|
|
478
|
+
* @desc Helper function to create the key section in metadata (abstraction for signing and encrypt use)
|
|
479
|
+
* @param {string} use type of certificate (e.g. signing, encrypt)
|
|
480
|
+
* @param {string} certString declares the certificate String
|
|
481
|
+
* @return {object} object used in xml module
|
|
482
|
+
*/
|
|
483
|
+
createKeySection(use, certString) {
|
|
484
|
+
return {
|
|
485
|
+
['KeyDescriptor']: [
|
|
486
|
+
{
|
|
487
|
+
_attr: { use },
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
['ds:KeyInfo']: [
|
|
491
|
+
{
|
|
492
|
+
_attr: {
|
|
493
|
+
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
['ds:X509Data']: [{
|
|
498
|
+
'ds:X509Certificate': utility.normalizeCerString(certString),
|
|
499
|
+
}],
|
|
500
|
+
},
|
|
501
|
+
],
|
|
502
|
+
}
|
|
503
|
+
],
|
|
504
|
+
};
|
|
505
|
+
},
|
|
506
|
+
/**
|
|
507
|
+
* SAML 消息签名 (符合 SAML V2.0 绑定规范)
|
|
508
|
+
* @param octetString - 要签名的原始数据 (OCTET STRING)
|
|
509
|
+
* @param key - PEM 格式私钥
|
|
510
|
+
* @param passphrase - 私钥密码 (如果有加密)
|
|
511
|
+
* @param isBase64 - 是否返回 base64 编码 (默认 true)
|
|
512
|
+
* @param signingAlgorithm - 签名算法 (默认 'rsa-sha256')
|
|
513
|
+
* @returns 消息签名
|
|
514
|
+
*/
|
|
515
|
+
constructMessageSignature(octetString, key, passphrase, isBase64 = true, signingAlgorithm = nrsaAliasMappingForNode[signatureAlgorithms.RSA_SHA256]) {
|
|
516
|
+
try {
|
|
517
|
+
// 1. 标准化输入数据
|
|
518
|
+
const inputData = Buffer.isBuffer(octetString)
|
|
519
|
+
? octetString
|
|
520
|
+
: Buffer.from(octetString, 'utf8');
|
|
521
|
+
// 2. 创建签名器并设置算
|
|
522
|
+
const signingAlgorithmValue = getSigningSchemeForNode(signingAlgorithm);
|
|
523
|
+
const signer = createSign(signingAlgorithmValue);
|
|
524
|
+
// 3. 加载私钥
|
|
525
|
+
const privateKey = createPrivateKey({
|
|
526
|
+
key: key,
|
|
527
|
+
format: 'pem',
|
|
528
|
+
passphrase: passphrase,
|
|
529
|
+
encoding: 'utf8'
|
|
530
|
+
});
|
|
531
|
+
signer.write(octetString);
|
|
532
|
+
signer.end();
|
|
533
|
+
const signature = signer.sign(privateKey, 'base64');
|
|
534
|
+
console.log(signature.toString());
|
|
535
|
+
console.log('dayingyixia');
|
|
536
|
+
// 5. 处理编码输出
|
|
537
|
+
return isBase64 ? signature.toString() : signature;
|
|
538
|
+
}
|
|
539
|
+
catch (error) {
|
|
540
|
+
throw new Error(`SAML 签名失败: ${error.message}`);
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
verifyMessageSignature(metadata, octetString, signature, verifyAlgorithm) {
|
|
544
|
+
const signCert = metadata.getX509Certificate(certUse.signing);
|
|
545
|
+
const signingScheme = getSigningSchemeForNode(verifyAlgorithm);
|
|
546
|
+
const verifier = createVerify(signingScheme);
|
|
547
|
+
verifier.update(octetString);
|
|
548
|
+
const isValid = verifier.verify(utility.getPublicKeyPemFromCertificate(signCert), Buffer.isBuffer(signature) ? signature : Buffer.from(signature, 'base64'));
|
|
549
|
+
console.log(isValid);
|
|
550
|
+
console.log('-------------签名验证结果-------------');
|
|
551
|
+
return isValid;
|
|
552
|
+
},
|
|
553
|
+
/**
|
|
554
|
+
* @desc Get the public key in string format
|
|
555
|
+
* @param {string} x509Certificate certificate
|
|
556
|
+
* @return {string} public key
|
|
557
|
+
*/
|
|
558
|
+
getKeyInfo(x509Certificate, signatureConfig = {}) {
|
|
559
|
+
const prefix = signatureConfig.prefix ? `${signatureConfig.prefix}:` : '';
|
|
560
|
+
return {
|
|
561
|
+
getKeyInfo: () => {
|
|
562
|
+
return `<${prefix}X509Data><${prefix}X509Certificate>${x509Certificate}</${prefix}X509Certificate></${prefix}X509Data>`;
|
|
563
|
+
},
|
|
564
|
+
getKey: () => {
|
|
565
|
+
return utility.getPublicKeyPemFromCertificate(x509Certificate).toString();
|
|
566
|
+
},
|
|
567
|
+
};
|
|
568
|
+
},
|
|
569
|
+
/**
|
|
570
|
+
* @desc Encrypt the assertion section in Response
|
|
571
|
+
* @param {Entity} sourceEntity source entity
|
|
572
|
+
* @param {Entity} targetEntity target entity
|
|
573
|
+
* @param {string} xml response in xml string format
|
|
574
|
+
* @return {Promise} a promise to resolve the finalized xml
|
|
575
|
+
*/
|
|
576
|
+
// tslint:disable-next-line:no-shadowed-variable
|
|
577
|
+
encryptAssertion(sourceEntity, targetEntity, xml) {
|
|
578
|
+
// Implement encryption after signature if it has
|
|
579
|
+
return new Promise((resolve, reject) => {
|
|
580
|
+
if (!xml) {
|
|
581
|
+
return reject(new Error('ERR_UNDEFINED_ASSERTION'));
|
|
582
|
+
}
|
|
583
|
+
const sourceEntitySetting = sourceEntity.entitySetting;
|
|
584
|
+
const targetEntityMetadata = targetEntity.entityMeta;
|
|
585
|
+
const { dom } = getContext();
|
|
586
|
+
const doc = dom.parseFromString(xml);
|
|
587
|
+
const assertions = select("//*[local-name(.)='Assertion']", doc);
|
|
588
|
+
if (!Array.isArray(assertions) || assertions.length === 0) {
|
|
589
|
+
throw new Error('ERR_NO_ASSERTION');
|
|
590
|
+
}
|
|
591
|
+
if (assertions.length > 1) {
|
|
592
|
+
throw new Error('ERR_MULTIPLE_ASSERTION');
|
|
593
|
+
}
|
|
594
|
+
const rawAssertionNode = assertions[0];
|
|
595
|
+
// Perform encryption depends on the setting, default is false
|
|
596
|
+
if (sourceEntitySetting.isAssertionEncrypted) {
|
|
597
|
+
const publicKeyPem = utility.getPublicKeyPemFromCertificate(targetEntityMetadata.getX509Certificate(certUse.encrypt));
|
|
598
|
+
xmlenc.encrypt(rawAssertionNode.toString(), {
|
|
599
|
+
// use xml-encryption module
|
|
600
|
+
rsa_pub: Buffer.from(publicKeyPem), // public key from certificate
|
|
601
|
+
pem: Buffer.from(`-----BEGIN CERTIFICATE-----${targetEntityMetadata.getX509Certificate(certUse.encrypt)}-----END CERTIFICATE-----`),
|
|
602
|
+
encryptionAlgorithm: sourceEntitySetting.dataEncryptionAlgorithm,
|
|
603
|
+
keyEncryptionAlgorithm: sourceEntitySetting.keyEncryptionAlgorithm,
|
|
604
|
+
keyEncryptionDigest: 'SHA-512',
|
|
605
|
+
disallowEncryptionWithInsecureAlgorithm: true,
|
|
606
|
+
warnInsecureAlgorithm: true
|
|
607
|
+
}, (err, res) => {
|
|
608
|
+
if (err) {
|
|
609
|
+
console.error(err);
|
|
610
|
+
return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_ENCRYPTION'));
|
|
611
|
+
}
|
|
612
|
+
if (!res) {
|
|
613
|
+
return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
|
|
614
|
+
}
|
|
615
|
+
const { encryptedAssertion: encAssertionPrefix } = sourceEntitySetting.tagPrefix;
|
|
616
|
+
const encryptAssertionDoc = dom.parseFromString(`<${encAssertionPrefix}:EncryptedAssertion xmlns:${encAssertionPrefix}="${namespace.names.assertion}">${res}</${encAssertionPrefix}:EncryptedAssertion>`);
|
|
617
|
+
doc.documentElement.replaceChild(encryptAssertionDoc.documentElement, rawAssertionNode);
|
|
618
|
+
return resolve(utility.base64Encode(doc.toString()));
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
return resolve(utility.base64Encode(xml)); // No need to do encryption
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
},
|
|
626
|
+
/**
|
|
627
|
+
* @desc Decrypt the assertion section in Response
|
|
628
|
+
* @param {string} type only accept SAMLResponse to proceed decryption
|
|
629
|
+
* @param {Entity} here this entity
|
|
630
|
+
* @param {Entity} from from the entity where the message is sent
|
|
631
|
+
* @param {string} entireXML response in xml string format
|
|
632
|
+
* @return {function} a promise to get back the entire xml with decrypted assertion
|
|
633
|
+
*/
|
|
634
|
+
decryptAssertion(here, entireXML) {
|
|
635
|
+
return new Promise((resolve, reject) => {
|
|
636
|
+
// Implement decryption first then check the signature
|
|
637
|
+
if (!entireXML) {
|
|
638
|
+
return reject(new Error('ERR_UNDEFINED_ASSERTION'));
|
|
639
|
+
}
|
|
640
|
+
// Perform encryption depends on the setting of where the message is sent, default is false
|
|
641
|
+
const hereSetting = here.entitySetting;
|
|
642
|
+
const { dom } = getContext();
|
|
643
|
+
const doc = dom.parseFromString(entireXML);
|
|
644
|
+
const encryptedAssertions = select("/*[contains(local-name(), 'Response')]/*[local-name(.)='EncryptedAssertion']", doc);
|
|
645
|
+
if (!Array.isArray(encryptedAssertions) || encryptedAssertions.length === 0) {
|
|
646
|
+
throw new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION');
|
|
647
|
+
}
|
|
648
|
+
if (encryptedAssertions.length > 1) {
|
|
649
|
+
throw new Error('ERR_MULTIPLE_ASSERTION');
|
|
650
|
+
}
|
|
651
|
+
const encAssertionNode = encryptedAssertions[0];
|
|
652
|
+
return xmlenc.decrypt(encAssertionNode.toString(), {
|
|
653
|
+
key: utility.readPrivateKey(hereSetting.encPrivateKey, hereSetting.encPrivateKeyPass),
|
|
654
|
+
}, (err, res) => {
|
|
655
|
+
if (err) {
|
|
656
|
+
console.error(err);
|
|
657
|
+
return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_DECRYPTION'));
|
|
658
|
+
}
|
|
659
|
+
if (!res) {
|
|
660
|
+
return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
|
|
661
|
+
}
|
|
662
|
+
const rawAssertionDoc = dom.parseFromString(res);
|
|
663
|
+
doc.documentElement.replaceChild(rawAssertionDoc.documentElement, encAssertionNode);
|
|
664
|
+
return resolve([doc.toString(), res]);
|
|
665
|
+
});
|
|
666
|
+
});
|
|
667
|
+
},
|
|
668
|
+
/**
|
|
669
|
+
* @desc Check if the xml string is valid and bounded
|
|
670
|
+
*/
|
|
671
|
+
async isValidXml(input) {
|
|
672
|
+
// check if global api contains the validate function
|
|
673
|
+
const { validate } = getContext();
|
|
674
|
+
/**
|
|
675
|
+
* user can write a validate function that always returns
|
|
676
|
+
* a resolved promise and skip the validator even in
|
|
677
|
+
* production, user will take the responsibility if
|
|
678
|
+
* they intend to skip the validation
|
|
679
|
+
*/
|
|
680
|
+
if (!validate) {
|
|
681
|
+
// otherwise, an error will be thrown
|
|
682
|
+
return Promise.reject('Your application is potentially vulnerable because no validation function found. Please read the documentation on how to setup the validator. (https://github.com/tngan/samlify#installation)');
|
|
683
|
+
}
|
|
684
|
+
try {
|
|
685
|
+
return await validate(input);
|
|
686
|
+
}
|
|
687
|
+
catch (e) {
|
|
688
|
+
throw e;
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
};
|
|
692
|
+
};
|
|
693
|
+
export default libSaml();
|
|
722
694
|
//# sourceMappingURL=libsaml.js.map
|