scimgateway 6.2.0 → 6.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -50,8 +50,17 @@ const scimgateway = new ScimGateway()
50
50
  const helper = new HelperRest(scimgateway)
51
51
  const config = scimgateway.getConfig()
52
52
  scimgateway.authPassThroughAllowed = false
53
+ scimgateway.pluginAndOrFilterEnabled = false
53
54
  // end - mandatory plugin initialization
54
55
 
56
+ const isAllowlistingUser = config.map?.user
57
+ ? Object.values(config.map.user).some((item: any) => typeof item?.valueMap === 'object' && Object.keys(item.valueMap).length > 0)
58
+ : false
59
+
60
+ const isAllowlistingGroup = config.map?.group
61
+ ? Object.values(config.map.group).some((item: any) => typeof item.valueMap === 'object' && Object.keys(item.valueMap).length > 0)
62
+ : false
63
+
55
64
  // =================================================
56
65
  // getUsers
57
66
  // =================================================
@@ -85,6 +94,11 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
85
94
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all users to be returned - correspond to exploreUsers() in versions < 4.x.x
86
95
  path = `/Users${(attrs.length > 0 ? '?attributes=' + attrs.join() : '')}`
87
96
  }
97
+ if (getObj.and || getObj.or) {
98
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
99
+ // we could have this logic above, if not it must be defined here
100
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
101
+ }
88
102
  // end mandatory if-else logic
89
103
 
90
104
  if (!path) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -114,16 +128,13 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
114
128
  totalResults: null,
115
129
  }
116
130
 
117
- const isAllowlisting = config.map?.user
118
- ? Object.values(config.map.user).some((item: any) => typeof item?.valueMap === 'object' && Object.keys(item.valueMap).length > 0)
119
- : false
120
- let currentStartIndex = isAllowlisting ? 1 : targetStartIndex
131
+ let currentStartIndex = isAllowlistingUser ? 1 : targetStartIndex
121
132
  let allValidResources: any[] = []
122
133
  let totalSkipped = 0
123
134
  let targetTotalResults: number | null = null
124
135
  let iteration = 0
125
136
  const maxIterations = 5 // Safety limit for look-ahead fetching
126
- const resourcesNeeded = isAllowlisting ? targetStartIndex + targetCount - 1 : targetCount
137
+ const resourcesNeeded = isAllowlistingUser ? targetStartIndex + targetCount - 1 : targetCount
127
138
 
128
139
  try {
129
140
  while (allValidResources.length < resourcesNeeded && iteration < maxIterations) {
@@ -166,8 +177,8 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
166
177
 
167
178
  if (targetTotalResults === null) {
168
179
  // Target endpoint returned full list
169
- ret.totalResults = isAllowlisting ? allValidResources.length : targetStartIndex - 1 + allValidResources.length
170
- ret.Resources = isAllowlisting ? allValidResources.slice(targetStartIndex - 1, targetStartIndex - 1 + targetCount) : allValidResources.slice(0, targetCount)
180
+ ret.totalResults = isAllowlistingUser ? allValidResources.length : targetStartIndex - 1 + allValidResources.length
181
+ ret.Resources = isAllowlistingUser ? allValidResources.slice(targetStartIndex - 1, targetStartIndex - 1 + targetCount) : allValidResources.slice(0, targetCount)
171
182
  return ret
172
183
  }
173
184
 
@@ -185,8 +196,8 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
185
196
  }
186
197
  if (ctx?.paging && Object.hasOwn(ctx.paging, 'totalResults')) targetTotalResults = ctx.paging.totalResults
187
198
 
188
- ret.totalResults = (targetTotalResults !== null && targetTotalResults > totalSkipped) ? targetTotalResults - totalSkipped : (isAllowlisting ? allValidResources.length : targetStartIndex - 1 + allValidResources.length)
189
- ret.Resources = isAllowlisting ? allValidResources.slice(targetStartIndex - 1, targetStartIndex - 1 + targetCount) : allValidResources.slice(0, targetCount)
199
+ ret.totalResults = (targetTotalResults !== null && targetTotalResults > totalSkipped) ? targetTotalResults - totalSkipped : (isAllowlistingUser ? allValidResources.length : targetStartIndex - 1 + allValidResources.length)
200
+ ret.Resources = isAllowlistingUser ? allValidResources.slice(targetStartIndex - 1, targetStartIndex - 1 + targetCount) : allValidResources.slice(0, targetCount)
190
201
  if (!ret.startIndex) ret.startIndex = targetStartIndex
191
202
 
192
203
  return ret
@@ -321,6 +332,11 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
321
332
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all groups to be returned - correspond to exploreGroups() in versions < 4.x.x
322
333
  path = `/Groups${(attrs.length > 0 ? '?attributes=' + attrs.join() : '')}`
323
334
  }
335
+ if (getObj.and || getObj.or) {
336
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
337
+ // we could have this logic above, if not it must be defined here
338
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
339
+ }
324
340
  // mandatory if-else logic - end
325
341
 
326
342
  if (!path) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -345,16 +361,13 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
345
361
  }
346
362
  */
347
363
 
348
- const isAllowlisting = config.map?.group
349
- ? Object.values(config.map.group).some((item: any) => typeof item.valueMap === 'object' && Object.keys(item.valueMap).length > 0)
350
- : false
351
- let currentStartIndex = isAllowlisting ? 1 : targetStartIndex
364
+ let currentStartIndex = isAllowlistingGroup ? 1 : targetStartIndex
352
365
  let allValidResources: any[] = []
353
366
  let totalSkipped = 0
354
367
  let targetTotalResults: number | null = null
355
368
  let iteration = 0
356
369
  const maxIterations = 5 // Safety limit for look-ahead fetching
357
- const resourcesNeeded = isAllowlisting ? targetStartIndex + targetCount - 1 : targetCount
370
+ const resourcesNeeded = isAllowlistingGroup ? targetStartIndex + targetCount - 1 : targetCount
358
371
 
359
372
  try {
360
373
  while (allValidResources.length < resourcesNeeded && iteration < maxIterations) {
@@ -399,8 +412,8 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
399
412
 
400
413
  if (targetTotalResults === null) {
401
414
  // Target endpoint returned full list
402
- ret.totalResults = isAllowlisting ? allValidResources.length : targetStartIndex - 1 + allValidResources.length
403
- ret.Resources = isAllowlisting ? allValidResources.slice(targetStartIndex - 1, targetStartIndex - 1 + targetCount) : allValidResources.slice(0, targetCount)
415
+ ret.totalResults = isAllowlistingGroup ? allValidResources.length : targetStartIndex - 1 + allValidResources.length
416
+ ret.Resources = isAllowlistingGroup ? allValidResources.slice(targetStartIndex - 1, targetStartIndex - 1 + targetCount) : allValidResources.slice(0, targetCount)
404
417
  return ret
405
418
  }
406
419
 
@@ -418,8 +431,8 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
418
431
  }
419
432
  if (ctx?.paging && Object.hasOwn(ctx.paging, 'totalResults')) targetTotalResults = ctx.paging.totalResults
420
433
 
421
- ret.totalResults = (targetTotalResults !== null && targetTotalResults > totalSkipped) ? targetTotalResults - totalSkipped : (isAllowlisting ? allValidResources.length : targetStartIndex - 1 + allValidResources.length)
422
- ret.Resources = isAllowlisting ? allValidResources.slice(targetStartIndex - 1, targetStartIndex - 1 + targetCount) : allValidResources.slice(0, targetCount)
434
+ ret.totalResults = (targetTotalResults !== null && targetTotalResults > totalSkipped) ? targetTotalResults - totalSkipped : (isAllowlistingGroup ? allValidResources.length : targetStartIndex - 1 + allValidResources.length)
435
+ ret.Resources = isAllowlistingGroup ? allValidResources.slice(targetStartIndex - 1, targetStartIndex - 1 + targetCount) : allValidResources.slice(0, targetCount)
423
436
  if (!ret.startIndex) ret.startIndex = targetStartIndex
424
437
 
425
438
  return ret
@@ -116,6 +116,7 @@ import { ScimGateway } from 'scimgateway'
116
116
  const scimgateway = new ScimGateway()
117
117
  const config = scimgateway.getConfig()
118
118
  scimgateway.authPassThroughAllowed = false
119
+ scimgateway.pluginAndOrFilterEnabled = false
119
120
  // end - mandatory plugin initialization
120
121
 
121
122
  // =================================================
@@ -123,9 +124,10 @@ scimgateway.authPassThroughAllowed = false
123
124
  // =================================================
124
125
  scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
125
126
  //
126
- // "getObj" = { attribute: <>, operator: <>, value: <>, rawFilter: <>, startIndex: <>, count: <> }
127
+ // "getObj" = { attribute: <>, operator: <>, value: <>, rawFilter: <>, startIndex: <>, count: <>, and/or: <getObj> }
127
128
  // rawFilter is always included when filtering
128
129
  // attribute, operator and value are included when requesting unique object or simpel filtering
130
+ // and/or will be included and the value set to corresponding getObj if the mandatory plugin initialization have 'scimgateway.pluginAndOrFilterEnabled = true' and the request query filter includes simple and/or logic
129
131
  // See comments in the "mandatory if-else logic - start"
130
132
  //
131
133
  // "attributes" is array of attributes to be returned - if empty, all supported attributes should be returned
@@ -207,6 +209,11 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
207
209
  attributes: attrs,
208
210
  }
209
211
  }
212
+ if (getObj.and || getObj.or) {
213
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
214
+ // we could have this logic above, if not it must be defined here
215
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
216
+ }
210
217
  // end mandatory if-else logic
211
218
 
212
219
  if (!ldapOptions) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -471,9 +478,10 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
471
478
  // =================================================
472
479
  scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
473
480
  //
474
- // "getObj" = { attribute: <>, operator: <>, value: <>, rawFilter: <>, startIndex: <>, count: <> }
481
+ // "getObj" = { attribute: <>, operator: <>, value: <>, rawFilter: <>, startIndex: <>, count: <>, and/or: <getObj> }
475
482
  // rawFilter is always included when filtering
476
483
  // attribute, operator and value are included when requesting unique object or simpel filtering
484
+ // and/or will be included and the value set to corresponding getObj if the mandatory plugin initialization have 'scimgateway.pluginAndOrFilterEnabled = true' and the request query filter includes simple and/or logic
477
485
  // See comments in the "mandatory if-else logic - start"
478
486
  //
479
487
  // "attributes" is array of attributes to be returned - if empty, all supported attributes should be returned
@@ -562,6 +570,11 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
562
570
  attributes: attrs,
563
571
  }
564
572
  }
573
+ if (getObj.and || getObj.or) {
574
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
575
+ // we could have this logic above, if not it must be defined here
576
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
577
+ }
565
578
  // mandatory if-else logic - end
566
579
 
567
580
  if (!ldapOptions) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -32,6 +32,7 @@ import { ScimGateway } from 'scimgateway'
32
32
  const scimgateway = new ScimGateway()
33
33
  const config = scimgateway.getConfig()
34
34
  scimgateway.authPassThroughAllowed = false
35
+ scimgateway.pluginAndOrFilterEnabled = false
35
36
  // end - mandatory plugin initialization
36
37
 
37
38
  const configDir = scimgateway.configDir
@@ -193,6 +194,11 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
193
194
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all users to be returned - correspond to exploreUsers() in versions < 4.x.x
194
195
  usersArr = users.chain().data()
195
196
  }
197
+ if (getObj.and || getObj.or) {
198
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
199
+ // we could have this logic above, if not it must be defined here
200
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
201
+ }
196
202
  // mandatory if-else logic - end
197
203
 
198
204
  if (!usersArr) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -363,6 +369,11 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
363
369
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all groups to be returned - correspond to exploreUsers() in versions < 4.x.x
364
370
  groupsArr = groups.chain().data()
365
371
  }
372
+ if (getObj.and || getObj.or) {
373
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
374
+ // we could have this logic above, if not it must be defined here
375
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
376
+ }
366
377
  // mandatory if-else logic - end
367
378
 
368
379
  if (!groupsArr) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -29,6 +29,7 @@ import { ScimGateway } from 'scimgateway'
29
29
  const scimgateway = new ScimGateway()
30
30
  const config = scimgateway.getConfig()
31
31
  scimgateway.authPassThroughAllowed = false
32
+ scimgateway.pluginAndOrFilterEnabled = false
32
33
  // end - mandatory plugin initialization
33
34
 
34
35
  const validFilterOperators = ['eq', 'ne', 'aeq', 'dteq', 'gt', 'gte', 'lt', 'lte', 'between', 'jgt', 'jgte', 'jlt', 'jlte', 'jbetween', 'regex', 'in', 'nin', 'keyin', 'nkeyin', 'definedin', 'undefinedin', 'contains', 'containsAny', 'type', 'finite', 'size', 'len', 'exists']
@@ -224,6 +225,11 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
224
225
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all users to be returned - correspond to exploreUsers() in versions < 4.x.x
225
226
  findObj = {}
226
227
  }
228
+ if (getObj.and || getObj.or) {
229
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
230
+ // we could have this logic above, if not it must be defined here
231
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
232
+ }
227
233
  // mandatory if-else logic - end
228
234
 
229
235
  if (!findObj) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -448,6 +454,11 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
448
454
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all groups to be returned - correspond to exploreUsers() in versions < 4.x.x
449
455
  findObj = {}
450
456
  }
457
+ if (getObj.and || getObj.or) {
458
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
459
+ // we could have this logic above, if not it must be defined here
460
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
461
+ }
451
462
  // mandatory if-else logic - end
452
463
 
453
464
  if (!findObj) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -63,6 +63,7 @@ import { ScimGateway } from 'scimgateway'
63
63
  const scimgateway = new ScimGateway()
64
64
  const config = scimgateway.getConfig()
65
65
  scimgateway.authPassThroughAllowed = false
66
+ scimgateway.pluginAndOrFilterEnabled = false
66
67
  // end - mandatory plugin initialization
67
68
 
68
69
  if (config?.connection?.authentication?.options?.password) {
@@ -97,6 +98,11 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
97
98
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all users to be returned - correspond to exploreUsers() in versions < 4.x.x
98
99
  sqlQuery = 'select * from [Users]'
99
100
  }
101
+ if (getObj.and || getObj.or) {
102
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
103
+ // we could have this logic above, if not it must be defined here
104
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
105
+ }
100
106
  // mandatory if-else logic - end
101
107
 
102
108
  if (!sqlQuery) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -269,6 +275,11 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
269
275
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all groups to be returned - correspond to exploreGroups() in versions < 4.x.x
270
276
  sqlQuery = 'select * from [Groups]'
271
277
  }
278
+ if (getObj.and || getObj.or) {
279
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
280
+ // we could have this logic above, if not it must be defined here
281
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
282
+ }
272
283
  // mandatory if-else logic - end
273
284
  if (!sqlQuery) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
274
285
 
@@ -24,6 +24,7 @@ import { ScimGateway } from 'scimgateway'
24
24
  const scimgateway = new ScimGateway()
25
25
  const config = scimgateway.getConfig()
26
26
  scimgateway.authPassThroughAllowed = false
27
+ scimgateway.pluginAndOrFilterEnabled = false
27
28
  // end - mandatory plugin initialization
28
29
 
29
30
  const endpointHost = config.host
@@ -66,6 +67,11 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
66
67
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all users to be returned - correspond to exploreUsers() in versions < 4.x.x
67
68
  sqlQuery = 'select USER_NAME, USER_DEACTIVATED from SYS.USERS where IS_SAML_ENABLED like \'TRUE\''
68
69
  }
70
+ if (getObj.and || getObj.or) {
71
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
72
+ // we could have this logic above, if not it must be defined here
73
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
74
+ }
69
75
  // mandatory if-else logic - end
70
76
 
71
77
  if (!sqlQuery) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -235,6 +241,11 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
235
241
  } else {
236
242
  // mandatory - no filtering (!getObj.operator && !getObj.rawFilter) - all groups to be returned - correspond to exploreGroups() in versions < 4.x.x
237
243
  }
244
+ if (getObj.and || getObj.or) {
245
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
246
+ // we could have this logic above, if not it must be defined here
247
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
248
+ }
238
249
  // mandatory if-else logic - end
239
250
 
240
251
  return { Resources: [] } // groups not supported - returning empty Resources
@@ -35,6 +35,7 @@ import { ScimGateway } from 'scimgateway'
35
35
  const scimgateway = new ScimGateway()
36
36
  const config = scimgateway.getConfig()
37
37
  scimgateway.authPassThroughAllowed = false
38
+ scimgateway.pluginAndOrFilterEnabled = false
38
39
  // end - mandatory plugin initialization
39
40
 
40
41
  const wsdlDir = path.join(`${scimgateway.configDir}`, 'wsdls')
@@ -74,6 +75,11 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
74
75
  soapRequest = { sql: 'SELECT * FROM Users' }
75
76
  soapAction = 'exploreUsers'
76
77
  }
78
+ if (getObj.and || getObj.or) {
79
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
80
+ // we could have this logic above, if not it must be defined here
81
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
82
+ }
77
83
  // mandatory if-else logic - end
78
84
 
79
85
  if (!soapRequest) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -327,6 +333,11 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
327
333
  soapRequest = { sql: 'SELECT * FROM Groups' }
328
334
  soapAction = 'exploreGroups'
329
335
  }
336
+ if (getObj.and || getObj.or) {
337
+ // plugin have enabled 'scimgateway.pluginAndOrFilterEnabled' and the query includes an additonal and/or getObj that must to be handled and combined with the initial getObj
338
+ // we could have this logic above, if not it must be defined here
339
+ throw new Error(`${action} error: logic for handling and/or filter is not implemented by plugin, not supporting: ${getObj.rawFilter}`)
340
+ }
330
341
  // mandatory if-else logic - end
331
342
 
332
343
  if (!soapRequest) throw new Error(`${action} error: mandatory if-else logic not fully implemented`)
@@ -56,6 +56,12 @@ export class ScimGateway {
56
56
  * header in the communication with endpoint
57
57
  */
58
58
  authPassThroughAllowed: boolean
59
+ /**
60
+ * pluginAndOrFilterEnabled can be set to 'true' by the plugin for letting the plugin handle query filtering that includes simple `and`/`or` logic instead of default handled by scimgateway
61
+ *
62
+ */
63
+ pluginAndOrFilterEnabled: boolean
64
+
59
65
  //
60
66
  // plugin methods
61
67
  //
@@ -364,6 +370,7 @@ export class ScimGateway {
364
370
  this.configDir = configDir
365
371
  this.configFile = configFile
366
372
  this.authPassThroughAllowed = false // set to true by plugin if using Auth PassThrough
373
+ this.pluginAndOrFilterEnabled = false // set to true by plugin if plugin handle simple and/or query filter
367
374
 
368
375
  let found: Record<string, any> = {}
369
376
  let configErr: any
@@ -1538,8 +1545,10 @@ export class ScimGateway {
1538
1545
  let isOrFilter = false
1539
1546
  if (getObj.rawFilter) {
1540
1547
  getObj.rawFilter = decodeURIComponent(getObj.rawFilter.trim())
1541
- if (getObj.rawFilter.includes(' and ')) isAndFilter = true
1542
- if (getObj.rawFilter.includes(' or ')) isOrFilter = true
1548
+ // Strip quoted literals (handling escaped quotes) to check for operators outside of values
1549
+ const filterWithoutQuotes = getObj.rawFilter.replace(/"(?:\\"|[^"])*"/g, '""')
1550
+ if (filterWithoutQuotes.includes(' and ')) isAndFilter = true
1551
+ if (filterWithoutQuotes.includes(' or ')) isOrFilter = true
1543
1552
  }
1544
1553
  if (ctx.query.filter) ctx.query.filter = decodeURIComponent(ctx.query.filter.trim())
1545
1554
  else ctx.query.filter = ''
@@ -1553,7 +1562,7 @@ export class ScimGateway {
1553
1562
  const value = arrFilter.slice(2).join(' ').replace(/"/g, '')
1554
1563
  getObj.value = value
1555
1564
  } else if (arrFilter[arrFilter.length - 1].endsWith(']')) { // emails[type eq "work"]
1556
- const rePattern = /^(.*)\[(.*) (.*) (.*)\]$/
1565
+ const rePattern = /^(.*)\[(.*) (.*) (".*")\]$/
1557
1566
  const arrMatches = ctx.query.filter.match(rePattern)
1558
1567
  if (Array.isArray(arrMatches) && arrMatches.length === 5) {
1559
1568
  getObj.attribute = `${arrMatches[1]}.${arrMatches[2]}` // emails.type
@@ -1703,7 +1712,7 @@ export class ScimGateway {
1703
1712
  const splitBy = isAndFilter ? ' and ' : ' or '
1704
1713
  const arr = obj.rawFilter.split(splitBy)
1705
1714
  const originalGetObjArrLength = arr.length
1706
- let getObjArr: object[] = []
1715
+ let getObjArr: Record<string, any>[] = []
1707
1716
  let complexAttr = ''
1708
1717
  for (let i = 0; i < arr.length; i++) {
1709
1718
  arr[i] = arr[i].replace(/\(/g, '').replace(/\)/g, '').trim()
@@ -1752,43 +1761,52 @@ export class ScimGateway {
1752
1761
  }
1753
1762
 
1754
1763
  if (getObjArr.length > 0) {
1755
- const getObj = async (o: Record<string, any>) => {
1756
- return await (this as any)[handle.getMethod](baseEntity, o, attributes, ctx.passThrough)
1757
- }
1758
- const chunk = 5
1759
- const chunkRes: Record<string, any>[] = []
1760
- logger.debug(`${gwName} calling ${handle.getMethod} in chunks of ${chunk}`, { baseEntity: ctx?.routeObj?.baseEntity })
1761
- do {
1762
- const arrChunk = getObjArr.splice(0, chunk)
1763
- const results = await Promise.allSettled(arrChunk.map(o => getObj(o))) as { status: 'fulfilled' | 'rejected', reason: any, value: any }[] // processing max chunk async
1764
- const errors = results.filter(result => result.status === 'rejected').map(result => result.reason.message)
1765
- if (errors.length > 0) {
1766
- const errMsg = `${handle.getMethod} chunks error: ${errors.join(', ')}`
1767
- throw new Error(errMsg)
1768
- }
1769
- const arrArr = results.map(result => result?.value?.Resources)
1770
- for (let i = 0; i < arrArr.length; i++) {
1771
- Array.prototype.push.apply(chunkRes, arrArr[i])
1764
+ if (this.pluginAndOrFilterEnabled && getObjArr.length === 2) { // simple and/or logic handled by plugin
1765
+ const o = getObjArr[0]
1766
+ o.rawFilter = obj.rawFilter
1767
+ if (isAndFilter) o.and = getObjArr[1]
1768
+ else if (isOrFilter) o.or = getObjArr[1]
1769
+ logger.debug(`${gwName} calling ${handle.getMethod}`, { baseEntity: ctx?.routeObj?.baseEntity })
1770
+ res = await (this as any)[handle.getMethod](baseEntity, o, attributes, ctx.passThrough)
1771
+ } else { // and/or logic handled by scimgateway
1772
+ const getObj = async (o: Record<string, any>) => {
1773
+ return await (this as any)[handle.getMethod](baseEntity, o, attributes, ctx.passThrough)
1772
1774
  }
1773
- } while (getObjArr.length > 0)
1775
+ const chunk = 5
1776
+ const chunkRes: Record<string, any>[] = []
1777
+ logger.debug(`${gwName} calling ${handle.getMethod} in chunks of ${chunk}`, { baseEntity: ctx?.routeObj?.baseEntity })
1778
+ do {
1779
+ const arrChunk = getObjArr.splice(0, chunk)
1780
+ const results = await Promise.allSettled(arrChunk.map(o => getObj(o))) as { status: 'fulfilled' | 'rejected', reason: any, value: any }[] // processing max chunk async
1781
+ const errors = results.filter(result => result.status === 'rejected').map(result => result.reason.message)
1782
+ if (errors.length > 0) {
1783
+ const errMsg = `${handle.getMethod} chunks error: ${errors.join(', ')}`
1784
+ throw new Error(errMsg)
1785
+ }
1786
+ const arrArr = results.map(result => result?.value?.Resources)
1787
+ for (let i = 0; i < arrArr.length; i++) {
1788
+ Array.prototype.push.apply(chunkRes, arrArr[i])
1789
+ }
1790
+ } while (getObjArr.length > 0)
1774
1791
 
1775
- if (isAndFilter) {
1776
- const idCounts = new Map<string, number>()
1777
- for (const item of chunkRes) {
1778
- if (item.id) {
1779
- idCounts.set(item.id, (idCounts.get(item.id) || 0) + 1)
1792
+ if (isAndFilter) {
1793
+ const idCounts = new Map<string, number>()
1794
+ for (const item of chunkRes) {
1795
+ if (item.id) {
1796
+ idCounts.set(item.id, (idCounts.get(item.id) || 0) + 1)
1797
+ }
1780
1798
  }
1799
+ const intersectionIds = new Set<string>()
1800
+ for (const [id, count] of idCounts.entries()) {
1801
+ if (count === originalGetObjArrLength) intersectionIds.add(id)
1802
+ }
1803
+ res = { Resources: Array.from(new Map(chunkRes.filter(item => intersectionIds.has(item.id)).map(item => [item.id, item])).values()) }
1804
+ } else if (isOrFilter) {
1805
+ const uniqueResources = Array.from(new Map(chunkRes.map(item =>
1806
+ [item.id, item])).values(),
1807
+ )
1808
+ res = { Resources: uniqueResources }
1781
1809
  }
1782
- const intersectionIds = new Set<string>()
1783
- for (const [id, count] of idCounts.entries()) {
1784
- if (count === originalGetObjArrLength) intersectionIds.add(id)
1785
- }
1786
- res = { Resources: Array.from(new Map(chunkRes.filter(item => intersectionIds.has(item.id)).map(item => [item.id, item])).values()) }
1787
- } else if (isOrFilter) {
1788
- const uniqueResources = Array.from(new Map(chunkRes.map(item =>
1789
- [item.id, item])).values(),
1790
- )
1791
- res = { Resources: uniqueResources }
1792
1810
  }
1793
1811
  }
1794
1812
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scimgateway",
3
- "version": "6.2.0",
3
+ "version": "6.2.2",
4
4
  "type": "module",
5
5
  "description": "Using SCIM protocol as a gateway for user provisioning to other endpoints",
6
6
  "author": "Jarle Elshaug <jarle.elshaug@gmail.com> (https://elshaug.xyz)",