@backstage-community/plugin-catalog-backend-module-keycloak 3.5.2 → 3.6.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,17 @@
1
1
  ### Dependencies
2
2
 
3
+ ## 3.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 984800c: Adding briefRepresentation flag to config to fetch additional group and user attributes
8
+
9
+ ## 3.5.3
10
+
11
+ ### Patch Changes
12
+
13
+ - 208e250: Updated dependency `@types/uuid` to `^10.0.0`.
14
+
3
15
  ## 3.5.2
4
16
 
5
17
  ### Patch Changes
package/config.d.ts CHANGED
@@ -55,7 +55,11 @@ export interface Config {
55
55
  * Maximum request concurrency to prevent DoS attacks on the Keycloak server.
56
56
  */
57
57
  maxConcurrency?: number;
58
-
58
+ /**
59
+ * Whether the API call will return a brief representation for groups and users or not. Defaults to true
60
+ * @defaultValue true
61
+ */
62
+ briefRepresentation?: boolean;
59
63
  schedule?: SchedulerServiceTaskScheduleDefinitionConfig;
60
64
  } & (
61
65
  | {
package/dist/index.d.ts CHANGED
@@ -138,6 +138,12 @@ type KeycloakProviderConfig = {
138
138
  * Maximum request concurrency to prevent DoS attacks on the Keycloak server.
139
139
  */
140
140
  maxConcurrency?: number;
141
+ /**
142
+ * Whether the API call will return a brief representation for groups and users or not. Defaults to true.
143
+ * A complete representation will include additional attributes
144
+ * @defaultValue true
145
+ */
146
+ briefRepresentation?: boolean;
141
147
  };
142
148
 
143
149
  /**
@@ -14,6 +14,9 @@ const readProviderConfig = (id, providerConfigInstance) => {
14
14
  const userQuerySize = providerConfigInstance.getOptionalNumber("userQuerySize");
15
15
  const groupQuerySize = providerConfigInstance.getOptionalNumber("groupQuerySize");
16
16
  const maxConcurrency = providerConfigInstance.getOptionalNumber("maxConcurrency");
17
+ const briefRepresentation = providerConfigInstance.getOptionalBoolean(
18
+ "briefRepresentation"
19
+ );
17
20
  if (clientId && !clientSecret) {
18
21
  throw new errors.InputError(
19
22
  `clientSecret must be provided when clientId is defined.`
@@ -45,7 +48,8 @@ const readProviderConfig = (id, providerConfigInstance) => {
45
48
  schedule,
46
49
  userQuerySize,
47
50
  groupQuerySize,
48
- maxConcurrency
51
+ maxConcurrency,
52
+ briefRepresentation
49
53
  };
50
54
  };
51
55
  const readProviderConfigs = (config) => {
@@ -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 /**\n * Maximum request concurrency to prevent DoS attacks on the Keycloak server.\n */\n maxConcurrency?: 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 const maxConcurrency =\n providerConfigInstance.getOptionalNumber('maxConcurrency');\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 maxConcurrency,\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":";;;;;AAoGA,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;AAC3D,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,cAAA;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
+ {"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 /**\n * Maximum request concurrency to prevent DoS attacks on the Keycloak server.\n */\n maxConcurrency?: number;\n\n /**\n * Whether the API call will return a brief representation for groups and users or not. Defaults to true.\n * A complete representation will include additional attributes\n * @defaultValue true\n */\n briefRepresentation?: boolean;\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 const maxConcurrency =\n providerConfigInstance.getOptionalNumber('maxConcurrency');\n const briefRepresentation = providerConfigInstance.getOptionalBoolean(\n 'briefRepresentation',\n );\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 maxConcurrency,\n briefRepresentation,\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":";;;;;AA2GA,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;AAC3D,EAAM,MAAA,cAAA,GACJ,sBAAuB,CAAA,iBAAA,CAAkB,gBAAgB,CAAA;AAC3D,EAAA,MAAM,sBAAsB,sBAAuB,CAAA,kBAAA;AAAA,IACjD;AAAA,GACF;AAEA,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,cAAA;AAAA,IACA,cAAA;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;;;;"}
@@ -3,7 +3,9 @@
3
3
  const KEYCLOAK_ID_ANNOTATION = "keycloak.org/id";
4
4
  const KEYCLOAK_REALM_ANNOTATION = "keycloak.org/realm";
5
5
  const KEYCLOAK_ENTITY_QUERY_SIZE = 100;
6
+ const KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT = true;
6
7
 
8
+ exports.KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT = KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT;
7
9
  exports.KEYCLOAK_ENTITY_QUERY_SIZE = KEYCLOAK_ENTITY_QUERY_SIZE;
8
10
  exports.KEYCLOAK_ID_ANNOTATION = KEYCLOAK_ID_ANNOTATION;
9
11
  exports.KEYCLOAK_REALM_ANNOTATION = KEYCLOAK_REALM_ANNOTATION;
@@ -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;AAC/B,MAAM,yBAA4B,GAAA;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;\nexport const KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT = true;\n"],"names":[],"mappings":";;AAiBO,MAAM,sBAAyB,GAAA;AAC/B,MAAM,yBAA4B,GAAA;AAClC,MAAM,0BAA6B,GAAA;AACnC,MAAM,qCAAwC,GAAA;;;;;;;"}
@@ -58,6 +58,7 @@ async function getEntities(getEntitiesFn, config, logger, limit, entityQuerySize
58
58
  const rawEntityCount = await entitiesAPI.count({ realm: config.realm });
59
59
  const entityCount = typeof rawEntityCount === "number" ? rawEntityCount : rawEntityCount.count;
60
60
  const pageCount = Math.ceil(entityCount / entityQuerySize);
61
+ const brief = config.briefRepresentation ?? constants.KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT;
61
62
  const entityPromises = Array.from(
62
63
  { length: pageCount },
63
64
  (_, i) => limit(
@@ -65,7 +66,8 @@ async function getEntities(getEntitiesFn, config, logger, limit, entityQuerySize
65
66
  return entities.find({
66
67
  realm: config.realm,
67
68
  max: entityQuerySize,
68
- first: i * entityQuerySize
69
+ first: i * entityQuerySize,
70
+ briefRepresentation: brief
69
71
  }).then((ents) => {
70
72
  logger.debug(
71
73
  `Importing keycloak entities batch with index ${i} from pages: ${pageCount}`
@@ -106,6 +108,7 @@ async function getAllGroupMembers(groupsAPI, groupId, config, options) {
106
108
  }
107
109
  async function processGroupsRecursively(kcAdminClient, config, logger, topLevelGroups) {
108
110
  const allGroups = [];
111
+ const brief = config.briefRepresentation ?? constants.KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT;
109
112
  for (const group of topLevelGroups) {
110
113
  allGroups.push(group);
111
114
  if (group.subGroupCount > 0) {
@@ -114,7 +117,7 @@ async function processGroupsRecursively(kcAdminClient, config, logger, topLevelG
114
117
  parentId: group.id,
115
118
  first: 0,
116
119
  max: group.subGroupCount,
117
- briefRepresentation: true
120
+ briefRepresentation: brief
118
121
  });
119
122
  const subGroupResults = await processGroupsRecursively(
120
123
  kcAdminClient,
@@ -185,6 +188,7 @@ const readKeycloakRealm = async (client, config, logger, limit, options) => {
185
188
  );
186
189
  }
187
190
  logger.debug(`Fetching group members for keycloak groups and list subgroups`);
191
+ const brief = config.briefRepresentation ?? constants.KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT;
188
192
  const kGroups = await Promise.all(
189
193
  rawKGroups.map(
190
194
  (g) => limit(async () => {
@@ -204,7 +208,7 @@ const readKeycloakRealm = async (client, config, logger, limit, options) => {
204
208
  parentId: g.id,
205
209
  first: 0,
206
210
  max: g.subGroupCount,
207
- briefRepresentation: false,
211
+ briefRepresentation: brief,
208
212
  realm: config.realm
209
213
  });
210
214
  }
@@ -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';\nimport { LimitFunction } from 'p-limit';\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';\nimport { ensureTokenValid } from './authenticate';\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 groupIndex: Map<string, string[]>,\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: groupIndex.get(user.username!) ?? [],\n },\n };\n\n return await transformer(entity, user, realm, keycloakGroups);\n};\n\nexport async function getEntities<T extends Users | Groups>(\n getEntitiesFn: () => Promise<T>,\n config: KeycloakProviderConfig,\n logger: LoggerService,\n limit: LimitFunction,\n entityQuerySize: number = KEYCLOAK_ENTITY_QUERY_SIZE,\n): Promise<Awaited<ReturnType<T['find']>>> {\n const entitiesAPI = await getEntitiesFn();\n const rawEntityCount = await entitiesAPI.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({ length: pageCount }, (_, i) =>\n limit(() =>\n getEntitiesFn().then(entities => {\n return entities\n .find({\n realm: config.realm,\n max: entityQuerySize,\n first: i * entityQuerySize,\n })\n .then(ents => {\n logger.debug(\n `Importing keycloak entities batch with index ${i} from pages: ${pageCount}`,\n );\n return ents;\n })\n .catch(err => {\n logger.warn('Failed to retieve Keycloak entities.', err);\n return [];\n }) as ReturnType<T['find']>;\n }),\n ),\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 groupsAPI: () => Promise<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 groups = await groupsAPI();\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 kcAdminClient: KeycloakAdminClient,\n config: KeycloakProviderConfig,\n logger: LoggerService,\n topLevelGroups: GroupRepresentationWithParent[],\n) {\n const allGroups: GroupRepresentationWithParent[] = [];\n for (const group of topLevelGroups) {\n allGroups.push(group);\n\n if (group.subGroupCount! > 0) {\n await ensureTokenValid(kcAdminClient, config, logger);\n const subgroups = await kcAdminClient.groups.listSubGroups({\n parentId: group.id!,\n first: 0,\n max: group.subGroupCount,\n briefRepresentation: true,\n });\n const subGroupResults = await processGroupsRecursively(\n kcAdminClient,\n config,\n logger,\n subgroups,\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 limit: LimitFunction,\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 async () => {\n await ensureTokenValid(client, config, logger);\n return client.users;\n },\n config,\n logger,\n limit,\n options?.userQuerySize,\n );\n logger.debug(`Fetched ${kUsers.length} users from Keycloak`);\n\n const topLevelKGroups = (await getEntities(\n async () => {\n await ensureTokenValid(client, config, logger);\n return client.groups;\n },\n config,\n logger,\n limit,\n options?.groupQuerySize,\n )) as GroupRepresentationWithParent[];\n logger.debug(`Fetched ${topLevelKGroups.length} groups from Keycloak`);\n\n let serverVersion: number;\n\n try {\n await ensureTokenValid(client, config, logger);\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 logger.debug(`Processing groups recursively`);\n if (isVersion23orHigher) {\n rawKGroups = await processGroupsRecursively(\n client,\n config,\n logger,\n topLevelKGroups,\n );\n } else {\n rawKGroups = topLevelKGroups.reduce(\n (acc, g) => acc.concat(...traverseGroups(g)),\n [] as GroupRepresentationWithParent[],\n );\n }\n\n logger.debug(`Fetching group members for keycloak groups and list subgroups`);\n const kGroups = await Promise.all(\n rawKGroups.map(g =>\n limit(async () => {\n g.members = await getAllGroupMembers(\n async () => {\n await ensureTokenValid(client, config, logger);\n return client.groups as Groups;\n },\n g.id!,\n config,\n options,\n );\n\n if (isVersion23orHigher) {\n if (g.subGroupCount! > 0) {\n await ensureTokenValid(client, config, logger);\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 await ensureTokenValid(client, config, logger);\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\n logger.debug(`Parsing groups`);\n const parsedGroups = await Promise.all(\n kGroups.map(async g => {\n // it is possible if fetch request failed\n if (!g) {\n return null;\n }\n const entity = await parseGroup(\n g,\n config.realm,\n options?.groupTransformer,\n );\n if (entity) {\n return { ...g, entity } as GroupRepresentationWithParentAndEntity;\n }\n return null;\n }),\n );\n const filteredParsedGroups = parsedGroups.filter(\n (group): group is GroupRepresentationWithParentAndEntity => group !== null,\n );\n\n const groupIndex = new Map<string, string[]>();\n filteredParsedGroups.forEach(group => {\n if (group.members) {\n group.members.forEach(member => {\n if (!groupIndex.has(member)) {\n groupIndex.set(member, []);\n }\n groupIndex.get(member)?.push(group.entity.metadata.name);\n });\n }\n });\n\n logger.debug('Parsing users');\n const parsedUsers = await Promise.all(\n kUsers.map(async u => {\n // it is possible if fetch request failed\n if (!u) {\n return null;\n }\n const entity = await parseUser(\n u,\n config.realm,\n filteredParsedGroups,\n groupIndex,\n options?.userTransformer,\n );\n if (entity) {\n return { ...u, entity } as UserRepresentationWithEntity;\n }\n return null;\n }),\n );\n const filteredParsedUsers = parsedUsers.filter(\n (user): user is UserRepresentationWithEntity => user !== null,\n );\n\n logger.debug(`Set up group members and children information`);\n\n const userMap = new Map(\n filteredParsedUsers.map(user => [user.username, user.entity.metadata.name]),\n );\n\n const groupMap = new Map(\n filteredParsedGroups.map(group => [group.name, group.entity.metadata.name]),\n );\n\n const groups = filteredParsedGroups.map(g => {\n const entity = g.entity;\n entity.spec.members =\n g.entity.spec.members?.flatMap(m => userMap.get(m) ?? []) ?? [];\n entity.spec.children =\n g.entity.spec.children?.flatMap(c => groupMap.get(c) ?? []) ?? [];\n entity.spec.parent = groupMap.get(entity.spec.parent);\n return entity;\n });\n\n logger.info(\n `Prepared to ingest ${parsedUsers.length} users and ${groups.length} groups into the catalog from Keycloak`,\n );\n\n return { users: filteredParsedUsers.map(u => u.entity), groups };\n};\n"],"names":["noopGroupTransformer","KEYCLOAK_ID_ANNOTATION","KEYCLOAK_REALM_ANNOTATION","noopUserTransformer","KEYCLOAK_ENTITY_QUERY_SIZE","ensureTokenValid"],"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';\nimport { LimitFunction } from 'p-limit';\n\nimport { KeycloakProviderConfig } from './config';\nimport {\n KEYCLOAK_ENTITY_QUERY_SIZE,\n KEYCLOAK_ID_ANNOTATION,\n KEYCLOAK_REALM_ANNOTATION,\n KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT,\n} from './constants';\nimport { noopGroupTransformer, noopUserTransformer } from './transformers';\nimport {\n GroupRepresentationWithParent,\n GroupRepresentationWithParentAndEntity,\n GroupTransformer,\n UserRepresentationWithEntity,\n UserTransformer,\n} from './types';\nimport { ensureTokenValid } from './authenticate';\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 groupIndex: Map<string, string[]>,\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: groupIndex.get(user.username!) ?? [],\n },\n };\n\n return await transformer(entity, user, realm, keycloakGroups);\n};\n\nexport async function getEntities<T extends Users | Groups>(\n getEntitiesFn: () => Promise<T>,\n config: KeycloakProviderConfig,\n logger: LoggerService,\n limit: LimitFunction,\n entityQuerySize: number = KEYCLOAK_ENTITY_QUERY_SIZE,\n): Promise<Awaited<ReturnType<T['find']>>> {\n const entitiesAPI = await getEntitiesFn();\n const rawEntityCount = await entitiesAPI.count({ realm: config.realm });\n const entityCount =\n typeof rawEntityCount === 'number' ? rawEntityCount : rawEntityCount.count;\n\n const pageCount = Math.ceil(entityCount / entityQuerySize);\n const brief =\n config.briefRepresentation ?? KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT;\n\n // The next line acts like range in python\n const entityPromises = Array.from({ length: pageCount }, (_, i) =>\n limit(() =>\n getEntitiesFn().then(entities => {\n return entities\n .find({\n realm: config.realm,\n max: entityQuerySize,\n first: i * entityQuerySize,\n briefRepresentation: brief,\n })\n .then(ents => {\n logger.debug(\n `Importing keycloak entities batch with index ${i} from pages: ${pageCount}`,\n );\n return ents;\n })\n .catch(err => {\n logger.warn('Failed to retieve Keycloak entities.', err);\n return [];\n }) as ReturnType<T['find']>;\n }),\n ),\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 groupsAPI: () => Promise<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 groups = await groupsAPI();\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 kcAdminClient: KeycloakAdminClient,\n config: KeycloakProviderConfig,\n logger: LoggerService,\n topLevelGroups: GroupRepresentationWithParent[],\n) {\n const allGroups: GroupRepresentationWithParent[] = [];\n const brief =\n config.briefRepresentation ?? KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT;\n\n for (const group of topLevelGroups) {\n allGroups.push(group);\n\n if (group.subGroupCount! > 0) {\n await ensureTokenValid(kcAdminClient, config, logger);\n const subgroups = await kcAdminClient.groups.listSubGroups({\n parentId: group.id!,\n first: 0,\n max: group.subGroupCount,\n briefRepresentation: brief,\n });\n const subGroupResults = await processGroupsRecursively(\n kcAdminClient,\n config,\n logger,\n subgroups,\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 limit: LimitFunction,\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 async () => {\n await ensureTokenValid(client, config, logger);\n return client.users;\n },\n config,\n logger,\n limit,\n options?.userQuerySize,\n );\n logger.debug(`Fetched ${kUsers.length} users from Keycloak`);\n\n const topLevelKGroups = (await getEntities(\n async () => {\n await ensureTokenValid(client, config, logger);\n return client.groups;\n },\n config,\n logger,\n limit,\n options?.groupQuerySize,\n )) as GroupRepresentationWithParent[];\n logger.debug(`Fetched ${topLevelKGroups.length} groups from Keycloak`);\n\n let serverVersion: number;\n\n try {\n await ensureTokenValid(client, config, logger);\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 logger.debug(`Processing groups recursively`);\n if (isVersion23orHigher) {\n rawKGroups = await processGroupsRecursively(\n client,\n config,\n logger,\n topLevelKGroups,\n );\n } else {\n rawKGroups = topLevelKGroups.reduce(\n (acc, g) => acc.concat(...traverseGroups(g)),\n [] as GroupRepresentationWithParent[],\n );\n }\n\n logger.debug(`Fetching group members for keycloak groups and list subgroups`);\n const brief =\n config.briefRepresentation ?? KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT;\n\n const kGroups = await Promise.all(\n rawKGroups.map(g =>\n limit(async () => {\n g.members = await getAllGroupMembers(\n async () => {\n await ensureTokenValid(client, config, logger);\n return client.groups as Groups;\n },\n g.id!,\n config,\n options,\n );\n\n if (isVersion23orHigher) {\n if (g.subGroupCount! > 0) {\n await ensureTokenValid(client, config, logger);\n g.subGroups = await client.groups.listSubGroups({\n parentId: g.id!,\n first: 0,\n max: g.subGroupCount,\n briefRepresentation: brief,\n realm: config.realm,\n });\n }\n if (g.parentId) {\n await ensureTokenValid(client, config, logger);\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\n logger.debug(`Parsing groups`);\n const parsedGroups = await Promise.all(\n kGroups.map(async g => {\n // it is possible if fetch request failed\n if (!g) {\n return null;\n }\n const entity = await parseGroup(\n g,\n config.realm,\n options?.groupTransformer,\n );\n if (entity) {\n return { ...g, entity } as GroupRepresentationWithParentAndEntity;\n }\n return null;\n }),\n );\n const filteredParsedGroups = parsedGroups.filter(\n (group): group is GroupRepresentationWithParentAndEntity => group !== null,\n );\n\n const groupIndex = new Map<string, string[]>();\n filteredParsedGroups.forEach(group => {\n if (group.members) {\n group.members.forEach(member => {\n if (!groupIndex.has(member)) {\n groupIndex.set(member, []);\n }\n groupIndex.get(member)?.push(group.entity.metadata.name);\n });\n }\n });\n\n logger.debug('Parsing users');\n const parsedUsers = await Promise.all(\n kUsers.map(async u => {\n // it is possible if fetch request failed\n if (!u) {\n return null;\n }\n const entity = await parseUser(\n u,\n config.realm,\n filteredParsedGroups,\n groupIndex,\n options?.userTransformer,\n );\n if (entity) {\n return { ...u, entity } as UserRepresentationWithEntity;\n }\n return null;\n }),\n );\n const filteredParsedUsers = parsedUsers.filter(\n (user): user is UserRepresentationWithEntity => user !== null,\n );\n\n logger.debug(`Set up group members and children information`);\n\n const userMap = new Map(\n filteredParsedUsers.map(user => [user.username, user.entity.metadata.name]),\n );\n\n const groupMap = new Map(\n filteredParsedGroups.map(group => [group.name, group.entity.metadata.name]),\n );\n\n const groups = filteredParsedGroups.map(g => {\n const entity = g.entity;\n entity.spec.members =\n g.entity.spec.members?.flatMap(m => userMap.get(m) ?? []) ?? [];\n entity.spec.children =\n g.entity.spec.children?.flatMap(c => groupMap.get(c) ?? []) ?? [];\n entity.spec.parent = groupMap.get(entity.spec.parent);\n return entity;\n });\n\n logger.info(\n `Prepared to ingest ${parsedUsers.length} users and ${groups.length} groups into the catalog from Keycloak`,\n );\n\n return { users: filteredParsedUsers.map(u => u.entity), groups };\n};\n"],"names":["noopGroupTransformer","KEYCLOAK_ID_ANNOTATION","KEYCLOAK_REALM_ANNOTATION","noopUserTransformer","KEYCLOAK_ENTITY_QUERY_SIZE","KEYCLOAK_BRIEF_REPRESENTATION_DEFAULT","ensureTokenValid"],"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.5.2",
3
+ "version": "3.6.0",
4
4
  "description": "A Backend backend plugin for Keycloak",
5
5
  "main": "./dist/index.cjs.js",
6
6
  "types": "./dist/index.d.ts",
@@ -65,7 +65,7 @@
65
65
  "@backstage/plugin-catalog-backend": "^1.30.0",
66
66
  "@spotify/prettier-config": "^15.0.0",
67
67
  "@types/lodash": "4.17.15",
68
- "@types/uuid": "9.0.8",
68
+ "@types/uuid": "10.0.0",
69
69
  "deepmerge": "4.3.1",
70
70
  "open-cli": "^8.0.0"
71
71
  },