@backstage/plugin-catalog-backend-module-ldap 0.11.10 → 0.11.11-next.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @backstage/plugin-catalog-backend-module-ldap
2
2
 
3
+ ## 0.11.11-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility.
8
+ - Updated dependencies
9
+ - @backstage/config@1.3.6-next.0
10
+ - @backstage/catalog-model@1.7.6-next.0
11
+ - @backstage/backend-plugin-api@1.4.5-next.0
12
+ - @backstage/errors@1.2.7
13
+ - @backstage/types@1.2.2
14
+ - @backstage/plugin-catalog-common@1.1.7-next.0
15
+ - @backstage/plugin-catalog-node@1.19.2-next.0
16
+
3
17
  ## 0.11.10
4
18
 
5
19
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -152,10 +152,10 @@ declare function readProviderConfigs(config: Config): LdapProviderConfig[];
152
152
  * @public
153
153
  */
154
154
  declare class LdapClient {
155
- private readonly client;
156
- private readonly logger;
157
155
  private vendor;
158
156
  static create(logger: LoggerService, target: string, bind?: BindConfig, tls?: TLSConfig): Promise<LdapClient>;
157
+ private readonly client;
158
+ private readonly logger;
159
159
  constructor(client: Client, logger: LoggerService);
160
160
  /**
161
161
  * Performs an LDAP search operation.
@@ -14,10 +14,6 @@ var ldap__default = /*#__PURE__*/_interopDefaultCompat(ldap);
14
14
  var tlsLib__default = /*#__PURE__*/_interopDefaultCompat(tlsLib);
15
15
 
16
16
  class LdapClient {
17
- constructor(client, logger) {
18
- this.client = client;
19
- this.logger = logger;
20
- }
21
17
  vendor;
22
18
  static async create(logger, target, bind, tls) {
23
19
  let secureContext;
@@ -53,6 +49,12 @@ class LdapClient {
53
49
  });
54
50
  });
55
51
  }
52
+ client;
53
+ logger;
54
+ constructor(client, logger) {
55
+ this.client = client;
56
+ this.logger = logger;
57
+ }
56
58
  /**
57
59
  * Performs an LDAP search operation.
58
60
  *
@@ -1 +1 @@
1
- {"version":3,"file":"client.cjs.js","sources":["../../src/ldap/client.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ForwardedError, stringifyError } from '@backstage/errors';\nimport { readFile } from 'fs/promises';\nimport ldap, { Client, SearchEntry, SearchOptions } from 'ldapjs';\nimport { cloneDeep } from 'lodash';\nimport tlsLib from 'tls';\nimport { BindConfig, TLSConfig } from './config';\nimport { createOptions, errorString } from './util';\nimport {\n AEDirVendor,\n ActiveDirectoryVendor,\n DefaultLdapVendor,\n GoogleLdapVendor,\n LLDAPVendor,\n FreeIpaVendor,\n LdapVendor,\n} from './vendors';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\n/**\n * Basic wrapper for the `ldapjs` library.\n *\n * Helps out with promisifying calls, paging, binding etc.\n *\n * @public\n */\nexport class LdapClient {\n private vendor: Promise<LdapVendor> | undefined;\n\n static async create(\n logger: LoggerService,\n target: string,\n bind?: BindConfig,\n tls?: TLSConfig,\n ): Promise<LdapClient> {\n let secureContext;\n if (tls && tls.certs && tls.keys) {\n const cert = await readFile(tls.certs, 'utf-8');\n const key = await readFile(tls.keys, 'utf-8');\n secureContext = tlsLib.createSecureContext({\n cert: cert,\n key: key,\n });\n }\n\n const client = ldap.createClient({\n url: target,\n tlsOptions: {\n secureContext,\n rejectUnauthorized: tls?.rejectUnauthorized,\n },\n });\n\n // We want to have a catch-all error handler at the top, since the default\n // behavior of the client is to blow up the entire process when it fails,\n // unless an error handler is set.\n client.on('error', (err: ldap.Error) => {\n logger.warn(`LDAP client threw an error, ${errorString(err)}`);\n });\n\n if (!bind) {\n return new LdapClient(client, logger);\n }\n\n return new Promise<LdapClient>((resolve, reject) => {\n const { dn, secret } = bind;\n client.bind(dn, secret, err => {\n if (err) {\n reject(`LDAP bind failed for ${dn}, ${errorString(err)}`);\n } else {\n resolve(new LdapClient(client, logger));\n }\n });\n });\n }\n\n constructor(\n private readonly client: Client,\n private readonly logger: LoggerService,\n ) {}\n\n /**\n * Performs an LDAP search operation.\n *\n * @param dn - The fully qualified base DN to search within\n * @param options - The search options\n */\n async search(dn: string, options: SearchOptions): Promise<SearchEntry[]> {\n try {\n const output: SearchEntry[] = [];\n\n const logInterval = setInterval(() => {\n this.logger.debug(`Read ${output.length} LDAP entries so far...`);\n }, 5000);\n\n const search = new Promise<SearchEntry[]>((resolve, reject) => {\n // Note that we clone the (frozen) options, since ldapjs rudely tries to\n // overwrite parts of them\n this.client.search(dn, cloneDeep(options), (err, res) => {\n if (err) {\n reject(new Error(errorString(err)));\n return;\n }\n\n res.on('searchReference', () => {\n this.logger.warn('Received unsupported search referral');\n });\n\n res.on('searchEntry', entry => {\n output.push(entry);\n });\n\n res.on('error', e => {\n reject(new Error(errorString(e)));\n });\n\n res.on('page', (_result, cb) => {\n if (cb) {\n cb();\n }\n });\n\n res.on('end', r => {\n if (!r) {\n reject(new Error('Null response'));\n } else if (r.status !== 0) {\n reject(new Error(`Got status ${r.status}: ${r.errorMessage}`));\n } else {\n resolve(output);\n }\n });\n });\n });\n\n return await search.finally(() => {\n clearInterval(logInterval);\n });\n } catch (e) {\n throw new ForwardedError(`LDAP search at DN \"${dn}\" failed`, e);\n }\n }\n\n /**\n * Performs an LDAP search operation, calls a function on each entry to limit memory usage\n *\n * @param dn - The fully qualified base DN to search within\n * @param options - The search options\n * @param f - The callback to call on each search entry\n */\n async searchStreaming(\n dn: string,\n options: SearchOptions,\n f: (entry: SearchEntry) => Promise<void> | void,\n ): Promise<void> {\n try {\n return await new Promise<void>((resolve, reject) => {\n // Note that we clone the (frozen) options, since ldapjs rudely tries to\n // overwrite parts of them\n this.client.search(dn, createOptions(options), (err, res) => {\n if (err) {\n reject(new Error(errorString(err)));\n }\n let awaitList: Array<Promise<void> | void> = [];\n let transformError = false;\n\n const transformReject = (e: Error) => {\n transformError = true;\n reject(\n new Error(\n `Transform function threw an exception, ${stringifyError(e)}`,\n ),\n );\n };\n\n res.on('searchReference', () => {\n this.logger.warn('Received unsupported search referral');\n });\n\n res.on('searchEntry', entry => {\n if (!transformError) awaitList.push(f(entry));\n });\n\n res.on('page', (_, cb) => {\n // awaits completion before fetching next page\n Promise.all(awaitList)\n .then(() => {\n // flush list\n awaitList = [];\n if (cb) cb();\n })\n .catch(transformReject);\n });\n\n res.on('error', e => {\n reject(new Error(errorString(e)));\n });\n\n res.on('end', r => {\n if (!r) {\n throw new Error('Null response');\n } else if (r.status !== 0) {\n throw new Error(`Got status ${r.status}: ${r.errorMessage}`);\n } else {\n Promise.all(awaitList)\n .then(() => resolve())\n .catch(transformReject);\n }\n });\n });\n });\n } catch (e) {\n throw new ForwardedError(`LDAP search at DN \"${dn}\" failed`, e);\n }\n }\n\n /**\n * Get the Server Vendor.\n * Currently only detects Microsoft Active Directory Servers.\n *\n * @see https://ldapwiki.com/wiki/Determine%20LDAP%20Server%20Vendor\n */\n async getVendor(): Promise<LdapVendor> {\n if (this.vendor) {\n return this.vendor;\n }\n const clientHost = this.client?.host || '';\n this.vendor = this.getRootDSE()\n .then(root => {\n if (root && root.raw?.forestFunctionality) {\n return ActiveDirectoryVendor;\n } else if (root && root.raw?.ipaDomainLevel) {\n return FreeIpaVendor;\n } else if (root && 'aeRoot' in root.raw) {\n return AEDirVendor;\n } else if (clientHost === 'ldap.google.com') {\n return GoogleLdapVendor;\n } else if (root && root.raw?.vendorName?.toString() === 'LLDAP') {\n return LLDAPVendor;\n }\n return DefaultLdapVendor;\n })\n .catch(err => {\n this.vendor = undefined;\n throw err;\n });\n return this.vendor;\n }\n\n /**\n * Get the Root DSE.\n *\n * @see https://ldapwiki.com/wiki/RootDSE\n */\n async getRootDSE(): Promise<SearchEntry | undefined> {\n const result = await this.search('', {\n scope: 'base',\n filter: '(objectclass=*)',\n } as SearchOptions);\n if (result && result.length === 1) {\n return result[0];\n }\n return undefined;\n }\n}\n"],"names":["readFile","tlsLib","ldap","errorString","cloneDeep","ForwardedError","createOptions","stringifyError","ActiveDirectoryVendor","FreeIpaVendor","AEDirVendor","GoogleLdapVendor","LLDAPVendor","DefaultLdapVendor"],"mappings":"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"}
1
+ {"version":3,"file":"client.cjs.js","sources":["../../src/ldap/client.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ForwardedError, stringifyError } from '@backstage/errors';\nimport { readFile } from 'fs/promises';\nimport ldap, { Client, SearchEntry, SearchOptions } from 'ldapjs';\nimport { cloneDeep } from 'lodash';\nimport tlsLib from 'tls';\nimport { BindConfig, TLSConfig } from './config';\nimport { createOptions, errorString } from './util';\nimport {\n AEDirVendor,\n ActiveDirectoryVendor,\n DefaultLdapVendor,\n GoogleLdapVendor,\n LLDAPVendor,\n FreeIpaVendor,\n LdapVendor,\n} from './vendors';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\n/**\n * Basic wrapper for the `ldapjs` library.\n *\n * Helps out with promisifying calls, paging, binding etc.\n *\n * @public\n */\nexport class LdapClient {\n private vendor: Promise<LdapVendor> | undefined;\n\n static async create(\n logger: LoggerService,\n target: string,\n bind?: BindConfig,\n tls?: TLSConfig,\n ): Promise<LdapClient> {\n let secureContext;\n if (tls && tls.certs && tls.keys) {\n const cert = await readFile(tls.certs, 'utf-8');\n const key = await readFile(tls.keys, 'utf-8');\n secureContext = tlsLib.createSecureContext({\n cert: cert,\n key: key,\n });\n }\n\n const client = ldap.createClient({\n url: target,\n tlsOptions: {\n secureContext,\n rejectUnauthorized: tls?.rejectUnauthorized,\n },\n });\n\n // We want to have a catch-all error handler at the top, since the default\n // behavior of the client is to blow up the entire process when it fails,\n // unless an error handler is set.\n client.on('error', (err: ldap.Error) => {\n logger.warn(`LDAP client threw an error, ${errorString(err)}`);\n });\n\n if (!bind) {\n return new LdapClient(client, logger);\n }\n\n return new Promise<LdapClient>((resolve, reject) => {\n const { dn, secret } = bind;\n client.bind(dn, secret, err => {\n if (err) {\n reject(`LDAP bind failed for ${dn}, ${errorString(err)}`);\n } else {\n resolve(new LdapClient(client, logger));\n }\n });\n });\n }\n\n private readonly client: Client;\n private readonly logger: LoggerService;\n\n constructor(client: Client, logger: LoggerService) {\n this.client = client;\n this.logger = logger;\n }\n\n /**\n * Performs an LDAP search operation.\n *\n * @param dn - The fully qualified base DN to search within\n * @param options - The search options\n */\n async search(dn: string, options: SearchOptions): Promise<SearchEntry[]> {\n try {\n const output: SearchEntry[] = [];\n\n const logInterval = setInterval(() => {\n this.logger.debug(`Read ${output.length} LDAP entries so far...`);\n }, 5000);\n\n const search = new Promise<SearchEntry[]>((resolve, reject) => {\n // Note that we clone the (frozen) options, since ldapjs rudely tries to\n // overwrite parts of them\n this.client.search(dn, cloneDeep(options), (err, res) => {\n if (err) {\n reject(new Error(errorString(err)));\n return;\n }\n\n res.on('searchReference', () => {\n this.logger.warn('Received unsupported search referral');\n });\n\n res.on('searchEntry', entry => {\n output.push(entry);\n });\n\n res.on('error', e => {\n reject(new Error(errorString(e)));\n });\n\n res.on('page', (_result, cb) => {\n if (cb) {\n cb();\n }\n });\n\n res.on('end', r => {\n if (!r) {\n reject(new Error('Null response'));\n } else if (r.status !== 0) {\n reject(new Error(`Got status ${r.status}: ${r.errorMessage}`));\n } else {\n resolve(output);\n }\n });\n });\n });\n\n return await search.finally(() => {\n clearInterval(logInterval);\n });\n } catch (e) {\n throw new ForwardedError(`LDAP search at DN \"${dn}\" failed`, e);\n }\n }\n\n /**\n * Performs an LDAP search operation, calls a function on each entry to limit memory usage\n *\n * @param dn - The fully qualified base DN to search within\n * @param options - The search options\n * @param f - The callback to call on each search entry\n */\n async searchStreaming(\n dn: string,\n options: SearchOptions,\n f: (entry: SearchEntry) => Promise<void> | void,\n ): Promise<void> {\n try {\n return await new Promise<void>((resolve, reject) => {\n // Note that we clone the (frozen) options, since ldapjs rudely tries to\n // overwrite parts of them\n this.client.search(dn, createOptions(options), (err, res) => {\n if (err) {\n reject(new Error(errorString(err)));\n }\n let awaitList: Array<Promise<void> | void> = [];\n let transformError = false;\n\n const transformReject = (e: Error) => {\n transformError = true;\n reject(\n new Error(\n `Transform function threw an exception, ${stringifyError(e)}`,\n ),\n );\n };\n\n res.on('searchReference', () => {\n this.logger.warn('Received unsupported search referral');\n });\n\n res.on('searchEntry', entry => {\n if (!transformError) awaitList.push(f(entry));\n });\n\n res.on('page', (_, cb) => {\n // awaits completion before fetching next page\n Promise.all(awaitList)\n .then(() => {\n // flush list\n awaitList = [];\n if (cb) cb();\n })\n .catch(transformReject);\n });\n\n res.on('error', e => {\n reject(new Error(errorString(e)));\n });\n\n res.on('end', r => {\n if (!r) {\n throw new Error('Null response');\n } else if (r.status !== 0) {\n throw new Error(`Got status ${r.status}: ${r.errorMessage}`);\n } else {\n Promise.all(awaitList)\n .then(() => resolve())\n .catch(transformReject);\n }\n });\n });\n });\n } catch (e) {\n throw new ForwardedError(`LDAP search at DN \"${dn}\" failed`, e);\n }\n }\n\n /**\n * Get the Server Vendor.\n * Currently only detects Microsoft Active Directory Servers.\n *\n * @see https://ldapwiki.com/wiki/Determine%20LDAP%20Server%20Vendor\n */\n async getVendor(): Promise<LdapVendor> {\n if (this.vendor) {\n return this.vendor;\n }\n const clientHost = this.client?.host || '';\n this.vendor = this.getRootDSE()\n .then(root => {\n if (root && root.raw?.forestFunctionality) {\n return ActiveDirectoryVendor;\n } else if (root && root.raw?.ipaDomainLevel) {\n return FreeIpaVendor;\n } else if (root && 'aeRoot' in root.raw) {\n return AEDirVendor;\n } else if (clientHost === 'ldap.google.com') {\n return GoogleLdapVendor;\n } else if (root && root.raw?.vendorName?.toString() === 'LLDAP') {\n return LLDAPVendor;\n }\n return DefaultLdapVendor;\n })\n .catch(err => {\n this.vendor = undefined;\n throw err;\n });\n return this.vendor;\n }\n\n /**\n * Get the Root DSE.\n *\n * @see https://ldapwiki.com/wiki/RootDSE\n */\n async getRootDSE(): Promise<SearchEntry | undefined> {\n const result = await this.search('', {\n scope: 'base',\n filter: '(objectclass=*)',\n } as SearchOptions);\n if (result && result.length === 1) {\n return result[0];\n }\n return undefined;\n }\n}\n"],"names":["readFile","tlsLib","ldap","errorString","cloneDeep","ForwardedError","createOptions","stringifyError","ActiveDirectoryVendor","FreeIpaVendor","AEDirVendor","GoogleLdapVendor","LLDAPVendor","DefaultLdapVendor"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend-module-ldap",
3
- "version": "0.11.10",
3
+ "version": "0.11.11-next.0",
4
4
  "description": "A Backstage catalog backend module that helps integrate towards LDAP",
5
5
  "backstage": {
6
6
  "role": "backend-plugin-module",
@@ -41,20 +41,20 @@
41
41
  "test": "backstage-cli package test"
42
42
  },
43
43
  "dependencies": {
44
- "@backstage/backend-plugin-api": "^1.4.4",
45
- "@backstage/catalog-model": "^1.7.5",
46
- "@backstage/config": "^1.3.5",
47
- "@backstage/errors": "^1.2.7",
48
- "@backstage/plugin-catalog-common": "^1.1.6",
49
- "@backstage/plugin-catalog-node": "^1.19.1",
50
- "@backstage/types": "^1.2.2",
44
+ "@backstage/backend-plugin-api": "1.4.5-next.0",
45
+ "@backstage/catalog-model": "1.7.6-next.0",
46
+ "@backstage/config": "1.3.6-next.0",
47
+ "@backstage/errors": "1.2.7",
48
+ "@backstage/plugin-catalog-common": "1.1.7-next.0",
49
+ "@backstage/plugin-catalog-node": "1.19.2-next.0",
50
+ "@backstage/types": "1.2.2",
51
51
  "@types/ldapjs": "^2.2.5",
52
52
  "ldapjs": "^2.3.3",
53
53
  "lodash": "^4.17.21",
54
54
  "uuid": "^11.0.0"
55
55
  },
56
56
  "devDependencies": {
57
- "@backstage/cli": "^0.34.4",
57
+ "@backstage/cli": "0.34.5-next.0",
58
58
  "@types/lodash": "^4.14.151"
59
59
  },
60
60
  "configSchema": "config.d.ts",