@backstage-community/plugin-catalog-backend-module-keycloak 3.2.4 → 3.3.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,11 @@
1
1
  ### Dependencies
2
2
 
3
+ ## 3.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - bd47f3a: Backstage version bump to v1.34.2
8
+
3
9
  ## 3.2.4
4
10
 
5
11
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"extensions.cjs.js","sources":["../src/extensions.ts"],"sourcesContent":["/*\n * Copyright 2024 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 */\nimport { createExtensionPoint } from '@backstage/backend-plugin-api';\n\nimport type { GroupTransformer, UserTransformer } from './lib/types';\n\n/**\n * An extension point that exposes the ability to implement user and group transformer functions for keycloak.\n *\n * @public\n */\nexport const keycloakTransformerExtensionPoint =\n createExtensionPoint<KeycloakTransformerExtensionPoint>({\n id: 'keycloak.transformer',\n });\n\n/**\n * The interface for {@link keycloakTransformerExtensionPoint}.\n *\n * @public\n */\nexport type KeycloakTransformerExtensionPoint = {\n setUserTransformer(userTransformer: UserTransformer): void;\n setGroupTransformer(groupTransformer: GroupTransformer): void;\n};\n"],"names":["createExtensionPoint"],"mappings":";;;;AAwBO,MAAM,oCACXA,qCAAwD,CAAA;AAAA,EACtD,EAAI,EAAA,sBAAA;AACN,CAAC;;;;"}
1
+ {"version":3,"file":"extensions.cjs.js","sources":["../src/extensions.ts"],"sourcesContent":["/*\n * Copyright 2024 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 */\nimport { createExtensionPoint } from '@backstage/backend-plugin-api';\n\nimport type { GroupTransformer, UserTransformer } from './lib/types';\n\n/**\n * An extension point that exposes the ability to implement user and group transformer functions for keycloak.\n *\n * @public\n */\nexport const keycloakTransformerExtensionPoint =\n createExtensionPoint<KeycloakTransformerExtensionPoint>({\n id: 'keycloak.transformer',\n });\n\n/**\n * The interface for {@link keycloakTransformerExtensionPoint}.\n *\n * @public\n */\nexport type KeycloakTransformerExtensionPoint = {\n setUserTransformer(userTransformer: UserTransformer): void;\n setGroupTransformer(groupTransformer: GroupTransformer): void;\n};\n"],"names":["createExtensionPoint"],"mappings":";;;;AAwBO,MAAM,oCACXA,qCAAwD,CAAA;AAAA,EACtD,EAAI,EAAA;AACN,CAAC;;;;"}
@@ -31,7 +31,7 @@ const readProviderConfig = (id, providerConfigInstance) => {
31
31
  }
32
32
  const schedule = providerConfigInstance.has("schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
33
33
  providerConfigInstance.getConfig("schedule")
34
- ) : void 0;
34
+ ) : undefined;
35
35
  return {
36
36
  id,
37
37
  baseUrl,
@@ -1 +1 @@
1
- {"version":3,"file":"config.cjs.js","sources":["../../src/lib/config.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport type { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\n\n/**\n * The configuration parameters for a single Keycloak provider.\n *\n * @public\n */\nexport type KeycloakProviderConfig = {\n /**\n * Identifier of the provider which will be used i.e. at the location key for ingested entities.\n */\n id: string;\n\n /**\n * The Keycloak base URL\n */\n baseUrl: string;\n\n /**\n * The username to use for authenticating requests\n * If specified, password must also be specified\n */\n username?: string;\n\n /**\n * The password to use for authenticating requests\n * If specified, username must also be specified\n */\n password?: string;\n\n /**\n * The clientId to use for authenticating requests\n * If specified, clientSecret must also be specified\n */\n clientId?: string;\n\n /**\n * The clientSecret to use for authenticating requests\n * If specified, clientId must also be specified\n */\n clientSecret?: string;\n\n /**\n * name of the Keycloak realm\n */\n realm: string;\n\n /**\n * name of the Keycloak login realm\n */\n loginRealm?: string;\n\n /**\n * Schedule configuration for refresh tasks.\n */\n schedule?: SchedulerServiceTaskScheduleDefinition;\n\n /**\n * The number of users to query at a time.\n * @defaultValue 100\n * @remarks\n * This is a performance optimization to avoid querying too many users at once.\n * @see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_users_resource\n */\n userQuerySize?: number;\n\n /**\n * The number of groups to query at a time.\n * @defaultValue 100\n * @remarks\n * This is a performance optimization to avoid querying too many groups at once.\n * @see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_groups_resource\n */\n groupQuerySize?: number;\n};\n\nconst readProviderConfig = (\n id: string,\n providerConfigInstance: Config,\n): KeycloakProviderConfig => {\n const baseUrl = providerConfigInstance.getString('baseUrl');\n const realm = providerConfigInstance.getOptionalString('realm') ?? 'master';\n const loginRealm =\n providerConfigInstance.getOptionalString('loginRealm') ?? 'master';\n const username = providerConfigInstance.getOptionalString('username');\n const password = providerConfigInstance.getOptionalString('password');\n const clientId = providerConfigInstance.getOptionalString('clientId');\n const clientSecret = providerConfigInstance.getOptionalString('clientSecret');\n const userQuerySize =\n providerConfigInstance.getOptionalNumber('userQuerySize');\n const groupQuerySize =\n providerConfigInstance.getOptionalNumber('groupQuerySize');\n\n if (clientId && !clientSecret) {\n throw new InputError(\n `clientSecret must be provided when clientId is defined.`,\n );\n }\n\n if (clientSecret && !clientId) {\n throw new InputError(\n `clientId must be provided when clientSecret is defined.`,\n );\n }\n\n if (username && !password) {\n throw new InputError(`password must be provided when username is defined.`);\n }\n\n if (password && !username) {\n throw new InputError(`username must be provided when password is defined.`);\n }\n\n const schedule = providerConfigInstance.has('schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n providerConfigInstance.getConfig('schedule'),\n )\n : undefined;\n\n return {\n id,\n baseUrl,\n loginRealm,\n realm,\n username,\n password,\n clientId,\n clientSecret,\n schedule,\n userQuerySize,\n groupQuerySize,\n };\n};\n\nexport const readProviderConfigs = (\n config: Config,\n): KeycloakProviderConfig[] => {\n const providersConfig = config.getOptionalConfig(\n 'catalog.providers.keycloakOrg',\n );\n if (!providersConfig) {\n return [];\n }\n return providersConfig.keys().map(id => {\n const providerConfigInstance = providersConfig.getConfig(id);\n return readProviderConfig(id, providerConfigInstance);\n });\n};\n"],"names":["InputError","readSchedulerServiceTaskScheduleDefinitionFromConfig"],"mappings":";;;;;AA+FA,MAAM,kBAAA,GAAqB,CACzB,EAAA,EACA,sBAC2B,KAAA;AAC3B,EAAM,MAAA,OAAA,GAAU,sBAAuB,CAAA,SAAA,CAAU,SAAS,CAAA,CAAA;AAC1D,EAAA,MAAM,KAAQ,GAAA,sBAAA,CAAuB,iBAAkB,CAAA,OAAO,CAAK,IAAA,QAAA,CAAA;AACnE,EAAA,MAAM,UACJ,GAAA,sBAAA,CAAuB,iBAAkB,CAAA,YAAY,CAAK,IAAA,QAAA,CAAA;AAC5D,EAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,iBAAA,CAAkB,UAAU,CAAA,CAAA;AACpE,EAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,iBAAA,CAAkB,UAAU,CAAA,CAAA;AACpE,EAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,iBAAA,CAAkB,UAAU,CAAA,CAAA;AACpE,EAAM,MAAA,YAAA,GAAe,sBAAuB,CAAA,iBAAA,CAAkB,cAAc,CAAA,CAAA;AAC5E,EAAM,MAAA,aAAA,GACJ,sBAAuB,CAAA,iBAAA,CAAkB,eAAe,CAAA,CAAA;AAC1D,EAAM,MAAA,cAAA,GACJ,sBAAuB,CAAA,iBAAA,CAAkB,gBAAgB,CAAA,CAAA;AAE3D,EAAI,IAAA,QAAA,IAAY,CAAC,YAAc,EAAA;AAC7B,IAAA,MAAM,IAAIA,iBAAA;AAAA,MACR,CAAA,uDAAA,CAAA;AAAA,KACF,CAAA;AAAA,GACF;AAEA,EAAI,IAAA,YAAA,IAAgB,CAAC,QAAU,EAAA;AAC7B,IAAA,MAAM,IAAIA,iBAAA;AAAA,MACR,CAAA,uDAAA,CAAA;AAAA,KACF,CAAA;AAAA,GACF;AAEA,EAAI,IAAA,QAAA,IAAY,CAAC,QAAU,EAAA;AACzB,IAAM,MAAA,IAAIA,kBAAW,CAAqD,mDAAA,CAAA,CAAA,CAAA;AAAA,GAC5E;AAEA,EAAI,IAAA,QAAA,IAAY,CAAC,QAAU,EAAA;AACzB,IAAM,MAAA,IAAIA,kBAAW,CAAqD,mDAAA,CAAA,CAAA,CAAA;AAAA,GAC5E;AAEA,EAAA,MAAM,QAAW,GAAA,sBAAA,CAAuB,GAAI,CAAA,UAAU,CAClD,GAAAC,qEAAA;AAAA,IACE,sBAAA,CAAuB,UAAU,UAAU,CAAA;AAAA,GAE7C,GAAA,KAAA,CAAA,CAAA;AAEJ,EAAO,OAAA;AAAA,IACL,EAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA,KAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,YAAA;AAAA,IACA,QAAA;AAAA,IACA,aAAA;AAAA,IACA,cAAA;AAAA,GACF,CAAA;AACF,CAAA,CAAA;AAEa,MAAA,mBAAA,GAAsB,CACjC,MAC6B,KAAA;AAC7B,EAAA,MAAM,kBAAkB,MAAO,CAAA,iBAAA;AAAA,IAC7B,+BAAA;AAAA,GACF,CAAA;AACA,EAAA,IAAI,CAAC,eAAiB,EAAA;AACpB,IAAA,OAAO,EAAC,CAAA;AAAA,GACV;AACA,EAAA,OAAO,eAAgB,CAAA,IAAA,EAAO,CAAA,GAAA,CAAI,CAAM,EAAA,KAAA;AACtC,IAAM,MAAA,sBAAA,GAAyB,eAAgB,CAAA,SAAA,CAAU,EAAE,CAAA,CAAA;AAC3D,IAAO,OAAA,kBAAA,CAAmB,IAAI,sBAAsB,CAAA,CAAA;AAAA,GACrD,CAAA,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"config.cjs.js","sources":["../../src/lib/config.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api';\nimport type { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api';\nimport type { Config } from '@backstage/config';\nimport { InputError } from '@backstage/errors';\n\n/**\n * The configuration parameters for a single Keycloak provider.\n *\n * @public\n */\nexport type KeycloakProviderConfig = {\n /**\n * Identifier of the provider which will be used i.e. at the location key for ingested entities.\n */\n id: string;\n\n /**\n * The Keycloak base URL\n */\n baseUrl: string;\n\n /**\n * The username to use for authenticating requests\n * If specified, password must also be specified\n */\n username?: string;\n\n /**\n * The password to use for authenticating requests\n * If specified, username must also be specified\n */\n password?: string;\n\n /**\n * The clientId to use for authenticating requests\n * If specified, clientSecret must also be specified\n */\n clientId?: string;\n\n /**\n * The clientSecret to use for authenticating requests\n * If specified, clientId must also be specified\n */\n clientSecret?: string;\n\n /**\n * name of the Keycloak realm\n */\n realm: string;\n\n /**\n * name of the Keycloak login realm\n */\n loginRealm?: string;\n\n /**\n * Schedule configuration for refresh tasks.\n */\n schedule?: SchedulerServiceTaskScheduleDefinition;\n\n /**\n * The number of users to query at a time.\n * @defaultValue 100\n * @remarks\n * This is a performance optimization to avoid querying too many users at once.\n * @see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_users_resource\n */\n userQuerySize?: number;\n\n /**\n * The number of groups to query at a time.\n * @defaultValue 100\n * @remarks\n * This is a performance optimization to avoid querying too many groups at once.\n * @see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_groups_resource\n */\n groupQuerySize?: number;\n};\n\nconst readProviderConfig = (\n id: string,\n providerConfigInstance: Config,\n): KeycloakProviderConfig => {\n const baseUrl = providerConfigInstance.getString('baseUrl');\n const realm = providerConfigInstance.getOptionalString('realm') ?? 'master';\n const loginRealm =\n providerConfigInstance.getOptionalString('loginRealm') ?? 'master';\n const username = providerConfigInstance.getOptionalString('username');\n const password = providerConfigInstance.getOptionalString('password');\n const clientId = providerConfigInstance.getOptionalString('clientId');\n const clientSecret = providerConfigInstance.getOptionalString('clientSecret');\n const userQuerySize =\n providerConfigInstance.getOptionalNumber('userQuerySize');\n const groupQuerySize =\n providerConfigInstance.getOptionalNumber('groupQuerySize');\n\n if (clientId && !clientSecret) {\n throw new InputError(\n `clientSecret must be provided when clientId is defined.`,\n );\n }\n\n if (clientSecret && !clientId) {\n throw new InputError(\n `clientId must be provided when clientSecret is defined.`,\n );\n }\n\n if (username && !password) {\n throw new InputError(`password must be provided when username is defined.`);\n }\n\n if (password && !username) {\n throw new InputError(`username must be provided when password is defined.`);\n }\n\n const schedule = providerConfigInstance.has('schedule')\n ? readSchedulerServiceTaskScheduleDefinitionFromConfig(\n providerConfigInstance.getConfig('schedule'),\n )\n : undefined;\n\n return {\n id,\n baseUrl,\n loginRealm,\n realm,\n username,\n password,\n clientId,\n clientSecret,\n schedule,\n userQuerySize,\n groupQuerySize,\n };\n};\n\nexport const readProviderConfigs = (\n config: Config,\n): KeycloakProviderConfig[] => {\n const providersConfig = config.getOptionalConfig(\n 'catalog.providers.keycloakOrg',\n );\n if (!providersConfig) {\n return [];\n }\n return providersConfig.keys().map(id => {\n const providerConfigInstance = providersConfig.getConfig(id);\n return readProviderConfig(id, providerConfigInstance);\n });\n};\n"],"names":["InputError","readSchedulerServiceTaskScheduleDefinitionFromConfig"],"mappings":";;;;;AA+FA,MAAM,kBAAA,GAAqB,CACzB,EAAA,EACA,sBAC2B,KAAA;AAC3B,EAAM,MAAA,OAAA,GAAU,sBAAuB,CAAA,SAAA,CAAU,SAAS,CAAA;AAC1D,EAAA,MAAM,KAAQ,GAAA,sBAAA,CAAuB,iBAAkB,CAAA,OAAO,CAAK,IAAA,QAAA;AACnE,EAAA,MAAM,UACJ,GAAA,sBAAA,CAAuB,iBAAkB,CAAA,YAAY,CAAK,IAAA,QAAA;AAC5D,EAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,iBAAA,CAAkB,UAAU,CAAA;AACpE,EAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,iBAAA,CAAkB,UAAU,CAAA;AACpE,EAAM,MAAA,QAAA,GAAW,sBAAuB,CAAA,iBAAA,CAAkB,UAAU,CAAA;AACpE,EAAM,MAAA,YAAA,GAAe,sBAAuB,CAAA,iBAAA,CAAkB,cAAc,CAAA;AAC5E,EAAM,MAAA,aAAA,GACJ,sBAAuB,CAAA,iBAAA,CAAkB,eAAe,CAAA;AAC1D,EAAM,MAAA,cAAA,GACJ,sBAAuB,CAAA,iBAAA,CAAkB,gBAAgB,CAAA;AAE3D,EAAI,IAAA,QAAA,IAAY,CAAC,YAAc,EAAA;AAC7B,IAAA,MAAM,IAAIA,iBAAA;AAAA,MACR,CAAA,uDAAA;AAAA,KACF;AAAA;AAGF,EAAI,IAAA,YAAA,IAAgB,CAAC,QAAU,EAAA;AAC7B,IAAA,MAAM,IAAIA,iBAAA;AAAA,MACR,CAAA,uDAAA;AAAA,KACF;AAAA;AAGF,EAAI,IAAA,QAAA,IAAY,CAAC,QAAU,EAAA;AACzB,IAAM,MAAA,IAAIA,kBAAW,CAAqD,mDAAA,CAAA,CAAA;AAAA;AAG5E,EAAI,IAAA,QAAA,IAAY,CAAC,QAAU,EAAA;AACzB,IAAM,MAAA,IAAIA,kBAAW,CAAqD,mDAAA,CAAA,CAAA;AAAA;AAG5E,EAAA,MAAM,QAAW,GAAA,sBAAA,CAAuB,GAAI,CAAA,UAAU,CAClD,GAAAC,qEAAA;AAAA,IACE,sBAAA,CAAuB,UAAU,UAAU;AAAA,GAE7C,GAAA,SAAA;AAEJ,EAAO,OAAA;AAAA,IACL,EAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA,KAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,QAAA;AAAA,IACA,YAAA;AAAA,IACA,QAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF;AACF,CAAA;AAEa,MAAA,mBAAA,GAAsB,CACjC,MAC6B,KAAA;AAC7B,EAAA,MAAM,kBAAkB,MAAO,CAAA,iBAAA;AAAA,IAC7B;AAAA,GACF;AACA,EAAA,IAAI,CAAC,eAAiB,EAAA;AACpB,IAAA,OAAO,EAAC;AAAA;AAEV,EAAA,OAAO,eAAgB,CAAA,IAAA,EAAO,CAAA,GAAA,CAAI,CAAM,EAAA,KAAA;AACtC,IAAM,MAAA,sBAAA,GAAyB,eAAgB,CAAA,SAAA,CAAU,EAAE,CAAA;AAC3D,IAAO,OAAA,kBAAA,CAAmB,IAAI,sBAAsB,CAAA;AAAA,GACrD,CAAA;AACH;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"constants.cjs.js","sources":["../../src/lib/constants.ts"],"sourcesContent":["/*\n * Copyright 2024 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\nexport const KEYCLOAK_HOST_ANNOTATION = 'keycloak.org/host';\nexport const KEYCLOAK_ID_ANNOTATION = 'keycloak.org/id';\nexport const KEYCLOAK_REALM_ANNOTATION = 'keycloak.org/realm';\nexport const KEYCLOAK_ENTITY_QUERY_SIZE = 100;\n"],"names":[],"mappings":";;AAiBO,MAAM,sBAAyB,GAAA,kBAAA;AAC/B,MAAM,yBAA4B,GAAA,qBAAA;AAClC,MAAM,0BAA6B,GAAA;;;;;;"}
1
+ {"version":3,"file":"constants.cjs.js","sources":["../../src/lib/constants.ts"],"sourcesContent":["/*\n * Copyright 2024 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\nexport const KEYCLOAK_HOST_ANNOTATION = 'keycloak.org/host';\nexport const KEYCLOAK_ID_ANNOTATION = 'keycloak.org/id';\nexport const KEYCLOAK_REALM_ANNOTATION = 'keycloak.org/realm';\nexport const KEYCLOAK_ENTITY_QUERY_SIZE = 100;\n"],"names":[],"mappings":";;AAiBO,MAAM,sBAAyB,GAAA;AAC/B,MAAM,yBAA4B,GAAA;AAClC,MAAM,0BAA6B,GAAA;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"read.cjs.js","sources":["../../src/lib/read.ts"],"sourcesContent":["/*\n * Copyright 2024 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 type { LoggerService } from '@backstage/backend-plugin-api';\nimport type { GroupEntity, UserEntity } from '@backstage/catalog-model';\n\nimport type KeycloakAdminClient from '@keycloak/keycloak-admin-client';\nimport type GroupRepresentation from '@keycloak/keycloak-admin-client/lib/defs/groupRepresentation';\nimport type UserRepresentation from '@keycloak/keycloak-admin-client/lib/defs/userRepresentation';\nimport type { Groups } from '@keycloak/keycloak-admin-client/lib/resources/groups';\nimport type { Users } from '@keycloak/keycloak-admin-client/lib/resources/users';\n\nimport { KeycloakProviderConfig } from './config';\nimport {\n KEYCLOAK_ENTITY_QUERY_SIZE,\n KEYCLOAK_ID_ANNOTATION,\n KEYCLOAK_REALM_ANNOTATION,\n} from './constants';\nimport { noopGroupTransformer, noopUserTransformer } from './transformers';\nimport {\n GroupRepresentationWithParent,\n GroupRepresentationWithParentAndEntity,\n GroupTransformer,\n UserRepresentationWithEntity,\n UserTransformer,\n} from './types';\n\nexport const parseGroup = async (\n keycloakGroup: GroupRepresentationWithParent,\n realm: string,\n groupTransformer?: GroupTransformer,\n): Promise<GroupEntity | undefined> => {\n const transformer = groupTransformer ?? noopGroupTransformer;\n const entity: GroupEntity = {\n apiVersion: 'backstage.io/v1beta1',\n kind: 'Group',\n metadata: {\n name: keycloakGroup.name!,\n annotations: {\n [KEYCLOAK_ID_ANNOTATION]: keycloakGroup.id!,\n [KEYCLOAK_REALM_ANNOTATION]: realm,\n },\n },\n spec: {\n type: 'group',\n profile: {\n displayName: keycloakGroup.name!,\n },\n // children, parent and members are updated again after all group and user transformers applied.\n children: keycloakGroup.subGroups?.map(g => g.name!) ?? [],\n parent: keycloakGroup.parent,\n members: keycloakGroup.members,\n },\n };\n\n return await transformer(entity, keycloakGroup, realm);\n};\n\nexport const parseUser = async (\n user: UserRepresentation,\n realm: string,\n keycloakGroups: GroupRepresentationWithParentAndEntity[],\n\n userTransformer?: UserTransformer,\n): Promise<UserEntity | undefined> => {\n const transformer = userTransformer ?? noopUserTransformer;\n const entity: UserEntity = {\n apiVersion: 'backstage.io/v1beta1',\n kind: 'User',\n metadata: {\n name: user.username!,\n annotations: {\n [KEYCLOAK_ID_ANNOTATION]: user.id!,\n [KEYCLOAK_REALM_ANNOTATION]: realm,\n },\n },\n spec: {\n profile: {\n email: user.email,\n ...(user.firstName || user.lastName\n ? {\n displayName: [user.firstName, user.lastName]\n .filter(Boolean)\n .join(' '),\n }\n : {}),\n },\n memberOf: keycloakGroups\n .filter(g => g.members?.includes(user.username!))\n .map(g => g.entity.metadata.name),\n },\n };\n\n return await transformer(entity, user, realm, keycloakGroups);\n};\n\nexport async function getEntities<T extends Users | Groups>(\n entities: T,\n config: KeycloakProviderConfig,\n logger: LoggerService,\n entityQuerySize: number = KEYCLOAK_ENTITY_QUERY_SIZE,\n): Promise<Awaited<ReturnType<T['find']>>> {\n const rawEntityCount = await entities.count({ realm: config.realm });\n const entityCount =\n typeof rawEntityCount === 'number' ? rawEntityCount : rawEntityCount.count;\n\n const pageCount = Math.ceil(entityCount / entityQuerySize);\n\n // The next line acts like range in python\n const entityPromises = Array.from(\n { length: pageCount },\n (_, i) =>\n entities\n .find({\n realm: config.realm,\n max: entityQuerySize,\n first: i * entityQuerySize,\n })\n .catch(err =>\n logger.warn('Failed to retieve Keycloak entities.', err),\n ) as ReturnType<T['find']>,\n );\n\n const entityResults = (await Promise.all(entityPromises)).flat() as Awaited<\n ReturnType<T['find']>\n >;\n\n return entityResults;\n}\n\nasync function getAllGroupMembers<T extends Groups>(\n groups: T,\n groupId: string,\n config: KeycloakProviderConfig,\n options?: { userQuerySize?: number },\n): Promise<string[]> {\n const querySize = options?.userQuerySize || 100;\n\n let allMembers: string[] = [];\n let page = 0;\n let totalMembers = 0;\n\n do {\n const members = await groups.listMembers({\n id: groupId,\n max: querySize,\n realm: config.realm,\n first: page * querySize,\n });\n\n if (members.length > 0) {\n allMembers = allMembers.concat(members.map(m => m.username!));\n totalMembers = members.length; // Get the number of members retrieved\n } else {\n totalMembers = 0; // No members retrieved\n }\n\n page++;\n } while (totalMembers > 0);\n\n return allMembers;\n}\n\nexport async function processGroupsRecursively(\n topLevelGroups: GroupRepresentationWithParent[],\n entities: Groups,\n realm: string,\n) {\n const allGroups: GroupRepresentationWithParent[] = [];\n for (const group of topLevelGroups) {\n allGroups.push(group);\n\n if (group.subGroupCount! > 0) {\n const subgroups = await entities.listSubGroups({\n parentId: group.id!,\n first: 0,\n max: group.subGroupCount,\n briefRepresentation: true,\n realm,\n });\n const subGroupResults = await processGroupsRecursively(\n subgroups,\n entities,\n realm,\n );\n allGroups.push(...subGroupResults);\n }\n }\n\n return allGroups;\n}\n\nexport function* traverseGroups(\n group: GroupRepresentation,\n): IterableIterator<GroupRepresentationWithParent> {\n yield group;\n for (const g of group.subGroups ?? []) {\n (g as GroupRepresentationWithParent).parent = group.name!;\n yield* traverseGroups(g);\n }\n}\n\nexport const readKeycloakRealm = async (\n client: KeycloakAdminClient,\n config: KeycloakProviderConfig,\n logger: LoggerService,\n options?: {\n userQuerySize?: number;\n groupQuerySize?: number;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n },\n): Promise<{\n users: UserEntity[];\n groups: GroupEntity[];\n}> => {\n const kUsers = await getEntities(\n client.users,\n config,\n logger,\n options?.userQuerySize,\n );\n\n const topLevelKGroups = (await getEntities(\n client.groups,\n config,\n logger,\n options?.groupQuerySize,\n )) as GroupRepresentationWithParent[];\n\n let serverVersion: number;\n\n try {\n const serverInfo = await client.serverInfo.find();\n serverVersion = parseInt(\n serverInfo.systemInfo?.version?.slice(0, 2) || '',\n 10,\n );\n } catch (error) {\n throw new Error(`Failed to retrieve Keycloak server information: ${error}`);\n }\n\n const isVersion23orHigher = serverVersion >= 23;\n\n let rawKGroups: GroupRepresentationWithParent[] = [];\n\n if (isVersion23orHigher) {\n rawKGroups = await processGroupsRecursively(\n topLevelKGroups,\n client.groups as Groups,\n config.realm,\n );\n } else {\n rawKGroups = topLevelKGroups.reduce(\n (acc, g) => acc.concat(...traverseGroups(g)),\n [] as GroupRepresentationWithParent[],\n );\n }\n const kGroups = await Promise.all(\n rawKGroups.map(async g => {\n g.members = await getAllGroupMembers(\n client.groups as Groups,\n g.id!,\n config,\n options,\n );\n\n if (isVersion23orHigher) {\n if (g.subGroupCount! > 0) {\n g.subGroups = await client.groups.listSubGroups({\n parentId: g.id!,\n first: 0,\n max: g.subGroupCount,\n briefRepresentation: false,\n realm: config.realm,\n });\n }\n if (g.parentId) {\n const groupParent = await client.groups.findOne({\n id: g.parentId,\n realm: config.realm,\n });\n g.parent = groupParent?.name;\n }\n }\n\n return g;\n }),\n );\n\n const parsedGroups = await kGroups.reduce(async (promise, g) => {\n const partial = await promise;\n const entity = await parseGroup(g, config.realm, options?.groupTransformer);\n if (entity) {\n const group = {\n ...g,\n entity,\n } as GroupRepresentationWithParentAndEntity;\n partial.push(group);\n }\n return partial;\n }, Promise.resolve([] as GroupRepresentationWithParentAndEntity[]));\n\n const parsedUsers = await kUsers.reduce(async (promise, u) => {\n const partial = await promise;\n const entity = await parseUser(\n u,\n config.realm,\n parsedGroups,\n options?.userTransformer,\n );\n if (entity) {\n const user = { ...u, entity } as UserRepresentationWithEntity;\n partial.push(user);\n }\n return partial;\n }, Promise.resolve([] as UserRepresentationWithEntity[]));\n\n const groups = parsedGroups.map(g => {\n const entity = g.entity;\n entity.spec.members =\n g.entity.spec.members?.flatMap(m => {\n const name = parsedUsers.find(p => p.username === m)?.entity.metadata\n .name;\n return name ? [name] : [];\n }) ?? [];\n entity.spec.children =\n g.entity.spec.children?.flatMap(c => {\n const child = parsedGroups.find(p => p.name === c)?.entity.metadata\n .name;\n return child ? [child] : [];\n }) ?? [];\n entity.spec.parent = parsedGroups.find(\n p => p.name === entity.spec.parent,\n )?.entity.metadata.name;\n return entity;\n });\n\n return { users: parsedUsers.map(u => u.entity), groups };\n};\n"],"names":["noopGroupTransformer","KEYCLOAK_ID_ANNOTATION","KEYCLOAK_REALM_ANNOTATION","noopUserTransformer","KEYCLOAK_ENTITY_QUERY_SIZE"],"mappings":"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"}
1
+ {"version":3,"file":"read.cjs.js","sources":["../../src/lib/read.ts"],"sourcesContent":["/*\n * Copyright 2024 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 type { LoggerService } from '@backstage/backend-plugin-api';\nimport type { GroupEntity, UserEntity } from '@backstage/catalog-model';\n\nimport type KeycloakAdminClient from '@keycloak/keycloak-admin-client';\nimport type GroupRepresentation from '@keycloak/keycloak-admin-client/lib/defs/groupRepresentation';\nimport type UserRepresentation from '@keycloak/keycloak-admin-client/lib/defs/userRepresentation';\nimport type { Groups } from '@keycloak/keycloak-admin-client/lib/resources/groups';\nimport type { Users } from '@keycloak/keycloak-admin-client/lib/resources/users';\n\nimport { KeycloakProviderConfig } from './config';\nimport {\n KEYCLOAK_ENTITY_QUERY_SIZE,\n KEYCLOAK_ID_ANNOTATION,\n KEYCLOAK_REALM_ANNOTATION,\n} from './constants';\nimport { noopGroupTransformer, noopUserTransformer } from './transformers';\nimport {\n GroupRepresentationWithParent,\n GroupRepresentationWithParentAndEntity,\n GroupTransformer,\n UserRepresentationWithEntity,\n UserTransformer,\n} from './types';\n\nexport const parseGroup = async (\n keycloakGroup: GroupRepresentationWithParent,\n realm: string,\n groupTransformer?: GroupTransformer,\n): Promise<GroupEntity | undefined> => {\n const transformer = groupTransformer ?? noopGroupTransformer;\n const entity: GroupEntity = {\n apiVersion: 'backstage.io/v1beta1',\n kind: 'Group',\n metadata: {\n name: keycloakGroup.name!,\n annotations: {\n [KEYCLOAK_ID_ANNOTATION]: keycloakGroup.id!,\n [KEYCLOAK_REALM_ANNOTATION]: realm,\n },\n },\n spec: {\n type: 'group',\n profile: {\n displayName: keycloakGroup.name!,\n },\n // children, parent and members are updated again after all group and user transformers applied.\n children: keycloakGroup.subGroups?.map(g => g.name!) ?? [],\n parent: keycloakGroup.parent,\n members: keycloakGroup.members,\n },\n };\n\n return await transformer(entity, keycloakGroup, realm);\n};\n\nexport const parseUser = async (\n user: UserRepresentation,\n realm: string,\n keycloakGroups: GroupRepresentationWithParentAndEntity[],\n\n userTransformer?: UserTransformer,\n): Promise<UserEntity | undefined> => {\n const transformer = userTransformer ?? noopUserTransformer;\n const entity: UserEntity = {\n apiVersion: 'backstage.io/v1beta1',\n kind: 'User',\n metadata: {\n name: user.username!,\n annotations: {\n [KEYCLOAK_ID_ANNOTATION]: user.id!,\n [KEYCLOAK_REALM_ANNOTATION]: realm,\n },\n },\n spec: {\n profile: {\n email: user.email,\n ...(user.firstName || user.lastName\n ? {\n displayName: [user.firstName, user.lastName]\n .filter(Boolean)\n .join(' '),\n }\n : {}),\n },\n memberOf: keycloakGroups\n .filter(g => g.members?.includes(user.username!))\n .map(g => g.entity.metadata.name),\n },\n };\n\n return await transformer(entity, user, realm, keycloakGroups);\n};\n\nexport async function getEntities<T extends Users | Groups>(\n entities: T,\n config: KeycloakProviderConfig,\n logger: LoggerService,\n entityQuerySize: number = KEYCLOAK_ENTITY_QUERY_SIZE,\n): Promise<Awaited<ReturnType<T['find']>>> {\n const rawEntityCount = await entities.count({ realm: config.realm });\n const entityCount =\n typeof rawEntityCount === 'number' ? rawEntityCount : rawEntityCount.count;\n\n const pageCount = Math.ceil(entityCount / entityQuerySize);\n\n // The next line acts like range in python\n const entityPromises = Array.from(\n { length: pageCount },\n (_, i) =>\n entities\n .find({\n realm: config.realm,\n max: entityQuerySize,\n first: i * entityQuerySize,\n })\n .catch(err =>\n logger.warn('Failed to retieve Keycloak entities.', err),\n ) as ReturnType<T['find']>,\n );\n\n const entityResults = (await Promise.all(entityPromises)).flat() as Awaited<\n ReturnType<T['find']>\n >;\n\n return entityResults;\n}\n\nasync function getAllGroupMembers<T extends Groups>(\n groups: T,\n groupId: string,\n config: KeycloakProviderConfig,\n options?: { userQuerySize?: number },\n): Promise<string[]> {\n const querySize = options?.userQuerySize || 100;\n\n let allMembers: string[] = [];\n let page = 0;\n let totalMembers = 0;\n\n do {\n const members = await groups.listMembers({\n id: groupId,\n max: querySize,\n realm: config.realm,\n first: page * querySize,\n });\n\n if (members.length > 0) {\n allMembers = allMembers.concat(members.map(m => m.username!));\n totalMembers = members.length; // Get the number of members retrieved\n } else {\n totalMembers = 0; // No members retrieved\n }\n\n page++;\n } while (totalMembers > 0);\n\n return allMembers;\n}\n\nexport async function processGroupsRecursively(\n topLevelGroups: GroupRepresentationWithParent[],\n entities: Groups,\n realm: string,\n) {\n const allGroups: GroupRepresentationWithParent[] = [];\n for (const group of topLevelGroups) {\n allGroups.push(group);\n\n if (group.subGroupCount! > 0) {\n const subgroups = await entities.listSubGroups({\n parentId: group.id!,\n first: 0,\n max: group.subGroupCount,\n briefRepresentation: true,\n realm,\n });\n const subGroupResults = await processGroupsRecursively(\n subgroups,\n entities,\n realm,\n );\n allGroups.push(...subGroupResults);\n }\n }\n\n return allGroups;\n}\n\nexport function* traverseGroups(\n group: GroupRepresentation,\n): IterableIterator<GroupRepresentationWithParent> {\n yield group;\n for (const g of group.subGroups ?? []) {\n (g as GroupRepresentationWithParent).parent = group.name!;\n yield* traverseGroups(g);\n }\n}\n\nexport const readKeycloakRealm = async (\n client: KeycloakAdminClient,\n config: KeycloakProviderConfig,\n logger: LoggerService,\n options?: {\n userQuerySize?: number;\n groupQuerySize?: number;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n },\n): Promise<{\n users: UserEntity[];\n groups: GroupEntity[];\n}> => {\n const kUsers = await getEntities(\n client.users,\n config,\n logger,\n options?.userQuerySize,\n );\n\n const topLevelKGroups = (await getEntities(\n client.groups,\n config,\n logger,\n options?.groupQuerySize,\n )) as GroupRepresentationWithParent[];\n\n let serverVersion: number;\n\n try {\n const serverInfo = await client.serverInfo.find();\n serverVersion = parseInt(\n serverInfo.systemInfo?.version?.slice(0, 2) || '',\n 10,\n );\n } catch (error) {\n throw new Error(`Failed to retrieve Keycloak server information: ${error}`);\n }\n\n const isVersion23orHigher = serverVersion >= 23;\n\n let rawKGroups: GroupRepresentationWithParent[] = [];\n\n if (isVersion23orHigher) {\n rawKGroups = await processGroupsRecursively(\n topLevelKGroups,\n client.groups as Groups,\n config.realm,\n );\n } else {\n rawKGroups = topLevelKGroups.reduce(\n (acc, g) => acc.concat(...traverseGroups(g)),\n [] as GroupRepresentationWithParent[],\n );\n }\n const kGroups = await Promise.all(\n rawKGroups.map(async g => {\n g.members = await getAllGroupMembers(\n client.groups as Groups,\n g.id!,\n config,\n options,\n );\n\n if (isVersion23orHigher) {\n if (g.subGroupCount! > 0) {\n g.subGroups = await client.groups.listSubGroups({\n parentId: g.id!,\n first: 0,\n max: g.subGroupCount,\n briefRepresentation: false,\n realm: config.realm,\n });\n }\n if (g.parentId) {\n const groupParent = await client.groups.findOne({\n id: g.parentId,\n realm: config.realm,\n });\n g.parent = groupParent?.name;\n }\n }\n\n return g;\n }),\n );\n\n const parsedGroups = await kGroups.reduce(async (promise, g) => {\n const partial = await promise;\n const entity = await parseGroup(g, config.realm, options?.groupTransformer);\n if (entity) {\n const group = {\n ...g,\n entity,\n } as GroupRepresentationWithParentAndEntity;\n partial.push(group);\n }\n return partial;\n }, Promise.resolve([] as GroupRepresentationWithParentAndEntity[]));\n\n const parsedUsers = await kUsers.reduce(async (promise, u) => {\n const partial = await promise;\n const entity = await parseUser(\n u,\n config.realm,\n parsedGroups,\n options?.userTransformer,\n );\n if (entity) {\n const user = { ...u, entity } as UserRepresentationWithEntity;\n partial.push(user);\n }\n return partial;\n }, Promise.resolve([] as UserRepresentationWithEntity[]));\n\n const groups = parsedGroups.map(g => {\n const entity = g.entity;\n entity.spec.members =\n g.entity.spec.members?.flatMap(m => {\n const name = parsedUsers.find(p => p.username === m)?.entity.metadata\n .name;\n return name ? [name] : [];\n }) ?? [];\n entity.spec.children =\n g.entity.spec.children?.flatMap(c => {\n const child = parsedGroups.find(p => p.name === c)?.entity.metadata\n .name;\n return child ? [child] : [];\n }) ?? [];\n entity.spec.parent = parsedGroups.find(\n p => p.name === entity.spec.parent,\n )?.entity.metadata.name;\n return entity;\n });\n\n return { users: parsedUsers.map(u => u.entity), groups };\n};\n"],"names":["noopGroupTransformer","KEYCLOAK_ID_ANNOTATION","KEYCLOAK_REALM_ANNOTATION","noopUserTransformer","KEYCLOAK_ENTITY_QUERY_SIZE"],"mappings":";;;;;AAwCO,MAAM,UAAa,GAAA,OACxB,aACA,EAAA,KAAA,EACA,gBACqC,KAAA;AACrC,EAAA,MAAM,cAAc,gBAAoB,IAAAA,iCAAA;AACxC,EAAA,MAAM,MAAsB,GAAA;AAAA,IAC1B,UAAY,EAAA,sBAAA;AAAA,IACZ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,MACR,MAAM,aAAc,CAAA,IAAA;AAAA,MACpB,WAAa,EAAA;AAAA,QACX,CAACC,gCAAsB,GAAG,aAAc,CAAA,EAAA;AAAA,QACxC,CAACC,mCAAyB,GAAG;AAAA;AAC/B,KACF;AAAA,IACA,IAAM,EAAA;AAAA,MACJ,IAAM,EAAA,OAAA;AAAA,MACN,OAAS,EAAA;AAAA,QACP,aAAa,aAAc,CAAA;AAAA,OAC7B;AAAA;AAAA,MAEA,QAAA,EAAU,cAAc,SAAW,EAAA,GAAA,CAAI,OAAK,CAAE,CAAA,IAAK,KAAK,EAAC;AAAA,MACzD,QAAQ,aAAc,CAAA,MAAA;AAAA,MACtB,SAAS,aAAc,CAAA;AAAA;AACzB,GACF;AAEA,EAAA,OAAO,MAAM,WAAA,CAAY,MAAQ,EAAA,aAAA,EAAe,KAAK,CAAA;AACvD;AAEO,MAAM,SAAY,GAAA,OACvB,IACA,EAAA,KAAA,EACA,gBAEA,eACoC,KAAA;AACpC,EAAA,MAAM,cAAc,eAAmB,IAAAC,gCAAA;AACvC,EAAA,MAAM,MAAqB,GAAA;AAAA,IACzB,UAAY,EAAA,sBAAA;AAAA,IACZ,IAAM,EAAA,MAAA;AAAA,IACN,QAAU,EAAA;AAAA,MACR,MAAM,IAAK,CAAA,QAAA;AAAA,MACX,WAAa,EAAA;AAAA,QACX,CAACF,gCAAsB,GAAG,IAAK,CAAA,EAAA;AAAA,QAC/B,CAACC,mCAAyB,GAAG;AAAA;AAC/B,KACF;AAAA,IACA,IAAM,EAAA;AAAA,MACJ,OAAS,EAAA;AAAA,QACP,OAAO,IAAK,CAAA,KAAA;AAAA,QACZ,GAAI,IAAA,CAAK,SAAa,IAAA,IAAA,CAAK,QACvB,GAAA;AAAA,UACE,WAAA,EAAa,CAAC,IAAA,CAAK,SAAW,EAAA,IAAA,CAAK,QAAQ,CAAA,CACxC,MAAO,CAAA,OAAO,CACd,CAAA,IAAA,CAAK,GAAG;AAAA,YAEb;AAAC,OACP;AAAA,MACA,UAAU,cACP,CAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,CAAE,SAAS,QAAS,CAAA,IAAA,CAAK,QAAS,CAAC,EAC/C,GAAI,CAAA,CAAA,CAAA,KAAK,CAAE,CAAA,MAAA,CAAO,SAAS,IAAI;AAAA;AACpC,GACF;AAEA,EAAA,OAAO,MAAM,WAAA,CAAY,MAAQ,EAAA,IAAA,EAAM,OAAO,cAAc,CAAA;AAC9D;AAEA,eAAsB,WACpB,CAAA,QAAA,EACA,MACA,EAAA,MAAA,EACA,kBAA0BE,oCACe,EAAA;AACzC,EAAM,MAAA,cAAA,GAAiB,MAAM,QAAS,CAAA,KAAA,CAAM,EAAE,KAAO,EAAA,MAAA,CAAO,OAAO,CAAA;AACnE,EAAA,MAAM,WACJ,GAAA,OAAO,cAAmB,KAAA,QAAA,GAAW,iBAAiB,cAAe,CAAA,KAAA;AAEvE,EAAA,MAAM,SAAY,GAAA,IAAA,CAAK,IAAK,CAAA,WAAA,GAAc,eAAe,CAAA;AAGzD,EAAA,MAAM,iBAAiB,KAAM,CAAA,IAAA;AAAA,IAC3B,EAAE,QAAQ,SAAU,EAAA;AAAA,IACpB,CAAC,CAAA,EAAG,CACF,KAAA,QAAA,CACG,IAAK,CAAA;AAAA,MACJ,OAAO,MAAO,CAAA,KAAA;AAAA,MACd,GAAK,EAAA,eAAA;AAAA,MACL,OAAO,CAAI,GAAA;AAAA,KACZ,CACA,CAAA,KAAA;AAAA,MAAM,CACL,GAAA,KAAA,MAAA,CAAO,IAAK,CAAA,sCAAA,EAAwC,GAAG;AAAA;AACzD,GACN;AAEA,EAAA,MAAM,iBAAiB,MAAM,OAAA,CAAQ,GAAI,CAAA,cAAc,GAAG,IAAK,EAAA;AAI/D,EAAO,OAAA,aAAA;AACT;AAEA,eAAe,kBACb,CAAA,MAAA,EACA,OACA,EAAA,MAAA,EACA,OACmB,EAAA;AACnB,EAAM,MAAA,SAAA,GAAY,SAAS,aAAiB,IAAA,GAAA;AAE5C,EAAA,IAAI,aAAuB,EAAC;AAC5B,EAAA,IAAI,IAAO,GAAA,CAAA;AACX,EAAA,IAAI,YAAe,GAAA,CAAA;AAEnB,EAAG,GAAA;AACD,IAAM,MAAA,OAAA,GAAU,MAAM,MAAA,CAAO,WAAY,CAAA;AAAA,MACvC,EAAI,EAAA,OAAA;AAAA,MACJ,GAAK,EAAA,SAAA;AAAA,MACL,OAAO,MAAO,CAAA,KAAA;AAAA,MACd,OAAO,IAAO,GAAA;AAAA,KACf,CAAA;AAED,IAAI,IAAA,OAAA,CAAQ,SAAS,CAAG,EAAA;AACtB,MAAA,UAAA,GAAa,WAAW,MAAO,CAAA,OAAA,CAAQ,IAAI,CAAK,CAAA,KAAA,CAAA,CAAE,QAAS,CAAC,CAAA;AAC5D,MAAA,YAAA,GAAe,OAAQ,CAAA,MAAA;AAAA,KAClB,MAAA;AACL,MAAe,YAAA,GAAA,CAAA;AAAA;AAGjB,IAAA,IAAA,EAAA;AAAA,WACO,YAAe,GAAA,CAAA;AAExB,EAAO,OAAA,UAAA;AACT;AAEsB,eAAA,wBAAA,CACpB,cACA,EAAA,QAAA,EACA,KACA,EAAA;AACA,EAAA,MAAM,YAA6C,EAAC;AACpD,EAAA,KAAA,MAAW,SAAS,cAAgB,EAAA;AAClC,IAAA,SAAA,CAAU,KAAK,KAAK,CAAA;AAEpB,IAAI,IAAA,KAAA,CAAM,gBAAiB,CAAG,EAAA;AAC5B,MAAM,MAAA,SAAA,GAAY,MAAM,QAAA,CAAS,aAAc,CAAA;AAAA,QAC7C,UAAU,KAAM,CAAA,EAAA;AAAA,QAChB,KAAO,EAAA,CAAA;AAAA,QACP,KAAK,KAAM,CAAA,aAAA;AAAA,QACX,mBAAqB,EAAA,IAAA;AAAA,QACrB;AAAA,OACD,CAAA;AACD,MAAA,MAAM,kBAAkB,MAAM,wBAAA;AAAA,QAC5B,SAAA;AAAA,QACA,QAAA;AAAA,QACA;AAAA,OACF;AACA,MAAU,SAAA,CAAA,IAAA,CAAK,GAAG,eAAe,CAAA;AAAA;AACnC;AAGF,EAAO,OAAA,SAAA;AACT;AAEO,UAAU,eACf,KACiD,EAAA;AACjD,EAAM,MAAA,KAAA;AACN,EAAA,KAAA,MAAW,CAAK,IAAA,KAAA,CAAM,SAAa,IAAA,EAAI,EAAA;AACrC,IAAC,CAAA,CAAoC,SAAS,KAAM,CAAA,IAAA;AACpD,IAAA,OAAO,eAAe,CAAC,CAAA;AAAA;AAE3B;AAEO,MAAM,iBAAoB,GAAA,OAC/B,MACA,EAAA,MAAA,EACA,QACA,OASI,KAAA;AACJ,EAAA,MAAM,SAAS,MAAM,WAAA;AAAA,IACnB,MAAO,CAAA,KAAA;AAAA,IACP,MAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAS,EAAA;AAAA,GACX;AAEA,EAAA,MAAM,kBAAmB,MAAM,WAAA;AAAA,IAC7B,MAAO,CAAA,MAAA;AAAA,IACP,MAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAS,EAAA;AAAA,GACX;AAEA,EAAI,IAAA,aAAA;AAEJ,EAAI,IAAA;AACF,IAAA,MAAM,UAAa,GAAA,MAAM,MAAO,CAAA,UAAA,CAAW,IAAK,EAAA;AAChD,IAAgB,aAAA,GAAA,QAAA;AAAA,MACd,WAAW,UAAY,EAAA,OAAA,EAAS,KAAM,CAAA,CAAA,EAAG,CAAC,CAAK,IAAA,EAAA;AAAA,MAC/C;AAAA,KACF;AAAA,WACO,KAAO,EAAA;AACd,IAAA,MAAM,IAAI,KAAA,CAAM,CAAmD,gDAAA,EAAA,KAAK,CAAE,CAAA,CAAA;AAAA;AAG5E,EAAA,MAAM,sBAAsB,aAAiB,IAAA,EAAA;AAE7C,EAAA,IAAI,aAA8C,EAAC;AAEnD,EAAA,IAAI,mBAAqB,EAAA;AACvB,IAAA,UAAA,GAAa,MAAM,wBAAA;AAAA,MACjB,eAAA;AAAA,MACA,MAAO,CAAA,MAAA;AAAA,MACP,MAAO,CAAA;AAAA,KACT;AAAA,GACK,MAAA;AACL,IAAA,UAAA,GAAa,eAAgB,CAAA,MAAA;AAAA,MAC3B,CAAC,KAAK,CAAM,KAAA,GAAA,CAAI,OAAO,GAAG,cAAA,CAAe,CAAC,CAAC,CAAA;AAAA,MAC3C;AAAC,KACH;AAAA;AAEF,EAAM,MAAA,OAAA,GAAU,MAAM,OAAQ,CAAA,GAAA;AAAA,IAC5B,UAAA,CAAW,GAAI,CAAA,OAAM,CAAK,KAAA;AACxB,MAAA,CAAA,CAAE,UAAU,MAAM,kBAAA;AAAA,QAChB,MAAO,CAAA,MAAA;AAAA,QACP,CAAE,CAAA,EAAA;AAAA,QACF,MAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,IAAI,mBAAqB,EAAA;AACvB,QAAI,IAAA,CAAA,CAAE,gBAAiB,CAAG,EAAA;AACxB,UAAA,CAAA,CAAE,SAAY,GAAA,MAAM,MAAO,CAAA,MAAA,CAAO,aAAc,CAAA;AAAA,YAC9C,UAAU,CAAE,CAAA,EAAA;AAAA,YACZ,KAAO,EAAA,CAAA;AAAA,YACP,KAAK,CAAE,CAAA,aAAA;AAAA,YACP,mBAAqB,EAAA,KAAA;AAAA,YACrB,OAAO,MAAO,CAAA;AAAA,WACf,CAAA;AAAA;AAEH,QAAA,IAAI,EAAE,QAAU,EAAA;AACd,UAAA,MAAM,WAAc,GAAA,MAAM,MAAO,CAAA,MAAA,CAAO,OAAQ,CAAA;AAAA,YAC9C,IAAI,CAAE,CAAA,QAAA;AAAA,YACN,OAAO,MAAO,CAAA;AAAA,WACf,CAAA;AACD,UAAA,CAAA,CAAE,SAAS,WAAa,EAAA,IAAA;AAAA;AAC1B;AAGF,MAAO,OAAA,CAAA;AAAA,KACR;AAAA,GACH;AAEA,EAAA,MAAM,eAAe,MAAM,OAAA,CAAQ,MAAO,CAAA,OAAO,SAAS,CAAM,KAAA;AAC9D,IAAA,MAAM,UAAU,MAAM,OAAA;AACtB,IAAA,MAAM,SAAS,MAAM,UAAA,CAAW,GAAG,MAAO,CAAA,KAAA,EAAO,SAAS,gBAAgB,CAAA;AAC1E,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,MAAM,KAAQ,GAAA;AAAA,QACZ,GAAG,CAAA;AAAA,QACH;AAAA,OACF;AACA,MAAA,OAAA,CAAQ,KAAK,KAAK,CAAA;AAAA;AAEpB,IAAO,OAAA,OAAA;AAAA,GACN,EAAA,OAAA,CAAQ,OAAQ,CAAA,EAA8C,CAAC,CAAA;AAElE,EAAA,MAAM,cAAc,MAAM,MAAA,CAAO,MAAO,CAAA,OAAO,SAAS,CAAM,KAAA;AAC5D,IAAA,MAAM,UAAU,MAAM,OAAA;AACtB,IAAA,MAAM,SAAS,MAAM,SAAA;AAAA,MACnB,CAAA;AAAA,MACA,MAAO,CAAA,KAAA;AAAA,MACP,YAAA;AAAA,MACA,OAAS,EAAA;AAAA,KACX;AACA,IAAA,IAAI,MAAQ,EAAA;AACV,MAAA,MAAM,IAAO,GAAA,EAAE,GAAG,CAAA,EAAG,MAAO,EAAA;AAC5B,MAAA,OAAA,CAAQ,KAAK,IAAI,CAAA;AAAA;AAEnB,IAAO,OAAA,OAAA;AAAA,GACN,EAAA,OAAA,CAAQ,OAAQ,CAAA,EAAoC,CAAC,CAAA;AAExD,EAAM,MAAA,MAAA,GAAS,YAAa,CAAA,GAAA,CAAI,CAAK,CAAA,KAAA;AACnC,IAAA,MAAM,SAAS,CAAE,CAAA,MAAA;AACjB,IAAA,MAAA,CAAO,KAAK,OACV,GAAA,CAAA,CAAE,OAAO,IAAK,CAAA,OAAA,EAAS,QAAQ,CAAK,CAAA,KAAA;AAClC,MAAM,MAAA,IAAA,GAAO,YAAY,IAAK,CAAA,CAAA,CAAA,KAAK,EAAE,QAAa,KAAA,CAAC,CAAG,EAAA,MAAA,CAAO,QAC1D,CAAA,IAAA;AACH,MAAA,OAAO,IAAO,GAAA,CAAC,IAAI,CAAA,GAAI,EAAC;AAAA,KACzB,KAAK,EAAC;AACT,IAAA,MAAA,CAAO,KAAK,QACV,GAAA,CAAA,CAAE,OAAO,IAAK,CAAA,QAAA,EAAU,QAAQ,CAAK,CAAA,KAAA;AACnC,MAAM,MAAA,KAAA,GAAQ,aAAa,IAAK,CAAA,CAAA,CAAA,KAAK,EAAE,IAAS,KAAA,CAAC,CAAG,EAAA,MAAA,CAAO,QACxD,CAAA,IAAA;AACH,MAAA,OAAO,KAAQ,GAAA,CAAC,KAAK,CAAA,GAAI,EAAC;AAAA,KAC3B,KAAK,EAAC;AACT,IAAO,MAAA,CAAA,IAAA,CAAK,SAAS,YAAa,CAAA,IAAA;AAAA,MAChC,CAAK,CAAA,KAAA,CAAA,CAAE,IAAS,KAAA,MAAA,CAAO,IAAK,CAAA;AAAA,KAC9B,EAAG,OAAO,QAAS,CAAA,IAAA;AACnB,IAAO,OAAA,MAAA;AAAA,GACR,CAAA;AAED,EAAO,OAAA,EAAE,OAAO,WAAY,CAAA,GAAA,CAAI,OAAK,CAAE,CAAA,MAAM,GAAG,MAAO,EAAA;AACzD;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"transformers.cjs.js","sources":["../../src/lib/transformers.ts"],"sourcesContent":["/*\n * Copyright 2024 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 */\nimport type { GroupTransformer, UserTransformer } from './types';\n\n/**\n * @public\n */\nexport const noopGroupTransformer: GroupTransformer = async (\n entity,\n _user,\n _realm,\n) => entity;\n\n/**\n * @public\n */\nexport const noopUserTransformer: UserTransformer = async (\n entity,\n _user,\n _realm,\n _groups,\n) => entity;\n\n/**\n * @public\n * User transformer that sanitizes .metadata.name from email address to a valid name\n */\nexport const sanitizeEmailTransformer: UserTransformer = async (\n entity,\n _user,\n _realm,\n _groups,\n) => {\n entity.metadata.name = entity.metadata.name.replace(/[^a-zA-Z0-9]/g, '-');\n return entity;\n};\n"],"names":[],"mappings":";;AAoBO,MAAM,oBAAyC,GAAA,OACpD,MACA,EAAA,KAAA,EACA,MACG,KAAA,OAAA;AAKE,MAAM,mBAAuC,GAAA,OAClD,MACA,EAAA,KAAA,EACA,QACA,OACG,KAAA,OAAA;AAME,MAAM,wBAA4C,GAAA,OACvD,MACA,EAAA,KAAA,EACA,QACA,OACG,KAAA;AACH,EAAA,MAAA,CAAO,SAAS,IAAO,GAAA,MAAA,CAAO,SAAS,IAAK,CAAA,OAAA,CAAQ,iBAAiB,GAAG,CAAA,CAAA;AACxE,EAAO,OAAA,MAAA,CAAA;AACT;;;;;;"}
1
+ {"version":3,"file":"transformers.cjs.js","sources":["../../src/lib/transformers.ts"],"sourcesContent":["/*\n * Copyright 2024 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 */\nimport type { GroupTransformer, UserTransformer } from './types';\n\n/**\n * @public\n */\nexport const noopGroupTransformer: GroupTransformer = async (\n entity,\n _user,\n _realm,\n) => entity;\n\n/**\n * @public\n */\nexport const noopUserTransformer: UserTransformer = async (\n entity,\n _user,\n _realm,\n _groups,\n) => entity;\n\n/**\n * @public\n * User transformer that sanitizes .metadata.name from email address to a valid name\n */\nexport const sanitizeEmailTransformer: UserTransformer = async (\n entity,\n _user,\n _realm,\n _groups,\n) => {\n entity.metadata.name = entity.metadata.name.replace(/[^a-zA-Z0-9]/g, '-');\n return entity;\n};\n"],"names":[],"mappings":";;AAoBO,MAAM,oBAAyC,GAAA,OACpD,MACA,EAAA,KAAA,EACA,MACG,KAAA;AAKE,MAAM,mBAAuC,GAAA,OAClD,MACA,EAAA,KAAA,EACA,QACA,OACG,KAAA;AAME,MAAM,wBAA4C,GAAA,OACvD,MACA,EAAA,KAAA,EACA,QACA,OACG,KAAA;AACH,EAAA,MAAA,CAAO,SAAS,IAAO,GAAA,MAAA,CAAO,SAAS,IAAK,CAAA,OAAA,CAAQ,iBAAiB,GAAG,CAAA;AACxE,EAAO,OAAA,MAAA;AACT;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"catalogModuleKeycloakEntityProvider.cjs.js","sources":["../../src/module/catalogModuleKeycloakEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2024 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 {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { InputError } from '@backstage/errors';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';\n\nimport { keycloakTransformerExtensionPoint } from '../extensions';\nimport type { GroupTransformer, UserTransformer } from '../lib/types';\nimport { KeycloakOrgEntityProvider } from '../providers';\n\n/**\n * Registers the `KeycloakEntityProvider` with the catalog processing extension point.\n *\n * @public\n */\nexport const catalogModuleKeycloakEntityProvider = createBackendModule({\n pluginId: 'catalog',\n moduleId: 'catalog-backend-module-keycloak',\n register(env) {\n let userTransformer: UserTransformer | undefined;\n let groupTransformer: GroupTransformer | undefined;\n\n env.registerExtensionPoint(keycloakTransformerExtensionPoint, {\n setUserTransformer(transformer) {\n if (userTransformer) {\n throw new InputError('User transformer may only be set once');\n }\n userTransformer = transformer;\n },\n setGroupTransformer(transformer) {\n if (groupTransformer) {\n throw new InputError('Group transformer may only be set once');\n }\n groupTransformer = transformer;\n },\n });\n env.registerInit({\n deps: {\n catalog: catalogProcessingExtensionPoint,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n },\n async init({ catalog, config, logger, scheduler }) {\n catalog.addEntityProvider(\n KeycloakOrgEntityProvider.fromConfig(\n { config, logger },\n {\n scheduler: scheduler,\n schedule: scheduler.createScheduledTaskRunner({\n frequency: { minutes: 30 },\n timeout: { minutes: 3 },\n }),\n userTransformer: userTransformer,\n groupTransformer: groupTransformer,\n },\n ),\n );\n },\n });\n },\n});\n"],"names":["createBackendModule","keycloakTransformerExtensionPoint","InputError","catalogProcessingExtensionPoint","coreServices","KeycloakOrgEntityProvider"],"mappings":";;;;;;;;AAgCO,MAAM,sCAAsCA,oCAAoB,CAAA;AAAA,EACrE,QAAU,EAAA,SAAA;AAAA,EACV,QAAU,EAAA,iCAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAI,IAAA,eAAA,CAAA;AACJ,IAAI,IAAA,gBAAA,CAAA;AAEJ,IAAA,GAAA,CAAI,uBAAuBC,4CAAmC,EAAA;AAAA,MAC5D,mBAAmB,WAAa,EAAA;AAC9B,QAAA,IAAI,eAAiB,EAAA;AACnB,UAAM,MAAA,IAAIC,kBAAW,uCAAuC,CAAA,CAAA;AAAA,SAC9D;AACA,QAAkB,eAAA,GAAA,WAAA,CAAA;AAAA,OACpB;AAAA,MACA,oBAAoB,WAAa,EAAA;AAC/B,QAAA,IAAI,gBAAkB,EAAA;AACpB,UAAM,MAAA,IAAIA,kBAAW,wCAAwC,CAAA,CAAA;AAAA,SAC/D;AACA,QAAmB,gBAAA,GAAA,WAAA,CAAA;AAAA,OACrB;AAAA,KACD,CAAA,CAAA;AACD,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,OAAS,EAAAC,qCAAA;AAAA,QACT,QAAQC,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA,EAAE,SAAS,MAAQ,EAAA,MAAA,EAAQ,WAAa,EAAA;AACjD,QAAQ,OAAA,CAAA,iBAAA;AAAA,UACNC,mDAA0B,CAAA,UAAA;AAAA,YACxB,EAAE,QAAQ,MAAO,EAAA;AAAA,YACjB;AAAA,cACE,SAAA;AAAA,cACA,QAAA,EAAU,UAAU,yBAA0B,CAAA;AAAA,gBAC5C,SAAA,EAAW,EAAE,OAAA,EAAS,EAAG,EAAA;AAAA,gBACzB,OAAA,EAAS,EAAE,OAAA,EAAS,CAAE,EAAA;AAAA,eACvB,CAAA;AAAA,cACD,eAAA;AAAA,cACA,gBAAA;AAAA,aACF;AAAA,WACF;AAAA,SACF,CAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"catalogModuleKeycloakEntityProvider.cjs.js","sources":["../../src/module/catalogModuleKeycloakEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2024 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 {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { InputError } from '@backstage/errors';\nimport { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';\n\nimport { keycloakTransformerExtensionPoint } from '../extensions';\nimport type { GroupTransformer, UserTransformer } from '../lib/types';\nimport { KeycloakOrgEntityProvider } from '../providers';\n\n/**\n * Registers the `KeycloakEntityProvider` with the catalog processing extension point.\n *\n * @public\n */\nexport const catalogModuleKeycloakEntityProvider = createBackendModule({\n pluginId: 'catalog',\n moduleId: 'catalog-backend-module-keycloak',\n register(env) {\n let userTransformer: UserTransformer | undefined;\n let groupTransformer: GroupTransformer | undefined;\n\n env.registerExtensionPoint(keycloakTransformerExtensionPoint, {\n setUserTransformer(transformer) {\n if (userTransformer) {\n throw new InputError('User transformer may only be set once');\n }\n userTransformer = transformer;\n },\n setGroupTransformer(transformer) {\n if (groupTransformer) {\n throw new InputError('Group transformer may only be set once');\n }\n groupTransformer = transformer;\n },\n });\n env.registerInit({\n deps: {\n catalog: catalogProcessingExtensionPoint,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n scheduler: coreServices.scheduler,\n },\n async init({ catalog, config, logger, scheduler }) {\n catalog.addEntityProvider(\n KeycloakOrgEntityProvider.fromConfig(\n { config, logger },\n {\n scheduler: scheduler,\n schedule: scheduler.createScheduledTaskRunner({\n frequency: { minutes: 30 },\n timeout: { minutes: 3 },\n }),\n userTransformer: userTransformer,\n groupTransformer: groupTransformer,\n },\n ),\n );\n },\n });\n },\n});\n"],"names":["createBackendModule","keycloakTransformerExtensionPoint","InputError","catalogProcessingExtensionPoint","coreServices","KeycloakOrgEntityProvider"],"mappings":";;;;;;;;AAgCO,MAAM,sCAAsCA,oCAAoB,CAAA;AAAA,EACrE,QAAU,EAAA,SAAA;AAAA,EACV,QAAU,EAAA,iCAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAI,IAAA,eAAA;AACJ,IAAI,IAAA,gBAAA;AAEJ,IAAA,GAAA,CAAI,uBAAuBC,4CAAmC,EAAA;AAAA,MAC5D,mBAAmB,WAAa,EAAA;AAC9B,QAAA,IAAI,eAAiB,EAAA;AACnB,UAAM,MAAA,IAAIC,kBAAW,uCAAuC,CAAA;AAAA;AAE9D,QAAkB,eAAA,GAAA,WAAA;AAAA,OACpB;AAAA,MACA,oBAAoB,WAAa,EAAA;AAC/B,QAAA,IAAI,gBAAkB,EAAA;AACpB,UAAM,MAAA,IAAIA,kBAAW,wCAAwC,CAAA;AAAA;AAE/D,QAAmB,gBAAA,GAAA,WAAA;AAAA;AACrB,KACD,CAAA;AACD,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,OAAS,EAAAC,qCAAA;AAAA,QACT,QAAQC,6BAAa,CAAA,UAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,WAAWA,6BAAa,CAAA;AAAA,OAC1B;AAAA,MACA,MAAM,IAAK,CAAA,EAAE,SAAS,MAAQ,EAAA,MAAA,EAAQ,WAAa,EAAA;AACjD,QAAQ,OAAA,CAAA,iBAAA;AAAA,UACNC,mDAA0B,CAAA,UAAA;AAAA,YACxB,EAAE,QAAQ,MAAO,EAAA;AAAA,YACjB;AAAA,cACE,SAAA;AAAA,cACA,QAAA,EAAU,UAAU,yBAA0B,CAAA;AAAA,gBAC5C,SAAA,EAAW,EAAE,OAAA,EAAS,EAAG,EAAA;AAAA,gBACzB,OAAA,EAAS,EAAE,OAAA,EAAS,CAAE;AAAA,eACvB,CAAA;AAAA,cACD,eAAA;AAAA,cACA;AAAA;AACF;AACF,SACF;AAAA;AACF,KACD,CAAA;AAAA;AAEL,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"KeycloakOrgEntityProvider.cjs.js","sources":["../../src/providers/KeycloakOrgEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2024 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 type {\n LoggerService,\n SchedulerService,\n SchedulerServiceTaskRunner,\n} from '@backstage/backend-plugin-api';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n type Entity,\n} from '@backstage/catalog-model';\nimport type { Config } from '@backstage/config';\nimport { InputError, isError, NotFoundError } from '@backstage/errors';\nimport type {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\n\nimport type { Credentials } from '@keycloak/keycloak-admin-client/lib/utils/auth';\n// @ts-ignore\nimport inclusion from 'inclusion';\nimport { merge } from 'lodash';\nimport * as uuid from 'uuid';\n\nimport {\n GroupTransformer,\n KEYCLOAK_ID_ANNOTATION,\n KeycloakProviderConfig,\n UserTransformer,\n} from '../lib';\nimport { readProviderConfigs } from '../lib/config';\nimport { readKeycloakRealm } from '../lib/read';\n\n/**\n * Options for {@link KeycloakOrgEntityProvider}.\n *\n * @public\n */\nexport interface KeycloakOrgEntityProviderOptions {\n /**\n * A unique, stable identifier for this provider.\n *\n * @example \"production\"\n */\n id: string;\n\n /**\n * The refresh schedule to use.\n * @remarks\n *\n * You can pass in the result of\n * {@link @backstage/backend-plugin-api#SchedulerService.createScheduledTaskRunner}\n * to enable automatic scheduling of tasks.\n */\n schedule?: SchedulerServiceTaskRunner;\n\n /**\n * Scheduler used to schedule refreshes based on\n * the schedule config.\n */\n scheduler?: SchedulerService;\n\n /**\n * The logger to use.\n */\n logger: LoggerService;\n\n /**\n * The function that transforms a user entry in LDAP to an entity.\n */\n userTransformer?: UserTransformer;\n\n /**\n * The function that transforms a group entry in LDAP to an entity.\n */\n groupTransformer?: GroupTransformer;\n}\n\n// Makes sure that emitted entities have a proper location\nexport const withLocations = (\n baseUrl: string,\n realm: string,\n entity: Entity,\n): Entity => {\n const kind = entity.kind === 'Group' ? 'groups' : 'users';\n const location = `url:${baseUrl}/admin/realms/${realm}/${kind}/${entity.metadata.annotations?.[KEYCLOAK_ID_ANNOTATION]}`;\n return merge(\n {\n metadata: {\n annotations: {\n [ANNOTATION_LOCATION]: location,\n [ANNOTATION_ORIGIN_LOCATION]: location,\n },\n },\n },\n entity,\n ) as Entity;\n};\n\n/**\n * Ingests org data (users and groups) from GitHub.\n *\n * @public\n */\nexport class KeycloakOrgEntityProvider implements EntityProvider {\n private connection?: EntityProviderConnection;\n private scheduleFn?: () => Promise<void>;\n\n static fromConfig(\n deps: {\n config: Config;\n logger: LoggerService;\n },\n options: (\n | { schedule: SchedulerServiceTaskRunner }\n | { scheduler: SchedulerService }\n ) & {\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n },\n ): KeycloakOrgEntityProvider[] {\n const { config, logger } = deps;\n return readProviderConfigs(config).map(providerConfig => {\n let taskRunner: SchedulerServiceTaskRunner | string;\n if ('scheduler' in options && providerConfig.schedule) {\n // Create a scheduled task runner using the provided scheduler and schedule configuration\n taskRunner = options.scheduler.createScheduledTaskRunner(\n providerConfig.schedule,\n );\n } else if ('schedule' in options) {\n // Use the provided schedule directly\n taskRunner = options.schedule;\n } else {\n throw new InputError(\n `No schedule provided via config for KeycloakOrgEntityProvider:${providerConfig.id}.`,\n );\n }\n\n const provider = new KeycloakOrgEntityProvider({\n id: providerConfig.id,\n provider: providerConfig,\n logger: logger,\n taskRunner: taskRunner,\n userTransformer: options.userTransformer,\n groupTransformer: options.groupTransformer,\n });\n\n return provider;\n });\n }\n\n constructor(\n private options: {\n id: string;\n provider: KeycloakProviderConfig;\n logger: LoggerService;\n taskRunner: SchedulerServiceTaskRunner;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n },\n ) {\n this.schedule(options.taskRunner);\n }\n\n getProviderName(): string {\n return `KeycloakOrgEntityProvider:${this.options.id}`;\n }\n\n async connect(connection: EntityProviderConnection) {\n this.connection = connection;\n await this.scheduleFn?.();\n }\n\n /**\n * Runs one complete ingestion loop. Call this method regularly at some\n * appropriate cadence.\n */\n async read(options?: { logger?: LoggerService }) {\n if (!this.connection) {\n throw new NotFoundError('Not initialized');\n }\n\n const logger = options?.logger ?? this.options.logger;\n const provider = this.options.provider;\n\n const { markReadComplete } = trackProgress(logger);\n const KeyCloakAdminClientModule = await inclusion(\n '@keycloak/keycloak-admin-client',\n );\n const KeyCloakAdminClient = KeyCloakAdminClientModule.default;\n\n const kcAdminClient = new KeyCloakAdminClient({\n baseUrl: provider.baseUrl,\n realmName: provider.loginRealm,\n });\n\n let credentials: Credentials;\n\n if (provider.username && provider.password) {\n credentials = {\n grantType: 'password',\n clientId: provider.clientId ?? 'admin-cli',\n username: provider.username,\n password: provider.password,\n };\n } else if (provider.clientId && provider.clientSecret) {\n credentials = {\n grantType: 'client_credentials',\n clientId: provider.clientId,\n clientSecret: provider.clientSecret,\n };\n } else {\n throw new InputError(\n `username and password or clientId and clientSecret must be provided.`,\n );\n }\n\n await kcAdminClient.auth(credentials);\n\n const { users, groups } = await readKeycloakRealm(\n kcAdminClient,\n provider,\n logger,\n {\n userQuerySize: provider.userQuerySize,\n groupQuerySize: provider.groupQuerySize,\n userTransformer: this.options.userTransformer,\n groupTransformer: this.options.groupTransformer,\n },\n );\n\n const { markCommitComplete } = markReadComplete({ users, groups });\n\n await this.connection.applyMutation({\n type: 'full',\n entities: [...users, ...groups].map(entity => ({\n locationKey: `keycloak-org-provider:${this.options.id}`,\n entity: withLocations(provider.baseUrl, provider.realm, entity),\n })),\n });\n\n markCommitComplete();\n }\n\n schedule(taskRunner: SchedulerServiceTaskRunner) {\n this.scheduleFn = async () => {\n const id = `${this.getProviderName()}:refresh`;\n await taskRunner.run({\n id,\n fn: async () => {\n const logger = this.options.logger.child({\n class: KeycloakOrgEntityProvider.prototype.constructor.name,\n taskId: id,\n taskInstanceId: uuid.v4(),\n });\n\n try {\n await this.read({ logger });\n } catch (error) {\n if (isError(error)) {\n // Ensure that we don't log any sensitive internal data:\n logger.error('Error while syncing Keycloak users and groups', {\n // Default Error properties:\n name: error.name,\n cause: error.cause,\n message: error.message,\n stack: error.stack,\n // Additional status code if available:\n status: (error.response as { status?: string })?.status,\n });\n }\n }\n },\n });\n };\n }\n}\n\n// Helps wrap the timing and logging behaviors\nfunction trackProgress(logger: LoggerService) {\n let timestamp = Date.now();\n let summary: string;\n\n logger.info('Reading Keycloak users and groups');\n\n function markReadComplete(read: { users: unknown[]; groups: unknown[] }) {\n summary = `${read.users.length} Keycloak users and ${read.groups.length} Keycloak groups`;\n const readDuration = ((Date.now() - timestamp) / 1000).toFixed(1);\n timestamp = Date.now();\n logger.info(`Read ${summary} in ${readDuration} seconds. Committing...`);\n return { markCommitComplete };\n }\n\n function markCommitComplete() {\n const commitDuration = ((Date.now() - timestamp) / 1000).toFixed(1);\n logger.info(`Committed ${summary} in ${commitDuration} seconds.`);\n }\n\n return { markReadComplete };\n}\n"],"names":["KEYCLOAK_ID_ANNOTATION","merge","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION","config","readProviderConfigs","InputError","NotFoundError","inclusion","readKeycloakRealm","uuid","isError"],"mappings":"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"}
1
+ {"version":3,"file":"KeycloakOrgEntityProvider.cjs.js","sources":["../../src/providers/KeycloakOrgEntityProvider.ts"],"sourcesContent":["/*\n * Copyright 2024 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 type {\n LoggerService,\n SchedulerService,\n SchedulerServiceTaskRunner,\n} from '@backstage/backend-plugin-api';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n type Entity,\n} from '@backstage/catalog-model';\nimport type { Config } from '@backstage/config';\nimport { InputError, isError, NotFoundError } from '@backstage/errors';\nimport type {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-node';\n\nimport type { Credentials } from '@keycloak/keycloak-admin-client/lib/utils/auth';\n// @ts-ignore\nimport inclusion from 'inclusion';\nimport { merge } from 'lodash';\nimport * as uuid from 'uuid';\n\nimport {\n GroupTransformer,\n KEYCLOAK_ID_ANNOTATION,\n KeycloakProviderConfig,\n UserTransformer,\n} from '../lib';\nimport { readProviderConfigs } from '../lib/config';\nimport { readKeycloakRealm } from '../lib/read';\n\n/**\n * Options for {@link KeycloakOrgEntityProvider}.\n *\n * @public\n */\nexport interface KeycloakOrgEntityProviderOptions {\n /**\n * A unique, stable identifier for this provider.\n *\n * @example \"production\"\n */\n id: string;\n\n /**\n * The refresh schedule to use.\n * @remarks\n *\n * You can pass in the result of\n * {@link @backstage/backend-plugin-api#SchedulerService.createScheduledTaskRunner}\n * to enable automatic scheduling of tasks.\n */\n schedule?: SchedulerServiceTaskRunner;\n\n /**\n * Scheduler used to schedule refreshes based on\n * the schedule config.\n */\n scheduler?: SchedulerService;\n\n /**\n * The logger to use.\n */\n logger: LoggerService;\n\n /**\n * The function that transforms a user entry in LDAP to an entity.\n */\n userTransformer?: UserTransformer;\n\n /**\n * The function that transforms a group entry in LDAP to an entity.\n */\n groupTransformer?: GroupTransformer;\n}\n\n// Makes sure that emitted entities have a proper location\nexport const withLocations = (\n baseUrl: string,\n realm: string,\n entity: Entity,\n): Entity => {\n const kind = entity.kind === 'Group' ? 'groups' : 'users';\n const location = `url:${baseUrl}/admin/realms/${realm}/${kind}/${entity.metadata.annotations?.[KEYCLOAK_ID_ANNOTATION]}`;\n return merge(\n {\n metadata: {\n annotations: {\n [ANNOTATION_LOCATION]: location,\n [ANNOTATION_ORIGIN_LOCATION]: location,\n },\n },\n },\n entity,\n ) as Entity;\n};\n\n/**\n * Ingests org data (users and groups) from GitHub.\n *\n * @public\n */\nexport class KeycloakOrgEntityProvider implements EntityProvider {\n private connection?: EntityProviderConnection;\n private scheduleFn?: () => Promise<void>;\n\n static fromConfig(\n deps: {\n config: Config;\n logger: LoggerService;\n },\n options: (\n | { schedule: SchedulerServiceTaskRunner }\n | { scheduler: SchedulerService }\n ) & {\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n },\n ): KeycloakOrgEntityProvider[] {\n const { config, logger } = deps;\n return readProviderConfigs(config).map(providerConfig => {\n let taskRunner: SchedulerServiceTaskRunner | string;\n if ('scheduler' in options && providerConfig.schedule) {\n // Create a scheduled task runner using the provided scheduler and schedule configuration\n taskRunner = options.scheduler.createScheduledTaskRunner(\n providerConfig.schedule,\n );\n } else if ('schedule' in options) {\n // Use the provided schedule directly\n taskRunner = options.schedule;\n } else {\n throw new InputError(\n `No schedule provided via config for KeycloakOrgEntityProvider:${providerConfig.id}.`,\n );\n }\n\n const provider = new KeycloakOrgEntityProvider({\n id: providerConfig.id,\n provider: providerConfig,\n logger: logger,\n taskRunner: taskRunner,\n userTransformer: options.userTransformer,\n groupTransformer: options.groupTransformer,\n });\n\n return provider;\n });\n }\n\n constructor(\n private options: {\n id: string;\n provider: KeycloakProviderConfig;\n logger: LoggerService;\n taskRunner: SchedulerServiceTaskRunner;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n },\n ) {\n this.schedule(options.taskRunner);\n }\n\n getProviderName(): string {\n return `KeycloakOrgEntityProvider:${this.options.id}`;\n }\n\n async connect(connection: EntityProviderConnection) {\n this.connection = connection;\n await this.scheduleFn?.();\n }\n\n /**\n * Runs one complete ingestion loop. Call this method regularly at some\n * appropriate cadence.\n */\n async read(options?: { logger?: LoggerService }) {\n if (!this.connection) {\n throw new NotFoundError('Not initialized');\n }\n\n const logger = options?.logger ?? this.options.logger;\n const provider = this.options.provider;\n\n const { markReadComplete } = trackProgress(logger);\n const KeyCloakAdminClientModule = await inclusion(\n '@keycloak/keycloak-admin-client',\n );\n const KeyCloakAdminClient = KeyCloakAdminClientModule.default;\n\n const kcAdminClient = new KeyCloakAdminClient({\n baseUrl: provider.baseUrl,\n realmName: provider.loginRealm,\n });\n\n let credentials: Credentials;\n\n if (provider.username && provider.password) {\n credentials = {\n grantType: 'password',\n clientId: provider.clientId ?? 'admin-cli',\n username: provider.username,\n password: provider.password,\n };\n } else if (provider.clientId && provider.clientSecret) {\n credentials = {\n grantType: 'client_credentials',\n clientId: provider.clientId,\n clientSecret: provider.clientSecret,\n };\n } else {\n throw new InputError(\n `username and password or clientId and clientSecret must be provided.`,\n );\n }\n\n await kcAdminClient.auth(credentials);\n\n const { users, groups } = await readKeycloakRealm(\n kcAdminClient,\n provider,\n logger,\n {\n userQuerySize: provider.userQuerySize,\n groupQuerySize: provider.groupQuerySize,\n userTransformer: this.options.userTransformer,\n groupTransformer: this.options.groupTransformer,\n },\n );\n\n const { markCommitComplete } = markReadComplete({ users, groups });\n\n await this.connection.applyMutation({\n type: 'full',\n entities: [...users, ...groups].map(entity => ({\n locationKey: `keycloak-org-provider:${this.options.id}`,\n entity: withLocations(provider.baseUrl, provider.realm, entity),\n })),\n });\n\n markCommitComplete();\n }\n\n schedule(taskRunner: SchedulerServiceTaskRunner) {\n this.scheduleFn = async () => {\n const id = `${this.getProviderName()}:refresh`;\n await taskRunner.run({\n id,\n fn: async () => {\n const logger = this.options.logger.child({\n class: KeycloakOrgEntityProvider.prototype.constructor.name,\n taskId: id,\n taskInstanceId: uuid.v4(),\n });\n\n try {\n await this.read({ logger });\n } catch (error) {\n if (isError(error)) {\n // Ensure that we don't log any sensitive internal data:\n logger.error('Error while syncing Keycloak users and groups', {\n // Default Error properties:\n name: error.name,\n cause: error.cause,\n message: error.message,\n stack: error.stack,\n // Additional status code if available:\n status: (error.response as { status?: string })?.status,\n });\n }\n }\n },\n });\n };\n }\n}\n\n// Helps wrap the timing and logging behaviors\nfunction trackProgress(logger: LoggerService) {\n let timestamp = Date.now();\n let summary: string;\n\n logger.info('Reading Keycloak users and groups');\n\n function markReadComplete(read: { users: unknown[]; groups: unknown[] }) {\n summary = `${read.users.length} Keycloak users and ${read.groups.length} Keycloak groups`;\n const readDuration = ((Date.now() - timestamp) / 1000).toFixed(1);\n timestamp = Date.now();\n logger.info(`Read ${summary} in ${readDuration} seconds. Committing...`);\n return { markCommitComplete };\n }\n\n function markCommitComplete() {\n const commitDuration = ((Date.now() - timestamp) / 1000).toFixed(1);\n logger.info(`Committed ${summary} in ${commitDuration} seconds.`);\n }\n\n return { markReadComplete };\n}\n"],"names":["KEYCLOAK_ID_ANNOTATION","merge","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION","config","readProviderConfigs","InputError","NotFoundError","inclusion","readKeycloakRealm","uuid","isError"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage-community/plugin-catalog-backend-module-keycloak",
3
- "version": "3.2.4",
3
+ "version": "3.3.0",
4
4
  "description": "A Backend backend plugin for Keycloak",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "types": "./dist/index.d.ts",
@@ -23,6 +23,13 @@
23
23
  },
24
24
  "./package.json": "./package.json"
25
25
  },
26
+ "typesVersions": {
27
+ "*": {
28
+ "index": [
29
+ "dist/index.d.ts"
30
+ ]
31
+ }
32
+ },
26
33
  "scripts": {
27
34
  "build": "backstage-cli package build",
28
35
  "clean": "backstage-cli package clean",
@@ -39,10 +46,10 @@
39
46
  "prettier:fix": "prettier --ignore-unknown --write ."
40
47
  },
41
48
  "dependencies": {
42
- "@backstage/backend-plugin-api": "^1.0.1",
43
- "@backstage/catalog-model": "^1.7.0",
44
- "@backstage/errors": "^1.2.4",
45
- "@backstage/plugin-catalog-node": "^1.13.1",
49
+ "@backstage/backend-plugin-api": "^1.1.0",
50
+ "@backstage/catalog-model": "^1.7.2",
51
+ "@backstage/errors": "^1.2.6",
52
+ "@backstage/plugin-catalog-node": "^1.15.0",
46
53
  "@keycloak/keycloak-admin-client": "24.0.5",
47
54
  "inclusion": "^1.0.1",
48
55
  "lodash": "^4.17.21",
@@ -50,11 +57,11 @@
50
57
  "uuid": "^9.0.1"
51
58
  },
52
59
  "devDependencies": {
53
- "@backstage/backend-defaults": "0.5.2",
54
- "@backstage/backend-test-utils": "1.0.2",
55
- "@backstage/cli": "0.28.2",
56
- "@backstage/config": "1.2.0",
57
- "@backstage/plugin-catalog-backend": "1.27.1",
60
+ "@backstage/backend-defaults": "^0.6.2",
61
+ "@backstage/backend-test-utils": "^1.2.0",
62
+ "@backstage/cli": "^0.29.4",
63
+ "@backstage/config": "^1.3.1",
64
+ "@backstage/plugin-catalog-backend": "^1.29.0",
58
65
  "@spotify/prettier-config": "^15.0.0",
59
66
  "@types/lodash": "4.17.13",
60
67
  "@types/uuid": "9.0.8",