scimgateway 5.0.1 → 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.
package/README.md CHANGED
@@ -123,7 +123,7 @@ If internet connection is blocked, we could install on another machine and copy
123
123
 
124
124
  bun c:\my-scimgateway
125
125
 
126
- If using Node.js instead of Bun: node --experimental-strip-types c:\my-scimgateway\index.ts
126
+ If using Node.js instead of Bun, scimgateway must be downloaded from github and startup: node --experimental-strip-types c:\my-scimgateway\index.ts
127
127
 
128
128
  Start a browser (note, Edge do not pop-up logon dialog box when using http)
129
129
 
@@ -569,8 +569,9 @@ docker-compose**
569
569
 
570
570
  mkdir /opt/my-scimgateway
571
571
  cd /opt/my-scimgateway
572
- npm init -y
573
- npm install scimgateway
572
+ bun init -y
573
+ bun install scimgateway
574
+ bun pm trust scimgateway
574
575
  cp ./config/docker/* .
575
576
 
576
577
  **docker-compose.yml** <== Here is where you would set the exposed port and environment
@@ -819,15 +820,29 @@ If using proxy, set proxy.host to `"http://<FQDN-ProxyHost>:<port>"` e.g `"http:
819
820
  "endpoint": {
820
821
  "entity": {
821
822
  "undefined": {
822
- "tenantIdGUID": "DomainName or DirectoryID (GUID)",
823
- "clientId": "Application ID",
824
- "clientSecret": "Generated application key value",
825
- "proxy": {
826
- "host": null,
827
- "username": null,
828
- "password": null
829
- }
823
+ "connection": {
824
+ "baseUrls": [
825
+ "not in use for Entra ID when tenantIdGUID is defined"
826
+ ],
827
+ "auth": {
828
+ "type": "oauth",
829
+ "options": {
830
+ "tokenUrl": "oauth token_url - not in use when tenantIdGUID is defined",
831
+ "tenantIdGUID": "Entra ID Tenant ID (GUID) or Primary domain name - only used by plugin-entra-id",
832
+ "clientId": "oauth client_id - Entra ID: Application ID",
833
+ "clientSecret": "oauth client_secret - Entra ID: generated application secret value"
834
+ }
835
+ },
836
+ "proxy": {
837
+ "host": null,
838
+ "username": null,
839
+ "password": null
840
+ }
841
+ }
830
842
  }
843
+ },
844
+ "map": {
845
+ ...
831
846
  }
832
847
  }
833
848
 
@@ -946,7 +961,7 @@ Preparation:
946
961
 
947
962
  * Copy "best matching" example plugin e.g. `lib\plugin-mssql.ts` and `config\plugin-mssql.json` and rename both copies to your plugin name prefix e.g. plugin-mine.ts and plugin-mine.json (for SOAP Webservice endpoint we might use plugin-soap as a template)
948
963
  * Edit plugin-mine.json and define a unique port number for the gateway setting
949
- * Edit index.ts and add a new line for starting your plugin e.g. `let mine = require('./lib/plugin-mine');`
964
+ * Edit index.ts and include your plugin in the startup e.g. `const plugins = ['mine']');`
950
965
  * Start SCIM Gateway and verify. If using CA Provisioning you could setup a SCIM endpoint using the port number you defined
951
966
 
952
967
  Now we are ready for custom coding by editing plugin-mine.ts
@@ -1071,11 +1086,28 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
1071
1086
 
1072
1087
  ## Change log
1073
1088
 
1089
+ ### v5.0.3
1090
+
1091
+ [Fixed]
1092
+
1093
+ - unauthorized connection when using configuration bearerJwtAzure
1094
+
1095
+ [Improved]
1096
+
1097
+ - minor type cosmetics
1098
+
1099
+
1100
+ ### v5.0.2
1101
+
1102
+ [Improved]
1103
+
1104
+ - minor cosmetics readme updates
1105
+
1074
1106
  ### v5.0.1
1075
1107
 
1076
1108
  [Fixed]
1077
1109
 
1078
- Postinstall did not update index.ts when default bun index.ts did exist
1110
+ - postinstall did not update index.ts when default bun index.ts did exist
1079
1111
 
1080
1112
 
1081
1113
  ### v5.0.0
@@ -1088,7 +1120,7 @@ Besides going from JavaScript to TypeScript, following can be mentioned:
1088
1120
 
1089
1121
  * Code editor now having IntelliSense showing available methods and documentation details for scimgateway methods
1090
1122
  * index.ts having new logic for starting plugins e.g.: `const plugins = ['ldap']` for starting plugin-ldap
1091
- * If using Node.js, node must be version >= 22.6.0 and include startup argument `--experimental-strip-types` e.g.; `node --experimental-strip-types index.ts`
1123
+ * If using Node.js: node must be version >= 22.6.0, scimgateway must be downloaded from github (because stripping types is currently unsupported for files under node_modules) and startup argument `--experimental-strip-types` e.g.; `node --experimental-strip-types index.ts`
1092
1124
  * Plugins can use `scimgateway.HelperRest()` for REST functionality. Previously this logic was included in each plugin that used REST.
1093
1125
 
1094
1126
  // start - mandatory plugin initialization
package/bun.lockb CHANGED
Binary file
package/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  //
4
- // for nodejs (version >= 22.6.0) use shebang: #!/usr/bin/env -S node --experimental-strip-types
4
+ // for Node.js (version >= 22.6.0) use shebang: #!/usr/bin/env -S node --experimental-strip-types
5
5
  //
6
6
  // SCIM Gateway plugin startup
7
7
  // One or more plugin can be started having unique port listener (configuration scimgateway.port)
@@ -9,10 +9,10 @@
9
9
  // example starting all default plugins:
10
10
  // const plugins = ['loki', 'scim', 'entra-id', 'ldap', 'mssql', 'api', 'mongodb', 'saphana', 'soap']
11
11
  //
12
- // some plugins may require modules to be installed e.g.,:
13
- // soap - bun/npm install soap
14
- // mssql - bun/npm install tedious
15
- // saphana - bun/npm install hdb
12
+ // some plugin requires module to be installed e.g.,:
13
+ // plugin-soap: bun install soap
14
+ // plugin-mssql: bun install tedious
15
+ // plugin-saphana - bun install hdb
16
16
  //
17
17
 
18
18
  const plugins = ['loki']
@@ -11,7 +11,7 @@ import type ScimGateway from 'scimgateway'
11
11
  */
12
12
  export class HelperRest {
13
13
  private lock = new utils.Lock()
14
- private _serviceClient = {}
14
+ private _serviceClient: Record<string, any> = {}
15
15
  private config_entity: any
16
16
  private scimgateway: ScimGateway
17
17
  private graphUrl = 'https://graph.microsoft.com/beta' // beta instead of 'v1.0' gives all user attributes when no $select
@@ -47,8 +47,8 @@ export class HelperRest {
47
47
  * @param ctx having format { autorization: "<type>:xxxxx" }
48
48
  * @returns user_secret
49
49
  **/
50
- private getClientIdentifier(ctx): string | undefined {
51
- if (!ctx?.headers?.authorization) return undefined
50
+ private getClientIdentifier(ctx: Record<string, any> | undefined): string {
51
+ if (!ctx?.headers?.authorization) return 'undefined'
52
52
  const [user, secret] = this.getCtxAuth(ctx)
53
53
  return `${encodeURIComponent(user)}_${encodeURIComponent(secret)}` // user_password or undefined_password
54
54
  }
@@ -58,7 +58,7 @@ export class HelperRest {
58
58
  * @param ctx includes Auth PassThrough having format {headers:{autorization:"<type>:xxxxx"}}
59
59
  * @returns [username, secret]
60
60
  **/
61
- private getCtxAuth(ctx): any[] {
61
+ private getCtxAuth(ctx: Record<string, any> | undefined): any[] {
62
62
  if (!ctx?.headers?.authorization) return []
63
63
  const [authType, authToken] = (ctx.headers.authorization || '').split(' ') // [0] = 'Basic' or 'Bearer'
64
64
  let username, password
@@ -73,7 +73,7 @@ export class HelperRest {
73
73
  * @param ctx
74
74
  * @returns oauth accesstoken
75
75
  */
76
- private async getAccessToken(baseEntity: string, ctx: string | undefined) {
76
+ private async getAccessToken(baseEntity: string, ctx: Record<string, any> | undefined) {
77
77
  await this.lock.acquire()
78
78
  const clientIdentifier = this.getClientIdentifier(ctx)
79
79
  const d = Math.floor(Date.now() / 1000) // seconds (unix time)
@@ -381,7 +381,7 @@ export class HelperRest {
381
381
  * @param clientIdentifier
382
382
  * @param obj
383
383
  */
384
- private updateServiceClient(baseEntity: string, clientIdentifier: string | undefined, obj: any) {
384
+ private updateServiceClient(baseEntity: string, clientIdentifier: string, obj: any) {
385
385
  if (this._serviceClient[baseEntity] && this._serviceClient[baseEntity][clientIdentifier]) this._serviceClient[baseEntity][clientIdentifier] = utils.extendObj(this._serviceClient[baseEntity][clientIdentifier], obj)
386
386
  }
387
387
 
@@ -396,7 +396,7 @@ export class HelperRest {
396
396
  * @param opt web-standard fetch client options, e.g., options not defined as general options in configuration file
397
397
  * @param retryCount internal use only - internal counter for retry and failover logic to other baseUrls defined
398
398
  **/
399
- private async doRequestHandler(baseEntity: string, method: string, path: string, body?: any, ctx?: any, opt?: any, retryCount?: number) {
399
+ private async doRequestHandler(baseEntity: string, method: string, path: string, body?: any, ctx?: any, opt?: any, retryCount?: number): Promise<any> {
400
400
  let retryAfter = 0
401
401
  try {
402
402
  const cli = await this.getServiceClient(baseEntity, method, path, opt, ctx)
@@ -219,7 +219,7 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
219
219
  try {
220
220
  const users: any = await doRequest(baseEntity, method, base, ldapOptions, ctx) // ignoring SCIM paging startIndex/count - get all
221
221
  result.totalResults = users.length
222
- result.Resources = await Promise.all(users.map(async (user) => { // Promise.all because of async map
222
+ result.Resources = await Promise.all(users.map(async (user: any) => { // Promise.all because of async map
223
223
  // endpoint spesific attribute handling
224
224
  // "active" must be handled separate
225
225
  if (user.userAccountControl !== undefined) { // SCIM "active" - Active Directory
@@ -368,7 +368,7 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
368
368
  delete attrObj.groups // make sure to be removed from attrObj
369
369
 
370
370
  const [groupsAttr] = scimgateway.endpointMapper('outbound', 'groups.value', config.map.user)
371
- const grp = { add: {}, remove: {} }
371
+ const grp: any = { add: {}, remove: {} }
372
372
  grp.add[groupsAttr] = []
373
373
  grp.remove[groupsAttr] = []
374
374
 
@@ -484,7 +484,7 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
484
484
  // clean up zoombie group members and use the new user DN incase not handled by ldap server
485
485
  const [memberAttr] = scimgateway.endpointMapper('outbound', 'members.value', config.map.group)
486
486
  if (memberAttr) {
487
- const grp = { add: {}, remove: {} }
487
+ const grp: any = { add: {}, remove: {} }
488
488
  grp.add[memberAttr] = []
489
489
  grp.remove[memberAttr] = []
490
490
  let r
@@ -644,7 +644,7 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
644
644
  if (ldapOptions === 'getMemberOfGroups') result.Resources = await getMemberOfGroups(baseEntity, getObj.value, ctx)
645
645
  else {
646
646
  const groups: any = await doRequest(baseEntity, method, base, ldapOptions, ctx)
647
- result.Resources = await Promise.all(groups.map(async (group) => { // Promise.all because of async map
647
+ result.Resources = await Promise.all(groups.map(async (group: any) => { // Promise.all because of async map
648
648
  if (config.useSID_id || config.useGUID_id) {
649
649
  if (group.member) {
650
650
  const arr: string[] = []
@@ -756,7 +756,7 @@ scimgateway.modifyGroup = async (baseEntity, id, attrObj, ctx) => {
756
756
  const [memberAttr] = scimgateway.endpointMapper('outbound', 'members.value', config.map.group)
757
757
  if (!memberAttr && attrObj.members) throw new Error(`${action} error: missing attribute mapping configuration for group members`)
758
758
 
759
- const grp = { add: {}, remove: {} }
759
+ const grp: any = { add: {}, remove: {} }
760
760
  grp.add[memberAttr] = []
761
761
  grp.remove[memberAttr] = []
762
762
 
@@ -821,14 +821,14 @@ scimgateway.modifyGroup = async (baseEntity, id, attrObj, ctx) => {
821
821
  // helpers
822
822
  // =================================================
823
823
 
824
- const _serviceClient = {}
824
+ const _serviceClient: Record<string, any> = {}
825
825
 
826
826
  //
827
827
  // createAndFilter creates AndFilter object to be used as filter instead of standard string filter
828
828
  // Using AndFilter object for eliminating internal ldapjs escaping problems related to values with some
829
829
  // combinations of parentheses e.g. ab(c)d
830
830
  //
831
- const createAndFilter = (baseEntity, type, arrObj) => {
831
+ const createAndFilter = (baseEntity: string, type: string, arrObj: any) => {
832
832
  const objFilters: ldap.PresenceFilter[] | ldap.SubstringFilter[] = []
833
833
 
834
834
  // add arrObj
@@ -906,7 +906,7 @@ const createAndFilter = (baseEntity, type, arrObj) => {
906
906
  //
907
907
  // dnToSidGuid is used for Active Directory to return objectGUID based on dn
908
908
  //
909
- const dnToSidGuid = async (baseEntity, dn, ctx): Promise<string> => {
909
+ const dnToSidGuid = async (baseEntity: string, dn: any, ctx: any): Promise<string> => {
910
910
  const method = 'search'
911
911
  const ldapOptions: any = {}
912
912
  if (config.useSID_id) ldapOptions.attributes = ['objectSid']
@@ -928,7 +928,7 @@ const dnToSidGuid = async (baseEntity, dn, ctx): Promise<string> => {
928
928
  //
929
929
  // guidToDn is used for Active Directory to return dn based on objectGUID
930
930
  //
931
- const sidGuidToDn = async (baseEntity, id, ctx): Promise<string> => {
931
+ const sidGuidToDn = async (baseEntity: string, id: string, ctx: any): Promise<string> => {
932
932
  const method = 'search'
933
933
  const ldapOptions = {
934
934
  attributes: ['dn'],
@@ -959,8 +959,8 @@ const sidGuidToDn = async (baseEntity, id, ctx): Promise<string> => {
959
959
  // output: S-1-5-21-2657077294-4200173015-2627628055-1146
960
960
  // ref: https://gist.github.com/Krizzzn/0ae47f280cca9749c67759a9adedc015
961
961
  //
962
- const pad = function (s) { if (s.length < 2) { return `0${s}` } else { return s } }
963
- const convertSidToString = (buf) => {
962
+ const pad = function (s: any) { if (s.length < 2) { return `0${s}` } else { return s } }
963
+ const convertSidToString = (buf: any) => {
964
964
  let asc: any, end: any
965
965
  let i: number
966
966
  if (buf == null) { return null }
@@ -997,7 +997,7 @@ const convertSidToString = (buf) => {
997
997
  // output: 010500000000000515000000a065cf7e784b9b5fe77c8770091c0100
998
998
  // ref: https://devblogs.microsoft.com/oldnewthing/20040315-00/?p=40253
999
999
  //
1000
- const convertStringToSid = (sidStr) => {
1000
+ const convertStringToSid = (sidStr: string) => {
1001
1001
  const arr = sidStr.split('-')
1002
1002
  if (arr.length !== 8) return null
1003
1003
  try {
@@ -1025,7 +1025,7 @@ const convertStringToSid = (sidStr) => {
1025
1025
  // getMemberOfGroups returns all groups the user is member of
1026
1026
  // [{ id: <id-group>> , displayName: <displayName-group>, members [{value: <id-user>}] }]
1027
1027
  //
1028
- const getMemberOfGroups = async (baseEntity, id, ctx) => {
1028
+ const getMemberOfGroups = async (baseEntity: string, id: string, ctx: any) => {
1029
1029
  const action = 'getMemberOfGroups'
1030
1030
  if (!config.map.group) throw new Error('missing configuration endpoint.map.group') // not using groups
1031
1031
 
@@ -1075,7 +1075,7 @@ const getMemberOfGroups = async (baseEntity, id, ctx) => {
1075
1075
 
1076
1076
  try {
1077
1077
  const groups: any = await doRequest(baseEntity, method, base, ldapOptions, ctx)
1078
- return groups.map((grp) => {
1078
+ return groups.map((grp: any) => {
1079
1079
  return { // { id: <id-group>> , displayName: <displayName-group>, members [{value: <id-user>}] }
1080
1080
  id: encodeURIComponent(grp[attrs[0]]), // not mandatory, but included anyhow
1081
1081
  displayName: grp[attrs[1]], // displayName is mandatory
@@ -1093,7 +1093,7 @@ const getMemberOfGroups = async (baseEntity, id, ctx) => {
1093
1093
  // using OpenLDAP, DN must be escaped - national characters and special ldap characters
1094
1094
  // using Active Directory (none OpenLDAP), DN should not be escaped, but DN retrieved from AD is character escaped
1095
1095
  //
1096
- const ldapEscDn = (isOpenLdap, str) => {
1096
+ const ldapEscDn = (isOpenLdap: any, str: string) => {
1097
1097
  if (typeof str !== 'string' || str.length < 1) return str
1098
1098
 
1099
1099
  if (!isOpenLdap && str.indexOf('\\') > 0) {
@@ -1150,13 +1150,13 @@ const ldapEscDn = (isOpenLdap, str) => {
1150
1150
  if (i === 0) {
1151
1151
  const ua = new Uint8Array(Buffer.from(a[1], 'utf-8'))
1152
1152
  const buf = Buffer.from(new Uint8Array([4, ua.length, ...ua]))
1153
- const rdn = {}
1153
+ const rdn: any = {}
1154
1154
  rdn[a[0]] = new BerReader(buf)
1155
1155
  dn.push(new ldap.RDN(rdn))
1156
1156
  // new BerReader(Buffer.from([0x04, 0x05, 0x4B, 0xc3, 0xbc, 0x72, 0x74])) // Kürt
1157
1157
  // the leading 04 is the tag for "octet string" and the following 05 is the length in bytes of the string.
1158
1158
  } else {
1159
- const rdn = {}
1159
+ const rdn: any = {}
1160
1160
  rdn[a[0]] = a[1]
1161
1161
  dn.push(new ldap.RDN(rdn))
1162
1162
  }
@@ -1172,7 +1172,7 @@ const ldapEscDn = (isOpenLdap, str) => {
1172
1172
  // Hex encoded escaping (extended and unicode ascii) is not included because
1173
1173
  // automatically handled by ldapjs when not using BER encoded DN
1174
1174
  //
1175
- const ldapEsc = (str) => {
1175
+ const ldapEsc = (str: any) => {
1176
1176
  if (!str) return str
1177
1177
  let newStr = ''
1178
1178
  for (let i = 0; i < str.length; i++) {
@@ -1228,7 +1228,7 @@ const ldapEsc = (str) => {
1228
1228
  // only using BER on first part of dn
1229
1229
  // Having BER decoding for Active Directory, but not for OpenLDAP
1230
1230
  //
1231
- const berDecodeDn = (dn) => {
1231
+ const berDecodeDn = (dn: any) => {
1232
1232
  if (Object.prototype.toString.call(dn) !== '[object LdapDn]') return dn // OpenLDAP
1233
1233
  const str = dn.toString()
1234
1234
  if (str.indexOf('#') < 1) return str
@@ -1262,7 +1262,7 @@ const berDecodeDn = (dn) => {
1262
1262
  return str
1263
1263
  }
1264
1264
 
1265
- const getNamingAttribute = (baseEntity, type) => {
1265
+ const getNamingAttribute = (baseEntity: string, type: string) => {
1266
1266
  let arr
1267
1267
  switch (type) {
1268
1268
  case 'user':
@@ -1278,7 +1278,7 @@ const getNamingAttribute = (baseEntity, type) => {
1278
1278
  return [arr[0].attribute, arr[0].mapTo]
1279
1279
  }
1280
1280
 
1281
- const checkIfNewDN = (baseEntity, base, type, obj, endpointObj) => {
1281
+ const checkIfNewDN = (baseEntity: string, base: any, type: string, obj: any, endpointObj: any) => {
1282
1282
  if (typeof obj !== 'object' || Object.keys(obj).length < 1) return ''
1283
1283
  if (typeof endpointObj !== 'object' || Object.keys(endpointObj).length < 1) return ''
1284
1284
 
@@ -1326,7 +1326,7 @@ const checkIfNewDN = (baseEntity, base, type, obj, endpointObj) => {
1326
1326
  //
1327
1327
  // getCtxAuth returns username/secret from ctx header when using Auth PassThrough
1328
1328
  //
1329
- const getCtxAuth = (ctx) => {
1329
+ const getCtxAuth = (ctx: any) => {
1330
1330
  if (!ctx?.request?.header?.authorization) return []
1331
1331
  const [authType, authToken] = (ctx.request.header.authorization || '').split(' ') // [0] = 'Basic' or 'Bearer'
1332
1332
  let username, password
@@ -1338,7 +1338,7 @@ const getCtxAuth = (ctx) => {
1338
1338
  //
1339
1339
  // getServiceClient returns LDAP client used by doRequest
1340
1340
  //
1341
- const getServiceClient = async (baseEntity, ctx) => {
1341
+ const getServiceClient = async (baseEntity: string, ctx: any) => {
1342
1342
  const action = 'getServiceClient'
1343
1343
  // TODO if (!config.entity[baseEntity].passwordDecrypted) config.entity[baseEntity].passwordDecrypted = scimgateway.getPassword(`endpoint.entity.${baseEntity}.password`, configFile)
1344
1344
  if (!config.entity[baseEntity].baseUrl) config.entity[baseEntity].baseUrl = config.entity[baseEntity].baseUrls[0] // failover logic also updates baseUrl
@@ -1390,7 +1390,7 @@ const getServiceClient = async (baseEntity, ctx) => {
1390
1390
  // "attributes": ["sAMAccountName","displayName","mail"]
1391
1391
  // }
1392
1392
  //
1393
- const doRequest = async (baseEntity, method, base, options, ctx) => {
1393
+ const doRequest = async (baseEntity: string, method: string, base: any, options: any, ctx: any) => {
1394
1394
  if (!config.entity[baseEntity]) throw new Error(`unsupported baseEntity: ${baseEntity}`)
1395
1395
  let result: any = null
1396
1396
  let client: any = null
@@ -1413,15 +1413,15 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1413
1413
  result = await new Promise((resolve, reject) => {
1414
1414
  const results: any = []
1415
1415
 
1416
- client.search(base, options, (err, search) => {
1416
+ client.search(base, options, (err: any, search: any) => {
1417
1417
  if (err) {
1418
1418
  return reject(err)
1419
1419
  }
1420
1420
 
1421
- search.on('searchEntry', (entry) => {
1421
+ search.on('searchEntry', (entry: any) => {
1422
1422
  if (!entry.pojo || !entry.pojo.attributes) return
1423
1423
  const obj: any = { dn: entry.pojo.objectName }
1424
- entry.pojo.attributes.map((el) => {
1424
+ entry.pojo.attributes.map((el: any) => {
1425
1425
  if (el.values.length > 1) obj[el.type] = el.values
1426
1426
  else obj[el.type] = el.values[0]
1427
1427
  return null
@@ -1449,7 +1449,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1449
1449
  // for OpenLDAP ensure dn is not hex escaped e.g.: cn=K\c3\bcrt => cn=Kürt
1450
1450
  // because dn may be be used as value in standard attributes like group memberOf
1451
1451
  obj.dn = obj.dn.replace(/\\\\/g, '__') // temp
1452
- let conv = obj.dn.replace(/\\([0-9A-Fa-f]{2})/g, (_, hex) => {
1452
+ let conv = obj.dn.replace(/\\([0-9A-Fa-f]{2})/g, (_: any, hex: any) => {
1453
1453
  const intAscii = parseInt(hex, 16)
1454
1454
  if (intAscii > 128) { // extended ascii - will be unescaped by decodeURIComponent
1455
1455
  return '%' + hex
@@ -1470,12 +1470,12 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1470
1470
  })
1471
1471
  */
1472
1472
 
1473
- search.on('error', (err) => {
1473
+ search.on('error', (err: any) => {
1474
1474
  if (err.message.includes('LdapErr: DSID-0C0909F2') || err.message.includes('NO_OBJECT')) return resolve([]) // object not found when using base <SID=...> or <GUID=...> ref. objectSid/objectGUID
1475
1475
  reject(err)
1476
1476
  })
1477
1477
 
1478
- search.on('end', (_) => { resolve(results) })
1478
+ search.on('end', (_: any) => { resolve(results) })
1479
1479
  })
1480
1480
  })
1481
1481
  break
@@ -1506,7 +1506,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1506
1506
  })
1507
1507
  changes.push(change)
1508
1508
  }
1509
- client.modify(dn, changes, (err) => {
1509
+ client.modify(dn, changes, (err: any) => {
1510
1510
  if (err) {
1511
1511
  if (options.operation && options.operation === 'add') {
1512
1512
  const msg = err.message.toLowerCase()
@@ -1526,7 +1526,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1526
1526
  let newDN = options?.modification?.newDN
1527
1527
  if (!newDN) return reject(new Error('modifyDN() missing newDN'))
1528
1528
  if (Object.prototype.toString.call(newDN) === '[object LdapDn]') newDN = newDN.toString()
1529
- client.modifyDN(dn, newDN, (err) => {
1529
+ client.modifyDN(dn, newDN, (err: any) => {
1530
1530
  if (err) {
1531
1531
  return reject(err)
1532
1532
  }
@@ -1537,7 +1537,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1537
1537
 
1538
1538
  case 'add':
1539
1539
  result = await new Promise((resolve: any, reject: any) => {
1540
- client.add(base, options, (err) => {
1540
+ client.add(base, options, (err: any) => {
1541
1541
  if (err) {
1542
1542
  return reject(err)
1543
1543
  }
@@ -1548,7 +1548,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1548
1548
 
1549
1549
  case 'del':
1550
1550
  result = await new Promise((resolve: any, reject: any) => {
1551
- client.del(base, (err) => {
1551
+ client.del(base, (err: any) => {
1552
1552
  if (err) {
1553
1553
  return reject(err)
1554
1554
  }