scimgateway 5.0.2 → 5.0.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.
@@ -12,7 +12,7 @@
12
12
 
13
13
  import { createServer as httpCreateServer } from 'node:http'
14
14
  import { createServer as httpsCreateServer } from 'node:https'
15
- import { type IncomingMessage, type ServerResponse } from 'http'
15
+ import { type IncomingMessage, type ServerResponse } from 'node:http'
16
16
  import { createChecker } from 'is-in-subnet'
17
17
  import { BearerStrategy, type IBearerStrategyOptionWithRequest } from 'passport-azure-ad'
18
18
  import { fileURLToPath } from 'url'
@@ -32,8 +32,8 @@ export class ScimGateway {
32
32
  private config: any
33
33
  private logger: any
34
34
  private gwName: string
35
- private scimDef: any // require
36
- private countries: any // require
35
+ private scimDef: any
36
+ private countries: any
37
37
  private multiValueTypes: any
38
38
  private replaceUsrGrp: any
39
39
  private getMemberOf: any
@@ -475,13 +475,13 @@ export class ScimGateway {
475
475
  const schemas = ['User', 'Group']
476
476
  let customMerged = false
477
477
  for (let i = 0; i < schemas.length; i++) {
478
- const schema = this.scimDef.Schemas.Resources.find(el => el.name === schemas[i])
479
- const customSchema = custom.find(el => el.name === schemas[i])
478
+ const schema = this.scimDef.Schemas.Resources.find((el: Record<string, any>) => el.name === schemas[i])
479
+ const customSchema = custom.find((el: Record<string, any>) => el.name === schemas[i])
480
480
  if (schema && customSchema && Array.isArray(customSchema.attributes)) {
481
481
  const arr1 = schema.attributes // core:1.0/2.0 schema
482
482
  const arr2 = customSchema.attributes
483
- schema.attributes = arr2.filter((arr2Obj) => { // only merge attributes (objects) having unique name into core schema
484
- if (!arr1.some(arr1Obj => arr1Obj.name === arr2Obj.name)) {
483
+ schema.attributes = arr2.filter((arr2Obj: Record<string, any>) => { // only merge attributes (objects) having unique name into core schema
484
+ if (!arr1.some((arr1Obj: Record<string, any>) => arr1Obj.name === arr2Obj.name)) {
485
485
  customMerged = true
486
486
  if (!isScimv2) arr2Obj.schema = 'urn:scim:schemas:core:1.0'
487
487
  return arr2Obj
@@ -529,7 +529,7 @@ export class ScimGateway {
529
529
  }
530
530
 
531
531
  // start auth methods - used by auth
532
- const basic = async (baseEntity, method, authType, authToken): Promise<boolean> => {
532
+ const basic = async (baseEntity: string, method: string, authType: string, authToken: string): Promise<boolean> => {
533
533
  return await new Promise((resolve, reject) => { // basic auth
534
534
  if (authType !== 'Basic') resolve(false)
535
535
  if (!found.Basic) resolve(false)
@@ -555,7 +555,7 @@ export class ScimGateway {
555
555
  })
556
556
  }
557
557
 
558
- const bearerToken = async (baseEntity, method, authType, authToken): Promise<boolean> => {
558
+ const bearerToken = async (baseEntity: string, method: string, authType: string, authToken: string): Promise<boolean> => {
559
559
  return await new Promise((resolve, reject) => { // bearer token
560
560
  if (authType !== 'Bearer' || !authToken) resolve(false)
561
561
  if (!found.BearerToken) resolve(false)
@@ -576,14 +576,15 @@ export class ScimGateway {
576
576
  })
577
577
  }
578
578
 
579
- const bearerJwtAzure = async (baseEntity, ctx, authType, authToken): Promise<boolean> => {
579
+ const bearerJwtAzure = async (baseEntity: string, method: string, authType: string, authToken: string): Promise<boolean> => {
580
580
  return await new Promise((resolve, reject) => {
581
581
  if (authType !== 'Bearer' || !found.BearerJwtAzure) resolve(false) // no azure bearer token
582
- const jtoken = jwt.decode(authToken, { complete: true })
582
+ const jtoken: any = jwt.decode(authToken, { complete: true })
583
583
  if (jtoken == null) resolve(false)
584
584
  else if (!jtoken.payload['iss']) resolve(false)
585
585
  if (jtoken?.payload['iss'].indexOf('https://sts.windows.net') !== 0) resolve(false)
586
- passport.authenticate('oauth-bearer', { session: false }, (err, user, info) => {
586
+ const req = { headers: { authorization: `${authType} ${authToken}` } } // Node.js http.createServer type IncomingMessage - header supported by passport
587
+ passport.authenticate('oauth-bearer', { session: false }, (err: any, user: any, info: any) => {
587
588
  if (err) { return reject(err) }
588
589
  if (user) { // authenticated OK
589
590
  const arr = this.config.scimgateway.auth.bearerJwtAzure
@@ -595,16 +596,16 @@ export class ScimGateway {
595
596
  if (!arr[i].baseEntities.includes(baseEntity)) return reject(new Error(`baseEntity=${baseEntity} not allowed for user ${arr[i].tenantIdGUID} according to bearerJwtAzure configuration baseEntitites=${arr[i].baseEntities}`))
596
597
  }
597
598
  }
598
- if (arr[i].readOnly === true && ctx.request.method !== 'GET') return reject(new Error(`only allowing readOnly for user ${arr[i].tenantIdGUID} according to bearerJwtAzure configuration readOnly=true`))
599
+ if (arr[i].readOnly === true && method !== 'GET') return reject(new Error(`only allowing readOnly for user ${arr[i].tenantIdGUID} according to bearerJwtAzure configuration readOnly=true`))
599
600
  }
600
601
  }
601
602
  resolve(true)
602
603
  } else reject(new Error(`Azure JWT authorization failed: ${info}`))
603
- })(ctx)
604
+ })(req)
604
605
  })
605
606
  }
606
607
 
607
- const jwtVerify = async (baseEntity, method, el, authToken) => { // used by bearerJwt
608
+ const jwtVerify = async (baseEntity: string, method: string, el: Record<string, any>, authToken: string) => { // used by bearerJwt
608
609
  return await new Promise((resolve) => {
609
610
  jwt.verify(authToken, (el.secret) ? el.secret : el.publicKeyContent, el.options, (err) => {
610
611
  if (err != null) resolve(false)
@@ -622,9 +623,9 @@ export class ScimGateway {
622
623
  })
623
624
  }
624
625
 
625
- const bearerJwt = async (baseEntity, method, authType, authToken): Promise<boolean> => {
626
+ const bearerJwt = async (baseEntity: string, method: string, authType: string, authToken: string): Promise<boolean> => {
626
627
  if (authType !== 'Bearer' || !found.BearerJwt) return false // no standard jwt bearer token
627
- const jtoken = jwt.decode(authToken, { complete: true })
628
+ const jtoken: any = jwt.decode(authToken, { complete: true })
628
629
  if (jtoken == null) return false
629
630
  if (jtoken?.payload['iss'] && jtoken?.payload['iss'].indexOf('https://sts.windows.net') === 0) return false // azure - handled by bearerJwtAzure
630
631
  const promises: any = []
@@ -639,7 +640,7 @@ export class ScimGateway {
639
640
  throw new Error('JWT authentication failed')
640
641
  }
641
642
 
642
- const bearerOAuth = async (baseEntity, method, authType, authToken): Promise<boolean> => {
643
+ const bearerOAuth = async (baseEntity: string, method: string, authType: string, authToken: string): Promise<boolean> => {
643
644
  return await new Promise((resolve, reject) => { // bearer token
644
645
  if (authType !== 'Bearer' || !authToken) resolve(false)
645
646
  if (!found.BearerOAuth || !authToken) resolve(false)
@@ -710,7 +711,7 @@ export class ScimGateway {
710
711
  const arrResolve = await Promise.all([
711
712
  basic(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
712
713
  bearerToken(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
713
- bearerJwtAzure(ctx.routeObj.baseEntity, ctx, authType, authToken),
714
+ bearerJwtAzure(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
714
715
  bearerJwt(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
715
716
  bearerOAuth(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
716
717
  authPassThrough(ctx.routeObj.baseEntity, ctx.request.method, authType, authToken),
@@ -727,13 +728,13 @@ export class ScimGateway {
727
728
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] request authToken = ${authToken}`)
728
729
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] request jwt.decode(authToken) = ${JSON.stringify(jwt.decode(authToken))}`)
729
730
  }
730
- if (authType === 'Bearer') ctx.response.headers['WWW-Authenticate'] = 'Bearer realm=""'
731
- else if (found.Basic) ctx.response.headers['WWW-Authenticate'] = 'Basic realm=""'
731
+ if (authType === 'Bearer') ctx.response.headers.set('WWW-Authenticate', 'Bearer realm=""')
732
+ else if (found.Basic) ctx.response.headers.set('WWW-Authenticate', 'Basic realm=""')
732
733
  if (ctx.request.url !== '/favicon.ico') logger.error(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] ${err.message}`)
733
734
  return false
734
735
  } catch (err: any) {
735
- if (authType === 'Bearer') ctx.response.headers['WWW-Authenticate'] = 'Bearer realm=""'
736
- else ctx.response.headers['WWW-Authenticate'] = 'Basic realm=""'
736
+ if (authType === 'Bearer') ctx.response.headers.set('WWW-Authenticate', 'Bearer realm=""')
737
+ else ctx.response.headers.set('WWW-Authenticate', 'Basic realm=""')
737
738
  if (pwErrCount < 3) {
738
739
  pwErrCount += 1
739
740
  logger.error(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] ${ctx.request.url} ${err.message}`)
@@ -757,8 +758,8 @@ export class ScimGateway {
757
758
 
758
759
  const getHandlerSchemas = async (ctx: Context) => {
759
760
  let tx = this.scimDef.Schemas
760
- tx = utilsScim.addResources(tx, null, null, null)
761
- tx = utilsScim.addSchemas(tx, null, isScimv2, null)
761
+ tx = utilsScim.addResources(tx, undefined, undefined, undefined)
762
+ tx = utilsScim.addSchemas(tx, isScimv2, undefined, undefined)
762
763
  ctx.response.body = JSON.stringify(tx)
763
764
  }
764
765
 
@@ -881,7 +882,7 @@ export class ScimGateway {
881
882
  refresh_token: token, // ignored by scimgateway, but maybe used by client
882
883
  }
883
884
 
884
- ctx.response.headers['Cache-Control'] = 'no-store'
885
+ ctx.response.headers.set('Cache-Control', 'no-store')
885
886
  ctx.response.body = JSON.stringify(tx)
886
887
  ctx.response.status = 200
887
888
  }
@@ -903,8 +904,8 @@ export class ScimGateway {
903
904
  ctx.response.body = JSON.stringify(e)
904
905
  return
905
906
  }
906
- if (ctx.query.attributes) ctx.query.attributes = ctx.query.attributes.split(',').map(item => item.trim()).join()
907
- if (ctx.query.excludedAttributes) ctx.query.excludedAttributes = ctx.query.excludedAttributes.split(',').map(item => item.trim()).join()
907
+ if (ctx.query.attributes) ctx.query.attributes = ctx.query.attributes.split(',').map((item: string) => item.trim()).join()
908
+ if (ctx.query.excludedAttributes) ctx.query.excludedAttributes = ctx.query.excludedAttributes.split(',').map((item: string) => item.trim()).join()
908
909
 
909
910
  const getObj = {
910
911
  attribute: 'id',
@@ -917,7 +918,7 @@ export class ScimGateway {
917
918
  let res
918
919
  try {
919
920
  const ob = utils.copyObj(getObj)
920
- const attributes = ctx.query.attributes ? ctx.query.attributes.split(',').map(item => item.trim()) : []
921
+ const attributes = ctx.query.attributes ? ctx.query.attributes.split(',').map((item: string) => item.trim()) : []
921
922
  if (this.config.scimgateway.stream.publisher.enabled) {
922
923
  const streamObj = {
923
924
  func: handle.getMethod,
@@ -930,7 +931,7 @@ export class ScimGateway {
930
931
  res = await this.pub.publish(streamObj)
931
932
  } else {
932
933
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] calling "${handle.getMethod}" and awaiting result`)
933
- res = await this[handle.getMethod](baseEntity, ob, attributes, ctx.passThrough)
934
+ res = await (this as any)[handle.getMethod](baseEntity, ob, attributes, ctx.passThrough)
934
935
  }
935
936
 
936
937
  let scimdata: { [key: string]: any } = {
@@ -968,7 +969,7 @@ export class ScimGateway {
968
969
 
969
970
  userObj = utilsScim.addPrimaryAttrs(userObj)
970
971
  scimdata = utils.stripObj(userObj, ctx.query.attributes, ctx.query.excludedAttributes)
971
- scimdata = utilsScim.addSchemas(scimdata, handle.description, isScimv2, null)
972
+ scimdata = utilsScim.addSchemas(scimdata, isScimv2, handle.description, undefined)
972
973
 
973
974
  if (!this.config.scimgateway.scim.skipMetaLocation) {
974
975
  const location = ctx.origin + ctx.path
@@ -994,8 +995,8 @@ export class ScimGateway {
994
995
  const getHandler = async (ctx: Context) => {
995
996
  const handle = handler[ctx.routeObj.handle]
996
997
  const baseEntity = ctx.routeObj.baseEntity
997
- if (ctx.query.attributes) ctx.query.attributes = ctx.query.attributes.split(',').map(item => item.trim()).join()
998
- if (ctx.query.excludedAttributes) ctx.query.excludedAttributes = ctx.query.excludedAttributes.split(',').map(item => item.trim()).join()
998
+ if (ctx.query.attributes) ctx.query.attributes = ctx.query.attributes.split(',').map((item: string) => item.trim()).join()
999
+ if (ctx.query.excludedAttributes) ctx.query.excludedAttributes = ctx.query.excludedAttributes.split(',').map((item: string) => item.trim()).join()
999
1000
 
1000
1001
  const getObj: any = {
1001
1002
  attribute: undefined,
@@ -1125,7 +1126,7 @@ export class ScimGateway {
1125
1126
 
1126
1127
  let res: any
1127
1128
  const obj: any = utils.copyObj(getObj)
1128
- const attributes = ctx.query.attributes ? ctx.query.attributes.split(',').map(item => item.trim()) : []
1129
+ const attributes = ctx.query.attributes ? ctx.query.attributes.split(',').map((item: string) => item.trim()) : []
1129
1130
  if (this.config.scimgateway.stream.publisher.enabled) {
1130
1131
  const streamObj = {
1131
1132
  func: handle.getMethod,
@@ -1158,11 +1159,11 @@ export class ScimGateway {
1158
1159
  }
1159
1160
  }
1160
1161
  if (getObjArr.length > 0) {
1161
- const getObj = async (o) => {
1162
- return await this[handle.getMethod](baseEntity, o, attributes, ctx.passThrough)
1162
+ const getObj = async (o: Record<string, any>) => {
1163
+ return await (this as any)[handle.getMethod](baseEntity, o, attributes, ctx.passThrough)
1163
1164
  }
1164
1165
  const chunk = 5
1165
- const chunkRes = []
1166
+ const chunkRes: Record<string, any>[] = []
1166
1167
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] calling "${handle.getMethod}" with chunks and awaiting result`)
1167
1168
  do {
1168
1169
  const arrChunk = getObjArr.splice(0, chunk)
@@ -1183,7 +1184,7 @@ export class ScimGateway {
1183
1184
 
1184
1185
  if (!res) { // standard
1185
1186
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] calling "${handle.getMethod}" and awaiting result`)
1186
- res = await this[handle.getMethod](baseEntity, obj, attributes, ctx.passThrough)
1187
+ res = await (this as any)[handle.getMethod](baseEntity, obj, attributes, ctx.passThrough)
1187
1188
  }
1188
1189
  // check for user attribute groups and include if needed
1189
1190
  if (Array.isArray(res?.Resources)) {
@@ -1221,7 +1222,7 @@ export class ScimGateway {
1221
1222
  scimdata.Resources[i] = utils.stripObj(scimdata.Resources[i], ctx.query.attributes, ctx.query.excludedAttributes)
1222
1223
  }
1223
1224
  scimdata = utilsScim.addResources(scimdata, ctx.query.startIndex, ctx.query.sortBy, ctx.query.sortOrder)
1224
- scimdata = utilsScim.addSchemas(scimdata, handle.description, isScimv2, location)
1225
+ scimdata = utilsScim.addSchemas(scimdata, isScimv2, handle.description, location)
1225
1226
 
1226
1227
  ctx.response.body = JSON.stringify(scimdata)
1227
1228
  } catch (err: any) {
@@ -1312,7 +1313,7 @@ export class ScimGateway {
1312
1313
  }
1313
1314
  }
1314
1315
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] calling "${handle.createMethod}" and awaiting result`)
1315
- res = await this[handle.createMethod](baseEntity, scimdata, ctx.passThrough)
1316
+ res = await (this as any)[handle.createMethod](baseEntity, scimdata, ctx.passThrough)
1316
1317
  }
1317
1318
  for (const key in res) { // merge any result e.g: {'id': 'xxxx'}
1318
1319
  jsonBody[key] = res[key]
@@ -1323,7 +1324,7 @@ export class ScimGateway {
1323
1324
  try {
1324
1325
  if (handle.createMethod === 'createUser') {
1325
1326
  let ob = {}
1326
- const attributes = []
1327
+ const attributes: string[] = []
1327
1328
  if (jsonBody.userName) ob = { attribute: 'userName', operator: 'eq', value: jsonBody.userName }
1328
1329
  else if (jsonBody.externalId) ob = { attribute: 'externalId', operator: 'eq', value: jsonBody.externalId }
1329
1330
  if (this.config.scimgateway.stream.publisher.enabled) {
@@ -1336,11 +1337,11 @@ export class ScimGateway {
1336
1337
  }
1337
1338
  res = await this.pub.publish(streamObj)
1338
1339
  } else {
1339
- res = await this[handle.getMethod](baseEntity, ob, attributes, ctx.passThrough)
1340
+ res = await (this as any)[handle.getMethod](baseEntity, ob, attributes, ctx.passThrough)
1340
1341
  }
1341
1342
  } else if (handle.createMethod === 'createGroup') {
1342
1343
  let ob = {}
1343
- const attributes = []
1344
+ const attributes: string[] = []
1344
1345
  if (jsonBody.externalId) ob = { attribute: 'externalId', operator: 'eq', value: jsonBody.externalId }
1345
1346
  else if (jsonBody.displayName) ob = { attribute: 'displayName', operator: 'eq', value: jsonBody.displayName }
1346
1347
  if (this.config.scimgateway.stream.publisher.enabled) {
@@ -1353,7 +1354,7 @@ export class ScimGateway {
1353
1354
  }
1354
1355
  res = await this.pub.publish(streamObj)
1355
1356
  } else {
1356
- res = await this[handle.getMethod](baseEntity, ob, attributes, ctx.passThrough)
1357
+ res = await (this as any)[handle.getMethod](baseEntity, ob, attributes, ctx.passThrough)
1357
1358
  }
1358
1359
  }
1359
1360
  } catch (err) { void 0 }
@@ -1365,7 +1366,7 @@ export class ScimGateway {
1365
1366
  }
1366
1367
 
1367
1368
  if (addGrps.length > 0 && handle.createMethod === 'createUser') { // add group membership
1368
- const addGroups = async (groupId) => {
1369
+ const addGroups = async (groupId: string) => {
1369
1370
  if (this.config.scimgateway.stream.publisher.enabled) {
1370
1371
  const streamObj = {
1371
1372
  func: handler.groups.modifyMethod,
@@ -1376,10 +1377,10 @@ export class ScimGateway {
1376
1377
  }
1377
1378
  return await this.pub.publish(streamObj)
1378
1379
  } else {
1379
- return await this[handler.groups.modifyMethod](baseEntity, groupId, { members: [{ value: jsonBody.id }] }, ctx.passThrough)
1380
+ return await (this as any)[handler.groups.modifyMethod](baseEntity, groupId, { members: [{ value: jsonBody.id }] }, ctx.passThrough)
1380
1381
  }
1381
1382
  }
1382
- const res = await Promise.allSettled(addGrps.map(groupId => addGroups(groupId)))
1383
+ const res = await Promise.allSettled(addGrps.map((groupId: string) => addGroups(groupId)))
1383
1384
  const errAdd = res.filter(result => result.status === 'rejected').map(result => result.reason.message)
1384
1385
  if (errAdd.length > 0) {
1385
1386
  const errMsg = `user created, but there are group membership errors: ${errAdd.join(', ')}`
@@ -1395,11 +1396,11 @@ export class ScimGateway {
1395
1396
  const location = ctx.origin + `${ctx.path}/${jsonBody.id}`
1396
1397
  if (!jsonBody.meta) jsonBody.meta = {}
1397
1398
  jsonBody.meta.location = location
1398
- ctx.response.headers['Location'] = location
1399
+ ctx.response.headers.set('Location', location)
1399
1400
  }
1400
1401
  delete jsonBody.password
1401
1402
  jsonBody = utilsScim.addPrimaryAttrs(jsonBody)
1402
- jsonBody = utilsScim.addSchemas(jsonBody, handle.description, isScimv2, null)
1403
+ jsonBody = utilsScim.addSchemas(jsonBody, isScimv2, handle.description, undefined)
1403
1404
  ctx.response.status = 201
1404
1405
  ctx.response.body = JSON.stringify(jsonBody)
1405
1406
  } catch (err: any) {
@@ -1449,7 +1450,7 @@ export class ScimGateway {
1449
1450
  const groups = await getMemberOf(baseEntity, id, handler.groups.getMethod, ctx.passThrough)
1450
1451
  if (Array.isArray(groups) && groups.length > 0) {
1451
1452
  const revokeGroupMember = async (grpId: string) => {
1452
- return await this[handler.groups.modifyMethod](baseEntity, grpId, { members: [{ operation: 'delete', value: id }] }, ctx.passThrough)
1453
+ return await (this as any)[handler.groups.modifyMethod](baseEntity, grpId, { members: [{ operation: 'delete', value: id }] }, ctx.passThrough)
1453
1454
  }
1454
1455
  await Promise.allSettled(groups.map((grp: any) => {
1455
1456
  if (grp.value) return revokeGroupMember(grp.value)
@@ -1458,7 +1459,7 @@ export class ScimGateway {
1458
1459
  }
1459
1460
  }
1460
1461
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] calling "${handle.deleteMethod}" and awaiting result`)
1461
- await this[handle.deleteMethod](baseEntity, id, ctx.passThrough)
1462
+ await (this as any)[handle.deleteMethod](baseEntity, id, ctx.passThrough)
1462
1463
  }
1463
1464
  ctx.response.status = 204
1464
1465
  } catch (err: any) {
@@ -1480,8 +1481,8 @@ export class ScimGateway {
1480
1481
  // example: {"members":[{"value":"bjensen"}],"schemas":["urn:scim:schemas:core:1.0"]}
1481
1482
  //
1482
1483
  const patchHandler = async (ctx: Context) => {
1483
- if (ctx.query.attributes) ctx.query.attributes = ctx.query.attributes.split(',').map(item => item.trim()).join()
1484
- if (ctx.query.excludedAttributes) ctx.query.excludedAttributes = ctx.query.excludedAttributes.split(',').map(item => item.trim()).join()
1484
+ if (ctx.query.attributes) ctx.query.attributes = ctx.query.attributes.split(',').map((item: string) => item.trim()).join()
1485
+ if (ctx.query.excludedAttributes) ctx.query.excludedAttributes = ctx.query.excludedAttributes.split(',').map((item: any) => item.trim()).join()
1485
1486
  const handle = handler[ctx.routeObj.handle]
1486
1487
  const baseEntity = ctx.routeObj.baseEntity
1487
1488
  const id = ctx.routeObj.id ? decodeURIComponent(ctx.routeObj.id) : ctx.routeObj.id
@@ -1545,12 +1546,12 @@ export class ScimGateway {
1545
1546
  res = await replaceUsrGrp(ctx.routeObj.handle, baseEntity, id, scimdata, this.config.scimgateway.scim.usePutSoftSync, ctx.passThrough)
1546
1547
  } else {
1547
1548
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] calling "${handle.modifyMethod}" and awaiting result`)
1548
- res = await this[handle.modifyMethod](baseEntity, id, scimdata, ctx.passThrough)
1549
+ res = await (this as any)[handle.modifyMethod](baseEntity, id, scimdata, ctx.passThrough)
1549
1550
  }
1550
1551
  }
1551
1552
 
1552
1553
  if (groups.length > 0 && handle.modifyMethod === 'modifyUser') { // modify user includes groups, add/remove group membership
1553
- const updateGroup = async (groupsObj) => {
1554
+ const updateGroup = async (groupsObj: Record<string, any>) => {
1554
1555
  const groupId = groupsObj.value
1555
1556
  const memberObj: any = { value: id }
1556
1557
  if (groupsObj.operation) memberObj.operation = groupsObj.operation
@@ -1564,10 +1565,10 @@ export class ScimGateway {
1564
1565
  }
1565
1566
  return await this.pub.publish(streamObj)
1566
1567
  } else {
1567
- return await this[handler.groups.modifyMethod](baseEntity, groupId, { members: [memberObj] }, ctx.passThrough)
1568
+ return await (this as any)[handler.groups.modifyMethod](baseEntity, groupId, { members: [memberObj] }, ctx.passThrough)
1568
1569
  }
1569
1570
  }
1570
- const res = await Promise.allSettled(groups.map(groupsObj => updateGroup(groupsObj)))
1571
+ const res = await Promise.allSettled(groups.map((groupsObj: Record<string, any>) => updateGroup(groupsObj)))
1571
1572
  const errRes = res.filter(result => result.status === 'rejected').map(result => result.reason.message)
1572
1573
  if (errRes.length > 0) {
1573
1574
  const errMsg = `modify user group membership error: ${errRes.join(', ')}`
@@ -1581,7 +1582,7 @@ export class ScimGateway {
1581
1582
  return
1582
1583
  }
1583
1584
  const ob = { attribute: 'id', operator: 'eq', value: id }
1584
- const attributes = ctx.query.attributes ? ctx.query.attributes.split(',').map(item => item.trim()) : []
1585
+ const attributes = ctx.query.attributes ? ctx.query.attributes.split(',').map((item: string) => item.trim()) : []
1585
1586
  if (this.config.scimgateway.stream.publisher.enabled) {
1586
1587
  const streamObj = {
1587
1588
  func: handle.getMethod,
@@ -1594,7 +1595,7 @@ export class ScimGateway {
1594
1595
  res = await this.pub.publish(streamObj)
1595
1596
  } else {
1596
1597
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] calling "${handle.getMethod}" and awaiting result`)
1597
- res = await this[handle.getMethod](baseEntity, ob, attributes, ctx.passThrough)
1598
+ res = await (this as any)[handle.getMethod](baseEntity, ob, attributes, ctx.passThrough)
1598
1599
  }
1599
1600
  }
1600
1601
 
@@ -1614,11 +1615,11 @@ export class ScimGateway {
1614
1615
  }
1615
1616
  if (!this.config.scimgateway.scim.skipMetaLocation) {
1616
1617
  const location = ctx.origin + ctx.path
1617
- ctx.response.headers['Location'] = location
1618
+ ctx.response.headers.set('Location', location)
1618
1619
  }
1619
1620
  const userObj = utilsScim.addPrimaryAttrs(scimdata.Resources[0])
1620
1621
  scimdata = utils.stripObj(userObj, ctx.query.attributes, ctx.query.excludedAttributes)
1621
- scimdata = utilsScim.addSchemas(scimdata, handle.description, isScimv2, null)
1622
+ scimdata = utilsScim.addSchemas(scimdata, isScimv2, handle.description, undefined)
1622
1623
  ctx.response.status = 200
1623
1624
  ctx.response.body = JSON.stringify(scimdata)
1624
1625
  } catch (err: any) {
@@ -1640,7 +1641,7 @@ export class ScimGateway {
1640
1641
 
1641
1642
  // get current object
1642
1643
  logger.debug(`${gwName}[${pluginName}][${baseEntity}] calling "${handle.getMethod}" and awaiting result`)
1643
- const res = await this[handle.getMethod](baseEntity, { attribute: 'id', operator: 'eq', value: id }, [], ctxPassThrough)
1644
+ const res = await (this as any)[handle.getMethod](baseEntity, { attribute: 'id', operator: 'eq', value: id }, [], ctxPassThrough)
1644
1645
  logger.debug(`${gwName}[${pluginName}][${baseEntity}] "${handle.getMethod}" result: ${res ? JSON.stringify(res) : ''}`)
1645
1646
  let currentObj
1646
1647
  if (res && res.Resources && Array.isArray(res.Resources)) {
@@ -1683,13 +1684,13 @@ export class ScimGateway {
1683
1684
  // update object
1684
1685
  if (Object.keys(scimdata).length > 0) {
1685
1686
  logger.debug(`${gwName}[${pluginName}][${baseEntity}] calling "${handle.modifyMethod}" and awaiting result`)
1686
- await this[handle.modifyMethod](baseEntity, id, scimdata, ctxPassThrough)
1687
+ await (this as any)[handle.modifyMethod](baseEntity, id, scimdata, ctxPassThrough)
1687
1688
  }
1688
1689
 
1689
1690
  // add/remove groups
1690
1691
  if (!this.config.scimgateway.scim.groupMemberOfUser) {
1691
1692
  if (objGroups && Array.isArray(objGroups) && !(usePutSoftSync && objGroups.length < 1)) { // only if groups included, { "groups": [] } will remove all existing
1692
- if (typeof this[handler.groups.getMethod] !== 'function' || typeof this[handler.groups.modifyMethod] !== 'function') {
1693
+ if (typeof (this as any)[handler.groups.getMethod] !== 'function' || typeof (this as any)[handler.groups.modifyMethod] !== 'function') {
1693
1694
  throw new Error('replaceUser error: put operation can not be fully completed for the user`s groups, methods like getGroups() and modifyGroup() are not implemented')
1694
1695
  }
1695
1696
  let currentGroups
@@ -1697,7 +1698,7 @@ export class ScimGateway {
1697
1698
  else { // try to get current groups the standard way
1698
1699
  let res
1699
1700
  try {
1700
- res = await this[handler.groups.getMethod](baseEntity, { attribute: 'members.value', operator: 'eq', value: decodeURIComponent(id) }, ['id', 'displayName'], ctxPassThrough)
1701
+ res = await (this as any)[handler.groups.getMethod](baseEntity, { attribute: 'members.value', operator: 'eq', value: decodeURIComponent(id) }, ['id', 'displayName'], ctxPassThrough)
1701
1702
  logger.debug(`${gwName}[${pluginName}][${baseEntity}] "${handler.groups.getMethod}" result: ${res ? JSON.stringify(res) : ''}`)
1702
1703
  } catch (err) { void 0 } // method may be implemented but throwing error like groups not supported/implemented
1703
1704
  currentGroups = []
@@ -1711,7 +1712,7 @@ export class ScimGateway {
1711
1712
  }
1712
1713
  }
1713
1714
  }
1714
- currentGroups = currentGroups.map((el) => {
1715
+ currentGroups = currentGroups.map((el: Record<string, any>) => {
1715
1716
  if (el.value) {
1716
1717
  el.value = decodeURIComponent(el.value)
1717
1718
  }
@@ -1747,22 +1748,22 @@ export class ScimGateway {
1747
1748
  if (!found && currentGroups[i].value) removeGrps.push(currentGroups[i].value)
1748
1749
  }
1749
1750
 
1750
- const assignGroupMember = async (grpId) => {
1751
- return await this[handler.groups.modifyMethod](baseEntity, grpId, { members: [{ value: id }] }, ctxPassThrough)
1751
+ const assignGroupMember = async (grpId: string) => {
1752
+ return await (this as any)[handler.groups.modifyMethod](baseEntity, grpId, { members: [{ value: id }] }, ctxPassThrough)
1752
1753
  }
1753
1754
 
1754
- const revokeGroupMember = async (grpId) => {
1755
- return await this[handler.groups.modifyMethod](baseEntity, grpId, { members: [{ operation: 'delete', value: id }] }, ctxPassThrough)
1755
+ const revokeGroupMember = async (grpId: string) => {
1756
+ return await (this as any)[handler.groups.modifyMethod](baseEntity, grpId, { members: [{ operation: 'delete', value: id }] }, ctxPassThrough)
1756
1757
  }
1757
1758
 
1758
1759
  let errRevoke: string[] = []
1759
1760
  if (!usePutSoftSync) { // default will remove any existing groups not included, usePutSoftSync=true prevents removing existing groups (only add groups)
1760
1761
  const res: { [key: string]: any } = await Promise.allSettled(removeGrps.map(async grpId => revokeGroupMember(grpId)))
1761
- errRevoke = res.filter(result => result.status === 'rejected').map(result => result.reason.message)
1762
+ errRevoke = res.filter((result: Record<string, any>) => result.status === 'rejected').map((result: Record<string, any>) => result.reason.message)
1762
1763
  }
1763
1764
 
1764
1765
  const res: { [key: string]: any } = await Promise.allSettled(addGrps.map(async grpId => assignGroupMember(grpId)))
1765
- const errAssign: string[] = res.filter(result => result.status === 'rejected').map(result => result.reason.message)
1766
+ const errAssign: string[] = res.filter((result: Record<string, any>) => result.status === 'rejected').map((result: Record<string, any>) => result.reason.message)
1766
1767
 
1767
1768
  let errMsg = ''
1768
1769
  if (errRevoke.length > 0) errMsg = `revokeGroupMember errors: ${errRevoke.join(', ')}`
@@ -1830,7 +1831,7 @@ export class ScimGateway {
1830
1831
  return
1831
1832
  }
1832
1833
  try {
1833
- let result
1834
+ let result: Record<string, any>
1834
1835
  if (this.config.scimgateway.stream.publisher.enabled) {
1835
1836
  const streamObj = {
1836
1837
  func: 'postApi',
@@ -1893,7 +1894,7 @@ export class ScimGateway {
1893
1894
  }
1894
1895
 
1895
1896
  try {
1896
- let result
1897
+ let result: Record<string, any>
1897
1898
  if (this.config.scimgateway.stream.publisher.enabled) {
1898
1899
  const streamObj = {
1899
1900
  func: 'putApi',
@@ -1955,7 +1956,7 @@ export class ScimGateway {
1955
1956
  return
1956
1957
  } else {
1957
1958
  try {
1958
- let result
1959
+ let result: Record<string, any>
1959
1960
  if (this.config.scimgateway.stream.publisher.enabled) {
1960
1961
  const streamObj = {
1961
1962
  func: 'patchApi',
@@ -2063,7 +2064,7 @@ export class ScimGateway {
2063
2064
  logger.debug(`${gwName}[${pluginName}][${ctx?.routeObj?.baseEntity}] [DELETE ${ctx.routeObj.handle} ] id=${id}`)
2064
2065
  try {
2065
2066
  if (!id || id.includes('/')) throw new Error('missing id')
2066
- let result
2067
+ let result: Record<string, any>
2067
2068
  if (this.config.scimgateway.stream.publisher.enabled) {
2068
2069
  const streamObj = {
2069
2070
  func: 'deleteApi',
@@ -2102,7 +2103,7 @@ export class ScimGateway {
2102
2103
  //
2103
2104
  // GET = /AppRoles
2104
2105
  //
2105
- this.getAppRoles = async (baseEntity) => {
2106
+ this.getAppRoles = async (baseEntity: string) => {
2106
2107
  return await stream.getAppRoles(this, baseEntity)
2107
2108
  }
2108
2109
 
@@ -2110,14 +2111,14 @@ export class ScimGateway {
2110
2111
  const getMemberOf = async (baseEntity: string, id: string, getMethod: string, ctxPassThrough: any) => {
2111
2112
  const groups: object[] = []
2112
2113
  if (getMethod !== 'getGroups') return groups
2113
- if (typeof this[handler.groups.getMethod] !== 'function') return groups // method not implemented
2114
+ if (typeof (this as any)[handler.groups.getMethod] !== 'function') return groups // method not implemented
2114
2115
  if (this.config.scimgateway.scim.groupMemberOfUser) return groups // only support user member of group
2115
2116
  let res: any
2116
2117
  try {
2117
2118
  const ob = { attribute: 'members.value', operator: 'eq', value: decodeURIComponent(id) }
2118
2119
  const attributes = ['id', 'displayName']
2119
2120
  logger.debug(`${gwName}[${pluginName}][${baseEntity}] calling "${handler.groups.getMethod}" and awaiting result - groups to be included`)
2120
- res = await this[handler.groups.getMethod](baseEntity, ob, attributes, ctxPassThrough)
2121
+ res = await (this as any)[handler.groups.getMethod](baseEntity, ob, attributes, ctxPassThrough)
2121
2122
  } catch (err) { } // ignore errors
2122
2123
  if (res && res.Resources && Array.isArray(res.Resources) && res.Resources.length > 0) {
2123
2124
  for (let i = 0; i < res.Resources.length; i++) {
@@ -2153,7 +2154,7 @@ export class ScimGateway {
2153
2154
  body: any
2154
2155
  }
2155
2156
  response: {
2156
- headers: HeadersInit
2157
+ headers: Headers // HeadersInit
2157
2158
  status?: number
2158
2159
  body?: string
2159
2160
  }
@@ -2262,7 +2263,7 @@ export class ScimGateway {
2262
2263
  },
2263
2264
  response: {
2264
2265
  status: undefined,
2265
- headers: {},
2266
+ headers: new Headers(),
2266
2267
  body: undefined,
2267
2268
  },
2268
2269
  routeObj: {
@@ -2277,8 +2278,8 @@ export class ScimGateway {
2277
2278
  ip: getIpFromHeader(request.headers) || directIp,
2278
2279
  origin: getOriginFromHeader(request.headers) || url.origin,
2279
2280
  passThrough: (found.PassThrough && this.authPassThroughAllowed) ? { headers: request.headers } : undefined,
2280
-
2281
2281
  }
2282
+
2282
2283
  url.searchParams.forEach((value, key) => {
2283
2284
  ctx.query[key] = value
2284
2285
  })
@@ -2454,12 +2455,12 @@ export class ScimGateway {
2454
2455
  // node --experimental-strip-types index.ts
2455
2456
 
2456
2457
  // return body from req
2457
- async function getRequestBody(req): Promise<Buffer> {
2458
+ async function getRequestBody(req: any): Promise<Buffer> {
2458
2459
  return new Promise((resolve, reject) => {
2459
2460
  const body: Uint8Array[] = []
2460
2461
  req.on('data', (chunk: Uint8Array) => body.push(chunk)) // Explicitly typing chunk
2461
2462
  req.on('end', () => resolve(Buffer.concat(body)))
2462
- req.on('error', err => reject(err))
2463
+ req.on('error', (err: Error) => reject(err))
2463
2464
  })
2464
2465
  }
2465
2466
 
@@ -2774,7 +2775,7 @@ export class ScimGateway {
2774
2775
  **/
2775
2776
  getArrayObject(obj: any, element: string, type: string): any {
2776
2777
  if (obj[element]) { // element is case sensitive
2777
- return obj[element].find(function (el) {
2778
+ return obj[element].find(function (el: Record<string, any>) {
2778
2779
  return (el.type && (el.type).toLowerCase() === type.toLowerCase())
2779
2780
  })
2780
2781
  }