@backstage-community/plugin-catalog-backend-module-keycloak 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,171 @@
1
+ # Keycloak backend plugin for Backstage
2
+
3
+ The Keycloak backend plugin integrates Keycloak into Backstage.
4
+
5
+ ## Capabilities
6
+
7
+ The Keycloak backend plugin has the following capabilities:
8
+
9
+ - Synchronization of Keycloak users in a realm
10
+ - Synchronization of Keycloak groups and their users in a realm
11
+
12
+ ## For administrators
13
+
14
+ ### Installation
15
+
16
+ Install the Backstage package into the backend. When not integrating with a published package, clone the repository locally and add the Backstage as follows:
17
+
18
+ ```console
19
+ yarn workspace backend add @backstage-community/plugin-catalog-backend-module-keycloak
20
+ ```
21
+
22
+ ### Configuration
23
+
24
+ #### New Backend Configuration
25
+
26
+ 1. Add the following configuration to the `app-config.yaml` file. The default schedule is a frequency of 30 minutes and a timeout of 3 minutes, please configure the schedule in the `app-config.yaml` as per your requirement.
27
+
28
+ ```yaml title="app-config.yaml"
29
+ catalog:
30
+ providers:
31
+ keycloakOrg:
32
+ default:
33
+ baseUrl: https://<keycloak_host>
34
+ loginRealm: ${KEYCLOAK_REALM}
35
+ realm: ${KEYCLOAK_REALM}
36
+ clientId: ${KEYCLOAK_CLIENTID}
37
+ clientSecret: ${KEYCLOAK_CLIENTSECRET}
38
+ schedule: # Optional (defaults to the configurations below if not provided); same options as in TaskScheduleDefinition
39
+ # supports cron, ISO duration, "human duration" as used in code
40
+ frequency: { minutes: 30 } # Customize this to fit your needs
41
+ # supports ISO duration, "human duration" as used in code
42
+ timeout: { minutes: 3 } # Customize this to fit your needs
43
+ ```
44
+
45
+ 1. Register the plugin in the `packages/backend/src/index.ts` file:
46
+
47
+ ```ts title="packages/backend/src/index.ts"
48
+ const backend = createBackend();
49
+
50
+ /* highlight-add-next-line */
51
+ backend.add(
52
+ import('@backstage-community/plugin-catalog-backend-module-keycloak'),
53
+ );
54
+
55
+ backend.start();
56
+ ```
57
+
58
+ 1. Optional: To configure custom transformer function for user/group to mutate the entity generated by the catalog-backend-module-keycloak. Create a new backend module with the `yarn new` command and add your custom user and group transformers to the `keycloakTransformerExtensionPoint`. Then install this new backend module into your backstage backend. Below is an example of how the backend module can be defined:
59
+
60
+ ```ts title="plugins/<module-name>/src/module.ts"
61
+ /* highlight-add-start */
62
+ import {
63
+ GroupTransformer,
64
+ keycloakTransformerExtensionPoint,
65
+ UserTransformer,
66
+ } from '@backstage-community/plugin-catalog-backend-module-keycloak';
67
+
68
+ const customGroupTransformer: GroupTransformer = async (
69
+ entity,
70
+ realm,
71
+ groups,
72
+ ) => {
73
+ /* apply transformations */
74
+ return entity;
75
+ };
76
+ const customUserTransformer: UserTransformer = async (
77
+ entity,
78
+ user,
79
+ realm,
80
+ groups,
81
+ ) => {
82
+ /* apply transformations */
83
+ return entity;
84
+ };
85
+ /* highlight-add-end */
86
+
87
+ export const keycloakBackendModuleTransformer = createBackendModule({
88
+ pluginId: 'catalog',
89
+ moduleId: 'keycloak-transformer',
90
+ register(reg) {
91
+ reg.registerInit({
92
+ deps: {
93
+ /* highlight-add-start */
94
+ keycloak: keycloakTransformerExtensionPoint,
95
+ /* highlight-add-end */
96
+ },
97
+ /* highlight-add-start */
98
+ async init({ keycloak }) {
99
+ keycloak.setUserTransformer(customUserTransformer);
100
+ keycloak.setGroupTransformer(customGroupTransformer);
101
+ /* highlight-add-end */
102
+ },
103
+ });
104
+ },
105
+ });
106
+ ```
107
+
108
+ ***
109
+
110
+ **IMPORTANT**
111
+
112
+ The `pluginId` for the module **MUST** be set to `catalog` to match the `pluginId` of the `catalog-backend-module-keycloak` or else the module will fail to initialize.
113
+
114
+ ***
115
+
116
+ Communication between Backstage and Keycloak is enabled by using the Keycloak API. Username/password or client credentials are supported authentication methods.
117
+
118
+ The following table describes the parameters that you can configure to enable the plugin under `catalog.providers.keycloakOrg.<ENVIRONMENT_NAME>` object in the `app-config.yaml` file:
119
+
120
+ | Name | Description | Default Value | Required |
121
+ | ---------------- | ------------------------------------------------------------------ | ------------- | ---------------------------------------------------- |
122
+ | `baseUrl` | Location of the Keycloak server, such as `https://localhost:8443`. | "" | Yes |
123
+ | `realm` | Realm to synchronize | `master` | No |
124
+ | `loginRealm` | Realm used to authenticate | `master` | No |
125
+ | `username` | Username to authenticate | "" | Yes if using password based authentication |
126
+ | `password` | Password to authenticate | "" | Yes if using password based authentication |
127
+ | `clientId` | Client ID to authenticate | "" | Yes if using client credentials based authentication |
128
+ | `clientSecret` | Client Secret to authenticate | "" | Yes if using client credentials based authentication |
129
+ | `userQuerySize` | Number of users to query at a time | `100` | No |
130
+ | `groupQuerySize` | Number of groups to query at a time | `100` | No |
131
+
132
+ When using client credentials, the access type must be set to `confidential` and service accounts must be enabled. You must also add the following roles from the `realm-management` client role:
133
+
134
+ - `query-groups`
135
+ - `query-users`
136
+ - `view-users`
137
+
138
+ ### Limitations
139
+
140
+ If you have self-signed or corporate certificate issues, you can set the following environment variable before starting Backstage:
141
+
142
+ `NODE_TLS_REJECT_UNAUTHORIZED=0`
143
+
144
+ ---
145
+
146
+ **NOTE**
147
+ The solution of setting the `NODE_TLS_REJECT_UNAUTHORIZED` environment variable is not recommended.
148
+
149
+ ---
150
+
151
+ ## For users
152
+
153
+ ### Imported users and groups in Backstage using Keycloak plugin
154
+
155
+ After configuring the plugin successfully, the plugin imports the users and groups each time when started.
156
+
157
+ After the first import is complete, you can select **User** to list the users from the catalog page:
158
+
159
+ ![catalog-list](./images/users.jpg)
160
+
161
+ You can see the list of users on the page:
162
+
163
+ ![user-list](./images/user-list.jpg)
164
+
165
+ When you select a user, you can see the information imported from Keycloak:
166
+
167
+ ![user-profile](./images/user2.jpg)
168
+
169
+ You can also select a group, view the list, and select or view the information imported from Keycloak for a group:
170
+
171
+ ![group-profile](./images/group1.jpg)
@@ -0,0 +1,11 @@
1
+ enabled:
2
+ keycloak: true
3
+ catalog:
4
+ providers:
5
+ keycloakOrg:
6
+ default:
7
+ baseUrl: '${KEYCLOAK_BASE_URL}'
8
+ loginRealm: '${KEYCLOAK_LOGIN_REALM}'
9
+ realm: '${KEYCLOAK_REALM}'
10
+ clientId: '${KEYCLOAK_CLIENT_ID}'
11
+ clientSecret: '${KEYCLOAK_CLIENT_SECRET}'
package/config.d.ts ADDED
@@ -0,0 +1,88 @@
1
+ /*
2
+ * Copyright 2024 The Backstage Authors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import type { SchedulerServiceTaskScheduleDefinitionConfig } from '@backstage/backend-plugin-api';
17
+
18
+ export interface Config {
19
+ catalog?: {
20
+ providers?: {
21
+ keycloakOrg?: {
22
+ [key: string]: {
23
+ /**
24
+ * KeycloakOrgConfig
25
+ */
26
+ /**
27
+ * Location of the Keycloak instance
28
+ */
29
+ baseUrl: string;
30
+ /**
31
+ * Keycloak realm name. This realm is scraped and entities are
32
+ */
33
+ realm?: string;
34
+ /**
35
+ * Keycloak realm name. This realm is used for authentication using the credentials below.
36
+ */
37
+ loginRealm?: string;
38
+ /**
39
+ * The number of users to query at a time.
40
+ * @defaultValue 100
41
+ * @remarks
42
+ * This is a performance optimization to avoid querying too many users at once.
43
+ * @see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_users_resource
44
+ */
45
+ userQuerySize?: number;
46
+ /**
47
+ * The number of groups to query at a time.
48
+ * @defaultValue 100
49
+ * @remarks
50
+ * This is a performance optimization to avoid querying too many groups at once.
51
+ * @see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_groups_resource
52
+ */
53
+ groupQuerySize?: number;
54
+ schedule?: SchedulerServiceTaskScheduleDefinitionConfig;
55
+ } & (
56
+ | {
57
+ /**
58
+ * KeycloakClientCredentials
59
+ */
60
+ /**
61
+ * Keycloak credentials. Use together with "password".
62
+ */
63
+ username: string;
64
+ /**
65
+ * Keycloak credentials. Use together with "username".
66
+ * @visibility secret
67
+ */
68
+ password: string;
69
+ }
70
+ | {
71
+ /**
72
+ * KeycloakClientCredentials
73
+ */
74
+ /**
75
+ * Keycloak credentials. Use together with "clientSecret".
76
+ */
77
+ clientId: string;
78
+ /**
79
+ * Keycloak credentials. Use together with "clientId".
80
+ * @visibility secret
81
+ */
82
+ clientSecret: string;
83
+ }
84
+ );
85
+ };
86
+ };
87
+ };
88
+ }
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+
5
+ const keycloakTransformerExtensionPoint = backendPluginApi.createExtensionPoint({
6
+ id: "keycloak.transformer"
7
+ });
8
+
9
+ exports.keycloakTransformerExtensionPoint = keycloakTransformerExtensionPoint;
10
+ //# sourceMappingURL=extensions.cjs.js.map
@@ -0,0 +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;;;;"}
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var KeycloakOrgEntityProvider = require('./providers/KeycloakOrgEntityProvider.cjs.js');
6
+ var transformers = require('./lib/transformers.cjs.js');
7
+ var extensions = require('./extensions.cjs.js');
8
+ var catalogModuleKeycloakEntityProvider = require('./module/catalogModuleKeycloakEntityProvider.cjs.js');
9
+
10
+
11
+
12
+ exports.KeycloakOrgEntityProvider = KeycloakOrgEntityProvider.KeycloakOrgEntityProvider;
13
+ exports.noopGroupTransformer = transformers.noopGroupTransformer;
14
+ exports.noopUserTransformer = transformers.noopUserTransformer;
15
+ exports.sanitizeEmailTransformer = transformers.sanitizeEmailTransformer;
16
+ exports.keycloakTransformerExtensionPoint = extensions.keycloakTransformerExtensionPoint;
17
+ exports.default = catalogModuleKeycloakEntityProvider.catalogModuleKeycloakEntityProvider;
18
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
@@ -0,0 +1,219 @@
1
+ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
+ import { SchedulerServiceTaskScheduleDefinition, SchedulerServiceTaskRunner, SchedulerService, LoggerService } from '@backstage/backend-plugin-api';
3
+ import { Config } from '@backstage/config';
4
+ import { EntityProvider, EntityProviderConnection } from '@backstage/plugin-catalog-node';
5
+ import { GroupEntity, UserEntity } from '@backstage/catalog-model';
6
+ import GroupRepresentation from '@keycloak/keycloak-admin-client/lib/defs/groupRepresentation';
7
+ import UserRepresentation from '@keycloak/keycloak-admin-client/lib/defs/userRepresentation';
8
+
9
+ interface GroupRepresentationWithParent extends GroupRepresentation {
10
+ parentId?: string;
11
+ parent?: string;
12
+ members?: string[];
13
+ }
14
+ interface GroupRepresentationWithParentAndEntity extends GroupRepresentationWithParent {
15
+ entity: GroupEntity;
16
+ }
17
+ interface UserRepresentationWithEntity extends UserRepresentation {
18
+ entity: UserEntity;
19
+ }
20
+ /**
21
+ * Customize the ingested User entity
22
+ *
23
+ * @public
24
+ *
25
+ * @param {UserEntity} entity The output of the default parser
26
+ * @param {UserRepresentation} user Keycloak user representation
27
+ * @param {string} realm Realm name
28
+ * @param {GroupRepresentationWithParentAndEntity[]} groups Data about available groups (can be used to create additional relationships)
29
+ *
30
+ * @returns {Promise<UserEntity | undefined>} Resolve to a modified `UserEntity` object that will be ingested into the catalog or resolve to `undefined` to reject the entity
31
+ */
32
+ type UserTransformer = (entity: UserEntity, user: UserRepresentation, realm: string, groups: GroupRepresentationWithParentAndEntity[]) => Promise<UserEntity | undefined>;
33
+ /**
34
+ * Customize the ingested Group entity
35
+ *
36
+ * @public
37
+ *
38
+ * @param {GroupEntity} entity The output of the default parser
39
+ * @param {GroupRepresentation} group Keycloak group representation
40
+ * @param {string} realm Realm name
41
+ *
42
+ * @returns {Promise<GroupEntity | undefined>} Resolve to a modified `GroupEntity` object that will be ingested into the catalog or resolve to `undefined` to reject the entity
43
+ */
44
+ type GroupTransformer = (entity: GroupEntity, group: GroupRepresentation, realm: string) => Promise<GroupEntity | undefined>;
45
+
46
+ /**
47
+ * The configuration parameters for a single Keycloak provider.
48
+ *
49
+ * @public
50
+ */
51
+ type KeycloakProviderConfig = {
52
+ /**
53
+ * Identifier of the provider which will be used i.e. at the location key for ingested entities.
54
+ */
55
+ id: string;
56
+ /**
57
+ * The Keycloak base URL
58
+ */
59
+ baseUrl: string;
60
+ /**
61
+ * The username to use for authenticating requests
62
+ * If specified, password must also be specified
63
+ */
64
+ username?: string;
65
+ /**
66
+ * The password to use for authenticating requests
67
+ * If specified, username must also be specified
68
+ */
69
+ password?: string;
70
+ /**
71
+ * The clientId to use for authenticating requests
72
+ * If specified, clientSecret must also be specified
73
+ */
74
+ clientId?: string;
75
+ /**
76
+ * The clientSecret to use for authenticating requests
77
+ * If specified, clientId must also be specified
78
+ */
79
+ clientSecret?: string;
80
+ /**
81
+ * name of the Keycloak realm
82
+ */
83
+ realm: string;
84
+ /**
85
+ * name of the Keycloak login realm
86
+ */
87
+ loginRealm?: string;
88
+ /**
89
+ * Schedule configuration for refresh tasks.
90
+ */
91
+ schedule?: SchedulerServiceTaskScheduleDefinition;
92
+ /**
93
+ * The number of users to query at a time.
94
+ * @defaultValue 100
95
+ * @remarks
96
+ * This is a performance optimization to avoid querying too many users at once.
97
+ * @see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_users_resource
98
+ */
99
+ userQuerySize?: number;
100
+ /**
101
+ * The number of groups to query at a time.
102
+ * @defaultValue 100
103
+ * @remarks
104
+ * This is a performance optimization to avoid querying too many groups at once.
105
+ * @see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_groups_resource
106
+ */
107
+ groupQuerySize?: number;
108
+ };
109
+
110
+ /**
111
+ * Options for {@link KeycloakOrgEntityProvider}.
112
+ *
113
+ * @public
114
+ */
115
+ interface KeycloakOrgEntityProviderOptions {
116
+ /**
117
+ * A unique, stable identifier for this provider.
118
+ *
119
+ * @example "production"
120
+ */
121
+ id: string;
122
+ /**
123
+ * The refresh schedule to use.
124
+ * @remarks
125
+ *
126
+ * You can pass in the result of
127
+ * {@link @backstage/backend-plugin-api#SchedulerService.createScheduledTaskRunner}
128
+ * to enable automatic scheduling of tasks.
129
+ */
130
+ schedule?: SchedulerServiceTaskRunner;
131
+ /**
132
+ * Scheduler used to schedule refreshes based on
133
+ * the schedule config.
134
+ */
135
+ scheduler?: SchedulerService;
136
+ /**
137
+ * The logger to use.
138
+ */
139
+ logger: LoggerService;
140
+ /**
141
+ * The function that transforms a user entry in LDAP to an entity.
142
+ */
143
+ userTransformer?: UserTransformer;
144
+ /**
145
+ * The function that transforms a group entry in LDAP to an entity.
146
+ */
147
+ groupTransformer?: GroupTransformer;
148
+ }
149
+ /**
150
+ * Ingests org data (users and groups) from GitHub.
151
+ *
152
+ * @public
153
+ */
154
+ declare class KeycloakOrgEntityProvider implements EntityProvider {
155
+ private options;
156
+ private connection?;
157
+ private scheduleFn?;
158
+ static fromConfig(deps: {
159
+ config: Config;
160
+ logger: LoggerService;
161
+ }, options: ({
162
+ schedule: SchedulerServiceTaskRunner;
163
+ } | {
164
+ scheduler: SchedulerService;
165
+ }) & {
166
+ userTransformer?: UserTransformer;
167
+ groupTransformer?: GroupTransformer;
168
+ }): KeycloakOrgEntityProvider[];
169
+ constructor(options: {
170
+ id: string;
171
+ provider: KeycloakProviderConfig;
172
+ logger: LoggerService;
173
+ taskRunner: SchedulerServiceTaskRunner;
174
+ userTransformer?: UserTransformer;
175
+ groupTransformer?: GroupTransformer;
176
+ });
177
+ getProviderName(): string;
178
+ connect(connection: EntityProviderConnection): Promise<void>;
179
+ /**
180
+ * Runs one complete ingestion loop. Call this method regularly at some
181
+ * appropriate cadence.
182
+ */
183
+ read(options?: {
184
+ logger?: LoggerService;
185
+ }): Promise<void>;
186
+ schedule(taskRunner: SchedulerServiceTaskRunner): void;
187
+ }
188
+
189
+ declare const noopGroupTransformer: GroupTransformer;
190
+ declare const noopUserTransformer: UserTransformer;
191
+ /**
192
+ * User transformer that sanitizes .metadata.name from email address to a valid name
193
+ */
194
+ declare const sanitizeEmailTransformer: UserTransformer;
195
+
196
+ /**
197
+ * An extension point that exposes the ability to implement user and group transformer functions for keycloak.
198
+ *
199
+ * @public
200
+ */
201
+ declare const keycloakTransformerExtensionPoint: _backstage_backend_plugin_api.ExtensionPoint<KeycloakTransformerExtensionPoint>;
202
+ /**
203
+ * The interface for {@link keycloakTransformerExtensionPoint}.
204
+ *
205
+ * @public
206
+ */
207
+ type KeycloakTransformerExtensionPoint = {
208
+ setUserTransformer(userTransformer: UserTransformer): void;
209
+ setGroupTransformer(groupTransformer: GroupTransformer): void;
210
+ };
211
+
212
+ /**
213
+ * Registers the `KeycloakEntityProvider` with the catalog processing extension point.
214
+ *
215
+ * @alpha
216
+ */
217
+ declare const catalogModuleKeycloakEntityProvider: _backstage_backend_plugin_api.BackendFeature;
218
+
219
+ export { type GroupRepresentationWithParent, type GroupRepresentationWithParentAndEntity, type GroupTransformer, KeycloakOrgEntityProvider, type KeycloakOrgEntityProviderOptions, type KeycloakTransformerExtensionPoint, type UserRepresentationWithEntity, type UserTransformer, catalogModuleKeycloakEntityProvider as default, keycloakTransformerExtensionPoint, noopGroupTransformer, noopUserTransformer, sanitizeEmailTransformer };
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ var backendPluginApi = require('@backstage/backend-plugin-api');
4
+ var errors = require('@backstage/errors');
5
+
6
+ const readProviderConfig = (id, providerConfigInstance) => {
7
+ const baseUrl = providerConfigInstance.getString("baseUrl");
8
+ const realm = providerConfigInstance.getOptionalString("realm") ?? "master";
9
+ const loginRealm = providerConfigInstance.getOptionalString("loginRealm") ?? "master";
10
+ const username = providerConfigInstance.getOptionalString("username");
11
+ const password = providerConfigInstance.getOptionalString("password");
12
+ const clientId = providerConfigInstance.getOptionalString("clientId");
13
+ const clientSecret = providerConfigInstance.getOptionalString("clientSecret");
14
+ const userQuerySize = providerConfigInstance.getOptionalNumber("userQuerySize");
15
+ const groupQuerySize = providerConfigInstance.getOptionalNumber("groupQuerySize");
16
+ if (clientId && !clientSecret) {
17
+ throw new errors.InputError(
18
+ `clientSecret must be provided when clientId is defined.`
19
+ );
20
+ }
21
+ if (clientSecret && !clientId) {
22
+ throw new errors.InputError(
23
+ `clientId must be provided when clientSecret is defined.`
24
+ );
25
+ }
26
+ if (username && !password) {
27
+ throw new errors.InputError(`password must be provided when username is defined.`);
28
+ }
29
+ if (password && !username) {
30
+ throw new errors.InputError(`username must be provided when password is defined.`);
31
+ }
32
+ const schedule = providerConfigInstance.has("schedule") ? backendPluginApi.readSchedulerServiceTaskScheduleDefinitionFromConfig(
33
+ providerConfigInstance.getConfig("schedule")
34
+ ) : void 0;
35
+ return {
36
+ id,
37
+ baseUrl,
38
+ loginRealm,
39
+ realm,
40
+ username,
41
+ password,
42
+ clientId,
43
+ clientSecret,
44
+ schedule,
45
+ userQuerySize,
46
+ groupQuerySize
47
+ };
48
+ };
49
+ const readProviderConfigs = (config) => {
50
+ const providersConfig = config.getOptionalConfig(
51
+ "catalog.providers.keycloakOrg"
52
+ );
53
+ if (!providersConfig) {
54
+ return [];
55
+ }
56
+ return providersConfig.keys().map((id) => {
57
+ const providerConfigInstance = providersConfig.getConfig(id);
58
+ return readProviderConfig(id, providerConfigInstance);
59
+ });
60
+ };
61
+
62
+ exports.readProviderConfigs = readProviderConfigs;
63
+ //# sourceMappingURL=config.cjs.js.map
@@ -0,0 +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;;;;"}
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ const KEYCLOAK_ID_ANNOTATION = "keycloak.org/id";
4
+ const KEYCLOAK_REALM_ANNOTATION = "keycloak.org/realm";
5
+ const KEYCLOAK_ENTITY_QUERY_SIZE = 100;
6
+
7
+ exports.KEYCLOAK_ENTITY_QUERY_SIZE = KEYCLOAK_ENTITY_QUERY_SIZE;
8
+ exports.KEYCLOAK_ID_ANNOTATION = KEYCLOAK_ID_ANNOTATION;
9
+ exports.KEYCLOAK_REALM_ANNOTATION = KEYCLOAK_REALM_ANNOTATION;
10
+ //# sourceMappingURL=constants.cjs.js.map
@@ -0,0 +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;;;;;;"}