scimgateway 5.0.2 → 5.0.4

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
@@ -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,6 +1086,23 @@ MIT © [Jarle Elshaug](https://www.elshaug.xyz)
1071
1086
 
1072
1087
  ## Change log
1073
1088
 
1089
+ ### v5.0.4
1090
+
1091
+ [Improved]
1092
+
1093
+ - minor type definition cosmetics
1094
+
1095
+ ### v5.0.3
1096
+
1097
+ [Fixed]
1098
+
1099
+ - unauthorized connection when using configuration bearerJwtAzure
1100
+
1101
+ [Improved]
1102
+
1103
+ - minor type definition cosmetics
1104
+
1105
+
1074
1106
  ### v5.0.2
1075
1107
 
1076
1108
  [Improved]
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)
@@ -82,6 +82,7 @@
82
82
  'use strict'
83
83
 
84
84
  import ldap from 'ldapjs'
85
+ // @ts-expect-error missing type definitions
85
86
  import { BerReader } from '@ldapjs/asn1'
86
87
 
87
88
  // for supporting nodejs running scimgateway package directly, using dynamic import instead of: import { ScimGateway } from 'scimgateway'
@@ -219,7 +220,7 @@ scimgateway.getUsers = async (baseEntity, getObj, attributes, ctx) => {
219
220
  try {
220
221
  const users: any = await doRequest(baseEntity, method, base, ldapOptions, ctx) // ignoring SCIM paging startIndex/count - get all
221
222
  result.totalResults = users.length
222
- result.Resources = await Promise.all(users.map(async (user) => { // Promise.all because of async map
223
+ result.Resources = await Promise.all(users.map(async (user: any) => { // Promise.all because of async map
223
224
  // endpoint spesific attribute handling
224
225
  // "active" must be handled separate
225
226
  if (user.userAccountControl !== undefined) { // SCIM "active" - Active Directory
@@ -368,7 +369,7 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
368
369
  delete attrObj.groups // make sure to be removed from attrObj
369
370
 
370
371
  const [groupsAttr] = scimgateway.endpointMapper('outbound', 'groups.value', config.map.user)
371
- const grp = { add: {}, remove: {} }
372
+ const grp: any = { add: {}, remove: {} }
372
373
  grp.add[groupsAttr] = []
373
374
  grp.remove[groupsAttr] = []
374
375
 
@@ -484,7 +485,7 @@ scimgateway.modifyUser = async (baseEntity, id, attrObj, ctx) => {
484
485
  // clean up zoombie group members and use the new user DN incase not handled by ldap server
485
486
  const [memberAttr] = scimgateway.endpointMapper('outbound', 'members.value', config.map.group)
486
487
  if (memberAttr) {
487
- const grp = { add: {}, remove: {} }
488
+ const grp: any = { add: {}, remove: {} }
488
489
  grp.add[memberAttr] = []
489
490
  grp.remove[memberAttr] = []
490
491
  let r
@@ -644,7 +645,7 @@ scimgateway.getGroups = async (baseEntity, getObj, attributes, ctx) => {
644
645
  if (ldapOptions === 'getMemberOfGroups') result.Resources = await getMemberOfGroups(baseEntity, getObj.value, ctx)
645
646
  else {
646
647
  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
648
+ result.Resources = await Promise.all(groups.map(async (group: any) => { // Promise.all because of async map
648
649
  if (config.useSID_id || config.useGUID_id) {
649
650
  if (group.member) {
650
651
  const arr: string[] = []
@@ -756,7 +757,7 @@ scimgateway.modifyGroup = async (baseEntity, id, attrObj, ctx) => {
756
757
  const [memberAttr] = scimgateway.endpointMapper('outbound', 'members.value', config.map.group)
757
758
  if (!memberAttr && attrObj.members) throw new Error(`${action} error: missing attribute mapping configuration for group members`)
758
759
 
759
- const grp = { add: {}, remove: {} }
760
+ const grp: any = { add: {}, remove: {} }
760
761
  grp.add[memberAttr] = []
761
762
  grp.remove[memberAttr] = []
762
763
 
@@ -821,14 +822,14 @@ scimgateway.modifyGroup = async (baseEntity, id, attrObj, ctx) => {
821
822
  // helpers
822
823
  // =================================================
823
824
 
824
- const _serviceClient = {}
825
+ const _serviceClient: Record<string, any> = {}
825
826
 
826
827
  //
827
828
  // createAndFilter creates AndFilter object to be used as filter instead of standard string filter
828
829
  // Using AndFilter object for eliminating internal ldapjs escaping problems related to values with some
829
830
  // combinations of parentheses e.g. ab(c)d
830
831
  //
831
- const createAndFilter = (baseEntity, type, arrObj) => {
832
+ const createAndFilter = (baseEntity: string, type: string, arrObj: any) => {
832
833
  const objFilters: ldap.PresenceFilter[] | ldap.SubstringFilter[] = []
833
834
 
834
835
  // add arrObj
@@ -906,7 +907,7 @@ const createAndFilter = (baseEntity, type, arrObj) => {
906
907
  //
907
908
  // dnToSidGuid is used for Active Directory to return objectGUID based on dn
908
909
  //
909
- const dnToSidGuid = async (baseEntity, dn, ctx): Promise<string> => {
910
+ const dnToSidGuid = async (baseEntity: string, dn: any, ctx: any): Promise<string> => {
910
911
  const method = 'search'
911
912
  const ldapOptions: any = {}
912
913
  if (config.useSID_id) ldapOptions.attributes = ['objectSid']
@@ -928,7 +929,7 @@ const dnToSidGuid = async (baseEntity, dn, ctx): Promise<string> => {
928
929
  //
929
930
  // guidToDn is used for Active Directory to return dn based on objectGUID
930
931
  //
931
- const sidGuidToDn = async (baseEntity, id, ctx): Promise<string> => {
932
+ const sidGuidToDn = async (baseEntity: string, id: string, ctx: any): Promise<string> => {
932
933
  const method = 'search'
933
934
  const ldapOptions = {
934
935
  attributes: ['dn'],
@@ -959,8 +960,8 @@ const sidGuidToDn = async (baseEntity, id, ctx): Promise<string> => {
959
960
  // output: S-1-5-21-2657077294-4200173015-2627628055-1146
960
961
  // ref: https://gist.github.com/Krizzzn/0ae47f280cca9749c67759a9adedc015
961
962
  //
962
- const pad = function (s) { if (s.length < 2) { return `0${s}` } else { return s } }
963
- const convertSidToString = (buf) => {
963
+ const pad = function (s: any) { if (s.length < 2) { return `0${s}` } else { return s } }
964
+ const convertSidToString = (buf: any) => {
964
965
  let asc: any, end: any
965
966
  let i: number
966
967
  if (buf == null) { return null }
@@ -997,7 +998,7 @@ const convertSidToString = (buf) => {
997
998
  // output: 010500000000000515000000a065cf7e784b9b5fe77c8770091c0100
998
999
  // ref: https://devblogs.microsoft.com/oldnewthing/20040315-00/?p=40253
999
1000
  //
1000
- const convertStringToSid = (sidStr) => {
1001
+ const convertStringToSid = (sidStr: string) => {
1001
1002
  const arr = sidStr.split('-')
1002
1003
  if (arr.length !== 8) return null
1003
1004
  try {
@@ -1025,7 +1026,7 @@ const convertStringToSid = (sidStr) => {
1025
1026
  // getMemberOfGroups returns all groups the user is member of
1026
1027
  // [{ id: <id-group>> , displayName: <displayName-group>, members [{value: <id-user>}] }]
1027
1028
  //
1028
- const getMemberOfGroups = async (baseEntity, id, ctx) => {
1029
+ const getMemberOfGroups = async (baseEntity: string, id: string, ctx: any) => {
1029
1030
  const action = 'getMemberOfGroups'
1030
1031
  if (!config.map.group) throw new Error('missing configuration endpoint.map.group') // not using groups
1031
1032
 
@@ -1075,7 +1076,7 @@ const getMemberOfGroups = async (baseEntity, id, ctx) => {
1075
1076
 
1076
1077
  try {
1077
1078
  const groups: any = await doRequest(baseEntity, method, base, ldapOptions, ctx)
1078
- return groups.map((grp) => {
1079
+ return groups.map((grp: any) => {
1079
1080
  return { // { id: <id-group>> , displayName: <displayName-group>, members [{value: <id-user>}] }
1080
1081
  id: encodeURIComponent(grp[attrs[0]]), // not mandatory, but included anyhow
1081
1082
  displayName: grp[attrs[1]], // displayName is mandatory
@@ -1093,7 +1094,7 @@ const getMemberOfGroups = async (baseEntity, id, ctx) => {
1093
1094
  // using OpenLDAP, DN must be escaped - national characters and special ldap characters
1094
1095
  // using Active Directory (none OpenLDAP), DN should not be escaped, but DN retrieved from AD is character escaped
1095
1096
  //
1096
- const ldapEscDn = (isOpenLdap, str) => {
1097
+ const ldapEscDn = (isOpenLdap: any, str: string) => {
1097
1098
  if (typeof str !== 'string' || str.length < 1) return str
1098
1099
 
1099
1100
  if (!isOpenLdap && str.indexOf('\\') > 0) {
@@ -1150,13 +1151,13 @@ const ldapEscDn = (isOpenLdap, str) => {
1150
1151
  if (i === 0) {
1151
1152
  const ua = new Uint8Array(Buffer.from(a[1], 'utf-8'))
1152
1153
  const buf = Buffer.from(new Uint8Array([4, ua.length, ...ua]))
1153
- const rdn = {}
1154
+ const rdn: any = {}
1154
1155
  rdn[a[0]] = new BerReader(buf)
1155
1156
  dn.push(new ldap.RDN(rdn))
1156
1157
  // new BerReader(Buffer.from([0x04, 0x05, 0x4B, 0xc3, 0xbc, 0x72, 0x74])) // Kürt
1157
1158
  // the leading 04 is the tag for "octet string" and the following 05 is the length in bytes of the string.
1158
1159
  } else {
1159
- const rdn = {}
1160
+ const rdn: any = {}
1160
1161
  rdn[a[0]] = a[1]
1161
1162
  dn.push(new ldap.RDN(rdn))
1162
1163
  }
@@ -1172,7 +1173,7 @@ const ldapEscDn = (isOpenLdap, str) => {
1172
1173
  // Hex encoded escaping (extended and unicode ascii) is not included because
1173
1174
  // automatically handled by ldapjs when not using BER encoded DN
1174
1175
  //
1175
- const ldapEsc = (str) => {
1176
+ const ldapEsc = (str: any) => {
1176
1177
  if (!str) return str
1177
1178
  let newStr = ''
1178
1179
  for (let i = 0; i < str.length; i++) {
@@ -1228,7 +1229,7 @@ const ldapEsc = (str) => {
1228
1229
  // only using BER on first part of dn
1229
1230
  // Having BER decoding for Active Directory, but not for OpenLDAP
1230
1231
  //
1231
- const berDecodeDn = (dn) => {
1232
+ const berDecodeDn = (dn: any) => {
1232
1233
  if (Object.prototype.toString.call(dn) !== '[object LdapDn]') return dn // OpenLDAP
1233
1234
  const str = dn.toString()
1234
1235
  if (str.indexOf('#') < 1) return str
@@ -1262,7 +1263,7 @@ const berDecodeDn = (dn) => {
1262
1263
  return str
1263
1264
  }
1264
1265
 
1265
- const getNamingAttribute = (baseEntity, type) => {
1266
+ const getNamingAttribute = (baseEntity: string, type: string) => {
1266
1267
  let arr
1267
1268
  switch (type) {
1268
1269
  case 'user':
@@ -1278,7 +1279,7 @@ const getNamingAttribute = (baseEntity, type) => {
1278
1279
  return [arr[0].attribute, arr[0].mapTo]
1279
1280
  }
1280
1281
 
1281
- const checkIfNewDN = (baseEntity, base, type, obj, endpointObj) => {
1282
+ const checkIfNewDN = (baseEntity: string, base: any, type: string, obj: any, endpointObj: any) => {
1282
1283
  if (typeof obj !== 'object' || Object.keys(obj).length < 1) return ''
1283
1284
  if (typeof endpointObj !== 'object' || Object.keys(endpointObj).length < 1) return ''
1284
1285
 
@@ -1326,7 +1327,7 @@ const checkIfNewDN = (baseEntity, base, type, obj, endpointObj) => {
1326
1327
  //
1327
1328
  // getCtxAuth returns username/secret from ctx header when using Auth PassThrough
1328
1329
  //
1329
- const getCtxAuth = (ctx) => {
1330
+ const getCtxAuth = (ctx: any) => {
1330
1331
  if (!ctx?.request?.header?.authorization) return []
1331
1332
  const [authType, authToken] = (ctx.request.header.authorization || '').split(' ') // [0] = 'Basic' or 'Bearer'
1332
1333
  let username, password
@@ -1338,7 +1339,7 @@ const getCtxAuth = (ctx) => {
1338
1339
  //
1339
1340
  // getServiceClient returns LDAP client used by doRequest
1340
1341
  //
1341
- const getServiceClient = async (baseEntity, ctx) => {
1342
+ const getServiceClient = async (baseEntity: string, ctx: any) => {
1342
1343
  const action = 'getServiceClient'
1343
1344
  // TODO if (!config.entity[baseEntity].passwordDecrypted) config.entity[baseEntity].passwordDecrypted = scimgateway.getPassword(`endpoint.entity.${baseEntity}.password`, configFile)
1344
1345
  if (!config.entity[baseEntity].baseUrl) config.entity[baseEntity].baseUrl = config.entity[baseEntity].baseUrls[0] // failover logic also updates baseUrl
@@ -1390,7 +1391,7 @@ const getServiceClient = async (baseEntity, ctx) => {
1390
1391
  // "attributes": ["sAMAccountName","displayName","mail"]
1391
1392
  // }
1392
1393
  //
1393
- const doRequest = async (baseEntity, method, base, options, ctx) => {
1394
+ const doRequest = async (baseEntity: string, method: string, base: any, options: any, ctx: any) => {
1394
1395
  if (!config.entity[baseEntity]) throw new Error(`unsupported baseEntity: ${baseEntity}`)
1395
1396
  let result: any = null
1396
1397
  let client: any = null
@@ -1413,15 +1414,15 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1413
1414
  result = await new Promise((resolve, reject) => {
1414
1415
  const results: any = []
1415
1416
 
1416
- client.search(base, options, (err, search) => {
1417
+ client.search(base, options, (err: any, search: any) => {
1417
1418
  if (err) {
1418
1419
  return reject(err)
1419
1420
  }
1420
1421
 
1421
- search.on('searchEntry', (entry) => {
1422
+ search.on('searchEntry', (entry: any) => {
1422
1423
  if (!entry.pojo || !entry.pojo.attributes) return
1423
1424
  const obj: any = { dn: entry.pojo.objectName }
1424
- entry.pojo.attributes.map((el) => {
1425
+ entry.pojo.attributes.map((el: any) => {
1425
1426
  if (el.values.length > 1) obj[el.type] = el.values
1426
1427
  else obj[el.type] = el.values[0]
1427
1428
  return null
@@ -1449,7 +1450,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1449
1450
  // for OpenLDAP ensure dn is not hex escaped e.g.: cn=K\c3\bcrt => cn=Kürt
1450
1451
  // because dn may be be used as value in standard attributes like group memberOf
1451
1452
  obj.dn = obj.dn.replace(/\\\\/g, '__') // temp
1452
- let conv = obj.dn.replace(/\\([0-9A-Fa-f]{2})/g, (_, hex) => {
1453
+ let conv = obj.dn.replace(/\\([0-9A-Fa-f]{2})/g, (_: any, hex: any) => {
1453
1454
  const intAscii = parseInt(hex, 16)
1454
1455
  if (intAscii > 128) { // extended ascii - will be unescaped by decodeURIComponent
1455
1456
  return '%' + hex
@@ -1470,12 +1471,12 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1470
1471
  })
1471
1472
  */
1472
1473
 
1473
- search.on('error', (err) => {
1474
+ search.on('error', (err: any) => {
1474
1475
  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
1476
  reject(err)
1476
1477
  })
1477
1478
 
1478
- search.on('end', (_) => { resolve(results) })
1479
+ search.on('end', (_: any) => { resolve(results) })
1479
1480
  })
1480
1481
  })
1481
1482
  break
@@ -1506,7 +1507,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1506
1507
  })
1507
1508
  changes.push(change)
1508
1509
  }
1509
- client.modify(dn, changes, (err) => {
1510
+ client.modify(dn, changes, (err: any) => {
1510
1511
  if (err) {
1511
1512
  if (options.operation && options.operation === 'add') {
1512
1513
  const msg = err.message.toLowerCase()
@@ -1526,7 +1527,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1526
1527
  let newDN = options?.modification?.newDN
1527
1528
  if (!newDN) return reject(new Error('modifyDN() missing newDN'))
1528
1529
  if (Object.prototype.toString.call(newDN) === '[object LdapDn]') newDN = newDN.toString()
1529
- client.modifyDN(dn, newDN, (err) => {
1530
+ client.modifyDN(dn, newDN, (err: any) => {
1530
1531
  if (err) {
1531
1532
  return reject(err)
1532
1533
  }
@@ -1537,7 +1538,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1537
1538
 
1538
1539
  case 'add':
1539
1540
  result = await new Promise((resolve: any, reject: any) => {
1540
- client.add(base, options, (err) => {
1541
+ client.add(base, options, (err: any) => {
1541
1542
  if (err) {
1542
1543
  return reject(err)
1543
1544
  }
@@ -1548,7 +1549,7 @@ const doRequest = async (baseEntity, method, base, options, ctx) => {
1548
1549
 
1549
1550
  case 'del':
1550
1551
  result = await new Promise((resolve: any, reject: any) => {
1551
- client.del(base, (err) => {
1552
+ client.del(base, (err: any) => {
1552
1553
  if (err) {
1553
1554
  return reject(err)
1554
1555
  }