samlesa 2.12.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/.editorconfig +19 -0
  2. package/.github/FUNDING.yml +1 -0
  3. package/.idea/compiler.xml +6 -0
  4. package/.idea/deployment.xml +14 -0
  5. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  6. package/.idea/jsLibraryMappings.xml +6 -0
  7. package/.idea/modules.xml +8 -0
  8. package/.idea/samlify.iml +12 -0
  9. package/.idea/vcs.xml +6 -0
  10. package/.pre-commit.sh +15 -0
  11. package/.snyk +8 -0
  12. package/.travis.yml +29 -0
  13. package/LICENSE +22 -0
  14. package/Makefile +25 -0
  15. package/README.md +84 -0
  16. package/build/.idea/workspace.xml +58 -0
  17. package/build/index.js +65 -0
  18. package/build/index.js.map +1 -0
  19. package/build/src/api.js +24 -0
  20. package/build/src/api.js.map +1 -0
  21. package/build/src/binding-post.js +369 -0
  22. package/build/src/binding-post.js.map +1 -0
  23. package/build/src/binding-redirect.js +333 -0
  24. package/build/src/binding-redirect.js.map +1 -0
  25. package/build/src/binding-simplesign.js +233 -0
  26. package/build/src/binding-simplesign.js.map +1 -0
  27. package/build/src/entity-idp.js +131 -0
  28. package/build/src/entity-idp.js.map +1 -0
  29. package/build/src/entity-sp.js +97 -0
  30. package/build/src/entity-sp.js.map +1 -0
  31. package/build/src/entity.js +236 -0
  32. package/build/src/entity.js.map +1 -0
  33. package/build/src/extractor.js +370 -0
  34. package/build/src/extractor.js.map +1 -0
  35. package/build/src/flow.js +320 -0
  36. package/build/src/flow.js.map +1 -0
  37. package/build/src/libsaml.js +642 -0
  38. package/build/src/libsaml.js.map +1 -0
  39. package/build/src/metadata-idp.js +128 -0
  40. package/build/src/metadata-idp.js.map +1 -0
  41. package/build/src/metadata-sp.js +232 -0
  42. package/build/src/metadata-sp.js.map +1 -0
  43. package/build/src/metadata.js +177 -0
  44. package/build/src/metadata.js.map +1 -0
  45. package/build/src/types.js +12 -0
  46. package/build/src/types.js.map +1 -0
  47. package/build/src/urn.js +213 -0
  48. package/build/src/urn.js.map +1 -0
  49. package/build/src/utility.js +249 -0
  50. package/build/src/utility.js.map +1 -0
  51. package/build/src/validator.js +27 -0
  52. package/build/src/validator.js.map +1 -0
  53. package/index.d.ts +10 -0
  54. package/index.js +19 -0
  55. package/index.js.map +1 -0
  56. package/index.ts +28 -0
  57. package/package.json +74 -0
  58. package/qodana.yaml +29 -0
  59. package/src/.idea/modules.xml +8 -0
  60. package/src/.idea/src.iml +12 -0
  61. package/src/.idea/vcs.xml +6 -0
  62. package/src/api.ts +36 -0
  63. package/src/binding-post.ts +338 -0
  64. package/src/binding-redirect.ts +331 -0
  65. package/src/binding-simplesign.ts +231 -0
  66. package/src/entity-idp.ts +145 -0
  67. package/src/entity-sp.ts +114 -0
  68. package/src/entity.ts +243 -0
  69. package/src/extractor.ts +392 -0
  70. package/src/flow.ts +467 -0
  71. package/src/libsaml.ts +786 -0
  72. package/src/metadata-idp.ts +146 -0
  73. package/src/metadata-sp.ts +268 -0
  74. package/src/metadata.ts +166 -0
  75. package/src/types.ts +153 -0
  76. package/src/urn.ts +211 -0
  77. package/src/utility.ts +248 -0
  78. package/src/validator.ts +44 -0
  79. package/tsconfig.json +38 -0
  80. package/tslint.json +35 -0
  81. package/types/index.d.ts +10 -0
  82. package/types/src/api.d.ts +13 -0
  83. package/types/src/binding-post.d.ts +46 -0
  84. package/types/src/binding-redirect.d.ts +52 -0
  85. package/types/src/binding-simplesign.d.ts +39 -0
  86. package/types/src/entity-idp.d.ts +42 -0
  87. package/types/src/entity-sp.d.ts +36 -0
  88. package/types/src/entity.d.ts +99 -0
  89. package/types/src/extractor.d.ts +25 -0
  90. package/types/src/flow.d.ts +6 -0
  91. package/types/src/libsaml.d.ts +210 -0
  92. package/types/src/metadata-idp.d.ts +24 -0
  93. package/types/src/metadata-sp.d.ts +36 -0
  94. package/types/src/metadata.d.ts +57 -0
  95. package/types/src/types.d.ts +127 -0
  96. package/types/src/urn.d.ts +194 -0
  97. package/types/src/utility.d.ts +134 -0
  98. package/types/src/validator.d.ts +3 -0
  99. package/types.d.ts +2 -0
@@ -0,0 +1,392 @@
1
+ import { select, SelectedValue } from 'xpath';
2
+ import { uniq, last, zipObject, notEmpty } from './utility.js';
3
+ import { getContext } from './api.js';
4
+ import camelCase from 'camelcase';
5
+
6
+ interface ExtractorField {
7
+ key: string;
8
+ localPath: string[] | string[][];
9
+ attributes: string[];
10
+ index?: string[];
11
+ attributePath?: string[];
12
+ context?: boolean;
13
+ }
14
+
15
+ export type ExtractorFields = ExtractorField[];
16
+
17
+ function buildAbsoluteXPath(paths) {
18
+ return paths.reduce((currentPath, name) => {
19
+ let appendedPath = currentPath;
20
+ const isWildcard = name.startsWith('~');
21
+ if (isWildcard) {
22
+ const pathName = name.replace('~', '');
23
+ appendedPath = currentPath + `/*[contains(local-name(), '${pathName}')]`;
24
+ }
25
+ if (!isWildcard) {
26
+ appendedPath = currentPath + `/*[local-name(.)='${name}']`;
27
+ }
28
+ return appendedPath;
29
+ }, '');
30
+ }
31
+
32
+ function buildAttributeXPath(attributes) {
33
+ if (attributes.length === 0) {
34
+ return '/text()';
35
+ }
36
+ if (attributes.length === 1) {
37
+ return `/@${attributes[0]}`;
38
+ }
39
+ const filters = attributes.map(attribute => `name()='${attribute}'`).join(' or ');
40
+ return `/@*[${filters}]`;
41
+ }
42
+
43
+ export const loginRequestFields: ExtractorFields = [
44
+ {
45
+ key: 'request',
46
+ localPath: ['AuthnRequest'],
47
+ attributes: ['ID', 'IssueInstant', 'Destination', 'AssertionConsumerServiceURL']
48
+ },
49
+ {
50
+ key: 'issuer',
51
+ localPath: ['AuthnRequest', 'Issuer'],
52
+ attributes: []
53
+ },
54
+ {
55
+ key: 'nameIDPolicy',
56
+ localPath: ['AuthnRequest', 'NameIDPolicy'],
57
+ attributes: ['Format', 'AllowCreate']
58
+ },
59
+ {
60
+ key: 'authnContextClassRef',
61
+ localPath: ['AuthnRequest', 'AuthnContextClassRef'],
62
+ attributes: []
63
+ },
64
+ {
65
+ key: 'signature',
66
+ localPath: ['AuthnRequest', 'Signature'],
67
+ attributes: [],
68
+ context: true
69
+ }
70
+ ];
71
+
72
+ // support two-tiers status code
73
+ export const loginResponseStatusFields = [
74
+ {
75
+ key: 'top',
76
+ localPath: ['Response', 'Status', 'StatusCode'],
77
+ attributes: ['Value'],
78
+ },
79
+ {
80
+ key: 'second',
81
+ localPath: ['Response', 'Status', 'StatusCode', 'StatusCode'],
82
+ attributes: ['Value'],
83
+ }
84
+ ];
85
+
86
+ // support two-tiers status code
87
+ export const logoutResponseStatusFields = [
88
+ {
89
+ key: 'top',
90
+ localPath: ['LogoutResponse', 'Status', 'StatusCode'],
91
+ attributes: ['Value']
92
+ },
93
+ {
94
+ key: 'second',
95
+ localPath: ['LogoutResponse', 'Status', 'StatusCode', 'StatusCode'],
96
+ attributes: ['Value'],
97
+ }
98
+ ];
99
+
100
+ export const loginResponseFields: ((assertion: any) => ExtractorFields) = assertion => [
101
+ {
102
+ key: 'conditions',
103
+ localPath: ['Assertion', 'Conditions'],
104
+ attributes: ['NotBefore', 'NotOnOrAfter'],
105
+ shortcut: assertion
106
+ },
107
+ {
108
+ key: 'response',
109
+ localPath: ['Response'],
110
+ attributes: ['ID', 'IssueInstant', 'Destination', 'InResponseTo'],
111
+ },
112
+ {
113
+ key: 'audience',
114
+ localPath: ['Assertion', 'Conditions', 'AudienceRestriction', 'Audience'],
115
+ attributes: [],
116
+ shortcut: assertion
117
+ },
118
+ // {
119
+ // key: 'issuer',
120
+ // localPath: ['Response', 'Issuer'],
121
+ // attributes: []
122
+ // },
123
+ {
124
+ key: 'issuer',
125
+ localPath: ['Assertion', 'Issuer'],
126
+ attributes: [],
127
+ shortcut: assertion
128
+ },
129
+ {
130
+ key: 'nameID',
131
+ localPath: ['Assertion', 'Subject', 'NameID'],
132
+ attributes: [],
133
+ shortcut: assertion
134
+ },
135
+ {
136
+ key: 'sessionIndex',
137
+ localPath: ['Assertion', 'AuthnStatement'],
138
+ attributes: ['AuthnInstant', 'SessionNotOnOrAfter', 'SessionIndex'],
139
+ shortcut: assertion
140
+ },
141
+ {
142
+ key: 'attributes',
143
+ localPath: ['Assertion', 'AttributeStatement', 'Attribute'],
144
+ index: ['Name'],
145
+ attributePath: ['AttributeValue'],
146
+ attributes: [],
147
+ shortcut: assertion
148
+ }
149
+ ];
150
+
151
+ export const logoutRequestFields: ExtractorFields = [
152
+ {
153
+ key: 'request',
154
+ localPath: ['LogoutRequest'],
155
+ attributes: ['ID', 'IssueInstant', 'Destination']
156
+ },
157
+ {
158
+ key: 'issuer',
159
+ localPath: ['LogoutRequest', 'Issuer'],
160
+ attributes: []
161
+ },
162
+ {
163
+ key: 'nameID',
164
+ localPath: ['LogoutRequest', 'NameID'],
165
+ attributes: []
166
+ },
167
+ {
168
+ key: 'sessionIndex',
169
+ localPath: ['LogoutRequest', 'SessionIndex'],
170
+ attributes: []
171
+ },
172
+ {
173
+ key: 'signature',
174
+ localPath: ['LogoutRequest', 'Signature'],
175
+ attributes: [],
176
+ context: true
177
+ }
178
+ ];
179
+
180
+ export const logoutResponseFields: ExtractorFields = [
181
+ {
182
+ key: 'response',
183
+ localPath: ['LogoutResponse'],
184
+ attributes: ['ID', 'Destination', 'InResponseTo']
185
+ },
186
+ {
187
+ key: 'issuer',
188
+ localPath: ['LogoutResponse', 'Issuer'],
189
+ attributes: []
190
+ },
191
+ {
192
+ key: 'signature',
193
+ localPath: ['LogoutResponse', 'Signature'],
194
+ attributes: [],
195
+ context: true
196
+ }
197
+ ];
198
+
199
+ export function extract(context: string, fields) {
200
+ const { dom } = getContext();
201
+ const rootDoc = dom.parseFromString(context);
202
+
203
+ return fields.reduce((result: any, field) => {
204
+ // get essential fields
205
+ const key = field.key;
206
+ const localPath = field.localPath;
207
+ const attributes = field.attributes;
208
+ const isEntire = field.context;
209
+ const shortcut = field.shortcut;
210
+ // get optional fields
211
+ const index = field.index;
212
+ const attributePath = field.attributePath;
213
+
214
+ // set allowing overriding if there is a shortcut injected
215
+ let targetDoc = rootDoc;
216
+
217
+ // if shortcut is used, then replace the doc
218
+ // it's a design for overriding the doc used during runtime
219
+ if (shortcut) {
220
+ targetDoc = dom.parseFromString(shortcut);
221
+ }
222
+
223
+ // special case: multiple path
224
+ /*
225
+ {
226
+ key: 'issuer',
227
+ localPath: [
228
+ ['Response', 'Issuer'],
229
+ ['Response', 'Assertion', 'Issuer']
230
+ ],
231
+ attributes: []
232
+ }
233
+ */
234
+ if (localPath.every(path => Array.isArray(path))) {
235
+ const multiXPaths = localPath
236
+ .map(path => {
237
+ // not support attribute yet, so ignore it
238
+ return `${buildAbsoluteXPath(path)}/text()`;
239
+ })
240
+ .join(' | ');
241
+
242
+ return {
243
+ ...result,
244
+ [key]: uniq(select(multiXPaths, targetDoc).map((n: Node) => n.nodeValue).filter(notEmpty))
245
+ };
246
+ }
247
+ // eo special case: multiple path
248
+
249
+ const baseXPath = buildAbsoluteXPath(localPath);
250
+ const attributeXPath = buildAttributeXPath(attributes);
251
+
252
+ // special case: get attributes where some are in child, some are in parent
253
+ /*
254
+ {
255
+ key: 'attributes',
256
+ localPath: ['Response', 'Assertion', 'AttributeStatement', 'Attribute'],
257
+ index: ['Name'],
258
+ attributePath: ['AttributeValue'],
259
+ attributes: []
260
+ }
261
+ */
262
+ if (index && attributePath) {
263
+ // find the index in localpath
264
+ const indexPath = buildAttributeXPath(index);
265
+ const fullLocalXPath = `${baseXPath}${indexPath}`;
266
+ const parentNodes = select(baseXPath, targetDoc);
267
+ // [uid, mail, edupersonaffiliation], ready for aggregate
268
+ const parentAttributes = select(fullLocalXPath, targetDoc).map((n: Attr) => n.value);
269
+ // [attribute, attributevalue]
270
+ const childXPath = buildAbsoluteXPath([last(localPath)].concat(attributePath));
271
+ const childAttributeXPath = buildAttributeXPath(attributes);
272
+ const fullChildXPath = `${childXPath}${childAttributeXPath}`;
273
+ // [ 'test', 'test@example.com', [ 'users', 'examplerole1' ] ]
274
+ const childAttributes = parentNodes.map(node => {
275
+ const nodeDoc = dom.parseFromString(node.toString());
276
+ if (attributes.length === 0) {
277
+ const childValues = select(fullChildXPath, nodeDoc).map((n: Node) => n.nodeValue);
278
+ if (childValues.length === 1) {
279
+ return childValues[0];
280
+ }
281
+ return childValues;
282
+ }
283
+ if (attributes.length > 0) {
284
+ const childValues = select(fullChildXPath, nodeDoc).map((n: Attr) => n.value);
285
+ if (childValues.length === 1) {
286
+ return childValues[0];
287
+ }
288
+ return childValues;
289
+ }
290
+ return null;
291
+ });
292
+ // aggregation
293
+ const obj = zipObject(parentAttributes, childAttributes, false);
294
+ return {
295
+ ...result,
296
+ [key]: obj
297
+ };
298
+
299
+ }
300
+ // case: fetch entire content, only allow one existence
301
+ /*
302
+ {
303
+ key: 'signature',
304
+ localPath: ['AuthnRequest', 'Signature'],
305
+ attributes: [],
306
+ context: true
307
+ }
308
+ */
309
+ if (isEntire) {
310
+ const node = select(baseXPath, targetDoc);
311
+ let value: string | string[] | null = null;
312
+ if (node.length === 1) {
313
+ value = node[0].toString();
314
+ }
315
+ if (node.length > 1) {
316
+ value = node.map(n => n.toString());
317
+ }
318
+ return {
319
+ ...result,
320
+ [key]: value
321
+ };
322
+ }
323
+
324
+ // case: multiple attribute
325
+ /*
326
+ {
327
+ key: 'nameIDPolicy',
328
+ localPath: ['AuthnRequest', 'NameIDPolicy'],
329
+ attributes: ['Format', 'AllowCreate']
330
+ }
331
+ */
332
+ if (attributes.length > 1) {
333
+ const baseNode = select(baseXPath, targetDoc).map(n => n.toString());
334
+ const childXPath = `${buildAbsoluteXPath([last(localPath)])}${attributeXPath}`;
335
+ const attributeValues = baseNode.map((node: string) => {
336
+ const nodeDoc = dom.parseFromString(node);
337
+ const values = select(childXPath, nodeDoc).reduce((r: any, n: Attr) => {
338
+ r[camelCase(n.name, {locale: 'en-us'})] = n.value;
339
+ return r;
340
+ }, {});
341
+ return values;
342
+ });
343
+ return {
344
+ ...result,
345
+ [key]: attributeValues.length === 1 ? attributeValues[0] : attributeValues
346
+ };
347
+ }
348
+ // case: single attribute
349
+ /*
350
+ {
351
+ key: 'statusCode',
352
+ localPath: ['Response', 'Status', 'StatusCode'],
353
+ attributes: ['Value'],
354
+ }
355
+ */
356
+ if (attributes.length === 1) {
357
+ const fullPath = `${baseXPath}${attributeXPath}`;
358
+ const attributeValues = select(fullPath, targetDoc).map((n: Attr) => n.value);
359
+ return {
360
+ ...result,
361
+ [key]: attributeValues[0]
362
+ };
363
+ }
364
+ // case: zero attribute
365
+ /*
366
+ {
367
+ key: 'issuer',
368
+ localPath: ['AuthnRequest', 'Issuer'],
369
+ attributes: []
370
+ }
371
+ */
372
+ if (attributes.length === 0) {
373
+ let attributeValue: SelectedValue[] | (string | null)[] | null = null;
374
+ const node = select(baseXPath, targetDoc);
375
+ if (node.length === 1) {
376
+ const fullPath = `string(${baseXPath}${attributeXPath})`;
377
+ attributeValue = select(fullPath, targetDoc);
378
+ }
379
+ if (node.length > 1) {
380
+ attributeValue = node.filter((n: Node) => n.firstChild)
381
+ .map((n: Node) => n.firstChild!.nodeValue);
382
+ }
383
+ return {
384
+ ...result,
385
+ [key]: attributeValue
386
+ };
387
+ }
388
+
389
+ return result;
390
+ }, {});
391
+
392
+ }