@backstage/plugin-catalog-backend-module-msgraph 0.3.3 → 0.4.0-next.2

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,123 @@
1
1
  # @backstage/plugin-catalog-backend-module-msgraph
2
2
 
3
+ ## 0.4.0-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - a70869e775: Updated dependency `msw` to `^0.43.0`.
8
+ - Updated dependencies
9
+ - @backstage/plugin-catalog-backend@1.3.0-next.3
10
+ - @backstage/backend-tasks@0.3.3-next.3
11
+ - @backstage/catalog-model@1.1.0-next.3
12
+
13
+ ## 0.4.0-next.1
14
+
15
+ ### Minor Changes
16
+
17
+ - a145672f0f: Align `msgraph` plugin's entity provider config with other providers. **Deprecated** entity processor as well as previous config.
18
+
19
+ You will see warning at the log output until you migrate to the new setup.
20
+ All deprecated parts will be removed eventually after giving some time to migrate.
21
+
22
+ Please find information on how to migrate your current setup to the new one below.
23
+
24
+ **Migration Guide:**
25
+
26
+ There were two different way on how to use the msgraph plugin: processor or provider.
27
+
28
+ Previous registration for the processor:
29
+
30
+ ```typescript
31
+ // packages/backend/src/plugins/catalog.ts
32
+ builder.addProcessor(
33
+ MicrosoftGraphOrgReaderProcessor.fromConfig(env.config, {
34
+ logger: env.logger,
35
+ // [...]
36
+ }),
37
+ );
38
+ ```
39
+
40
+ Previous registration when using the provider:
41
+
42
+ ```typescript
43
+ // packages/backend/src/plugins/catalog.ts
44
+ builder.addEntityProvider(
45
+ MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
46
+ id: 'https://graph.microsoft.com/v1.0',
47
+ target: 'https://graph.microsoft.com/v1.0',
48
+ logger: env.logger,
49
+ schedule: env.scheduler.createScheduledTaskRunner({
50
+ frequency: { minutes: 30 },
51
+ timeout: { minutes: 3 },
52
+ }),
53
+ // [...]
54
+ }),
55
+ );
56
+ ```
57
+
58
+ Previous configuration as used for both:
59
+
60
+ ```yaml
61
+ # app-config.yaml
62
+ catalog:
63
+ processors:
64
+ microsoftGraphOrg:
65
+ providers:
66
+ - target: https://graph.microsoft.com/v1.0
67
+ # [...]
68
+ ```
69
+
70
+ **Replacement:**
71
+
72
+ Please check https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-msgraph/README.md for the complete documentation of all configuration options (config as well as registration of the provider).
73
+
74
+ ```yaml
75
+ # app-config.yaml
76
+ catalog:
77
+ providers:
78
+ microsoftGraphOrg:
79
+ # In case you used the deprecated configuration with the entity provider
80
+ # using the value of `target` will keep the same location key for all
81
+ providerId: # some stable ID which will be used as part of the location key for all ingested data
82
+ target: https://graph.microsoft.com/v1.0
83
+ # [...]
84
+ ```
85
+
86
+ ```typescript
87
+ // packages/backend/src/plugins/catalog.ts
88
+ builder.addEntityProvider(
89
+ MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
90
+ logger: env.logger,
91
+ schedule: env.scheduler.createScheduledTaskRunner({
92
+ frequency: { minutes: 30 },
93
+ timeout: { minutes: 3 },
94
+ }),
95
+ // [...]
96
+ }),
97
+ );
98
+ ```
99
+
100
+ In case you've used multiple entity providers before
101
+ **and** you had different transformers for each of them
102
+ you can provide these directly at the one `fromConfig` call
103
+ by passing a Record with the provider ID as key.
104
+
105
+ ### Patch Changes
106
+
107
+ - Updated dependencies
108
+ - @backstage/catalog-model@1.1.0-next.2
109
+ - @backstage/backend-tasks@0.3.3-next.2
110
+ - @backstage/plugin-catalog-backend@1.2.1-next.2
111
+
112
+ ## 0.3.4-next.0
113
+
114
+ ### Patch Changes
115
+
116
+ - Updated dependencies
117
+ - @backstage/catalog-model@1.1.0-next.0
118
+ - @backstage/backend-tasks@0.3.3-next.0
119
+ - @backstage/plugin-catalog-backend@1.2.1-next.0
120
+
3
121
  ## 0.3.3
4
122
 
5
123
  ### Patch Changes
package/README.md CHANGED
@@ -1,85 +1,86 @@
1
1
  # Catalog Backend Module for Microsoft Graph
2
2
 
3
- This is an extension module to the `plugin-catalog-backend` plugin, providing a
4
- `MicrosoftGraphOrgReaderProcessor` and a `MicrosoftGraphOrgEntityProvider` that
5
- can be used to ingest organization data from the Microsoft Graph API. This
6
- processor is useful if you want to import users and groups from Azure Active
7
- Directory or Office 365.
3
+ This is an extension module to the `plugin-catalog-backend` plugin, providing a `MicrosoftGraphOrgEntityProvider`
4
+ that can be used to ingest organization data from the Microsoft Graph API.
5
+ This provider is useful if you want to import users and groups from Azure Active Directory or Office 365.
8
6
 
9
7
  ## Getting Started
10
8
 
11
- First you need to decide whether you want to use an [entity provider or a processor](https://backstage.io/docs/features/software-catalog/life-of-an-entity#stitching) to ingest the organization data.
12
- If you want groups and users deleted from the source to be automatically deleted
13
- from Backstage, choose the entity provider.
14
-
15
9
  1. Create or use an existing App registration in the [Microsoft Azure Portal](https://portal.azure.com/).
16
10
  The App registration requires at least the API permissions `Group.Read.All`,
17
11
  `GroupMember.Read.All`, `User.Read` and `User.Read.All` for Microsoft Graph
18
12
  (if you still run into errors about insufficient privileges, add
19
13
  `Team.ReadBasic.All` and `TeamMember.Read.All` too).
20
14
 
21
- 2. Configure the processor or entity provider:
15
+ 2. Configure the entity provider:
22
16
 
23
17
  ```yaml
24
18
  # app-config.yaml
25
19
  catalog:
26
- processors:
20
+ providers:
27
21
  microsoftGraphOrg:
28
- providers:
29
- - target: https://graph.microsoft.com/v1.0
30
- authority: https://login.microsoftonline.com
31
- # If you don't know you tenantId, you can use Microsoft Graph Explorer
32
- # to query it
33
- tenantId: ${MICROSOFT_GRAPH_TENANT_ID}
34
- # Client Id and Secret can be created under Certificates & secrets in
35
- # the App registration in the Microsoft Azure Portal.
36
- clientId: ${MICROSOFT_GRAPH_CLIENT_ID}
37
- clientSecret: ${MICROSOFT_GRAPH_CLIENT_SECRET_TOKEN}
38
- # Optional mode for querying which defaults to "basic".
39
- # By default, the Microsoft Graph API only provides the basic feature set
40
- # for querying. Certain features are limited to advanced querying capabilities.
41
- # (See https://docs.microsoft.com/en-us/graph/aad-advanced-queries)
42
- queryMode: basic # basic | advanced
22
+ providerId:
23
+ target: https://graph.microsoft.com/v1.0
24
+ authority: https://login.microsoftonline.com
25
+ # If you don't know you tenantId, you can use Microsoft Graph Explorer
26
+ # to query it
27
+ tenantId: ${MICROSOFT_GRAPH_TENANT_ID}
28
+ # Client Id and Secret can be created under Certificates & secrets in
29
+ # the App registration in the Microsoft Azure Portal.
30
+ clientId: ${MICROSOFT_GRAPH_CLIENT_ID}
31
+ clientSecret: ${MICROSOFT_GRAPH_CLIENT_SECRET_TOKEN}
32
+ # Optional mode for querying which defaults to "basic".
33
+ # By default, the Microsoft Graph API only provides the basic feature set
34
+ # for querying. Certain features are limited to advanced querying capabilities.
35
+ # (See https://docs.microsoft.com/en-us/graph/aad-advanced-queries)
36
+ queryMode: basic # basic | advanced
37
+ # Optional configuration block
38
+ user:
43
39
  # Optional parameter to include the expanded resource or collection referenced
44
40
  # by a single relationship (navigation property) in your results.
45
41
  # Only one relationship can be expanded in a single request.
46
42
  # See https://docs.microsoft.com/en-us/graph/query-parameters#expand-parameter
47
43
  # Can be combined with userGroupMember[...] instead of userFilter.
48
- userExpand: manager
44
+ expand: manager
49
45
  # Optional filter for user, see Microsoft Graph API for the syntax
50
46
  # See https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0#properties
51
47
  # and for the syntax https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter
52
48
  # This and userGroupMemberFilter are mutually exclusive, only one can be specified
53
- userFilter: accountEnabled eq true and userType eq 'member'
49
+ filter: accountEnabled eq true and userType eq 'member'
50
+ # Optional configuration block
51
+ userGroupMember:
54
52
  # Optional filter for users, use group membership to get users.
55
53
  # (Filtered groups and fetch their members.)
56
54
  # This and userFilter are mutually exclusive, only one can be specified
57
55
  # See https://docs.microsoft.com/en-us/graph/search-query-parameter
58
- userGroupMemberFilter: "displayName eq 'Backstage Users'"
56
+ filter: "displayName eq 'Backstage Users'"
57
+ # Optional search for users, use group membership to get users.
58
+ # (Search for groups and fetch their members.)
59
+ # This and userFilter are mutually exclusive, only one can be specified
60
+ search: '"description:One" AND ("displayName:Video" OR "displayName:Drive")'
61
+ # Optional configuration block
62
+ group:
59
63
  # Optional parameter to include the expanded resource or collection referenced
60
64
  # by a single relationship (navigation property) in your results.
61
65
  # Only one relationship can be expanded in a single request.
62
66
  # See https://docs.microsoft.com/en-us/graph/query-parameters#expand-parameter
63
67
  # Can be combined with userGroupMember[...] instead of userFilter.
64
- groupExpand: member
65
- # Optional search for users, use group membership to get users.
66
- # (Search for groups and fetch their members.)
67
- # This and userFilter are mutually exclusive, only one can be specified
68
- userGroupMemberSearch: '"description:One" AND ("displayName:Video" OR "displayName:Drive")'
68
+ expand: member
69
69
  # Optional filter for group, see Microsoft Graph API for the syntax
70
70
  # See https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties
71
- groupFilter: securityEnabled eq false and mailEnabled eq true and groupTypes/any(c:c+eq+'Unified')
71
+ filter: securityEnabled eq false and mailEnabled eq true and groupTypes/any(c:c+eq+'Unified')
72
72
  # Optional search for groups, see Microsoft Graph API for the syntax
73
73
  # See https://docs.microsoft.com/en-us/graph/search-query-parameter
74
- groupSearch: '"description:One" AND ("displayName:Video" OR "displayName:Drive")'
75
- # Optional select for groups, this will allow you work with schemaExtensions in order to add extra information to your groups that can be used on you custom groupTransformers
74
+ search: '"description:One" AND ("displayName:Video" OR "displayName:Drive")'
75
+ # Optional select for groups, this will allow you work with schemaExtensions
76
+ # in order to add extra information to your groups that can be used on you custom groupTransformers
76
77
  # See https://docs.microsoft.com/en-us/graph/api/resources/schemaextension?view=graph-rest-1.0
77
- groupSelect: ['id', 'displayName', 'description']
78
+ select: ['id', 'displayName', 'description']
78
79
  ```
79
80
 
80
- `userFilter` and `userGroupMemberFilter` are mutually exclusive, only one can be provided. If both are provided, an error will be thrown.
81
+ `user.filter` and `userGroupMember.filter` are mutually exclusive, only one can be provided. If both are provided, an error will be thrown.
81
82
 
82
- By default, all users are loaded. If you want to filter users based on their attributes, use `userFilter`. `userGroupMemberFilter` can be used if you want to load users based on their group membership.
83
+ By default, all users are loaded. If you want to filter users based on their attributes, use `user.filter`. `userGroupMember.filter` can be used if you want to load users based on their group membership.
83
84
 
84
85
  3. The package is not installed by default, therefore you have to add a
85
86
  dependency to `@backstage/plugin-catalog-backend-module-msgraph` to your
@@ -90,15 +91,12 @@ By default, all users are loaded. If you want to filter users based on their att
90
91
  yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-msgraph
91
92
  ```
92
93
 
93
- ### Using the Entity Provider
94
-
95
94
  4. The `MicrosoftGraphOrgEntityProvider` is not registered by default, so you
96
95
  have to register it in the catalog plugin. Pass the target to reference a
97
96
  provider from the configuration.
98
97
 
99
98
  ```diff
100
99
  // packages/backend/src/plugins/catalog.ts
101
- +import { Duration } from 'luxon';
102
100
  +import { MicrosoftGraphOrgEntityProvider } from '@backstage/plugin-catalog-backend-module-msgraph';
103
101
 
104
102
  export default async function createPlugin(
@@ -106,53 +104,21 @@ yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-msgraph
106
104
  ): Promise<Router> {
107
105
  const builder = await CatalogBuilder.create(env);
108
106
 
109
- + // The target parameter below needs to match one of the providers' target
110
- + // value specified in your app-config (see above).
111
107
  + builder.addEntityProvider(
112
108
  + MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
113
- + id: 'production',
114
- + target: 'https://graph.microsoft.com/v1.0',
115
109
  + logger: env.logger,
116
110
  + schedule: env.scheduler.createScheduledTaskRunner({
117
- + frequency: Duration.fromObject({ minutes: 5 }),
118
- + timeout: Duration.fromObject({ minutes: 3 }),
111
+ + frequency: { minutes: 5 },
112
+ + timeout: { minutes: 3 },
119
113
  + }),
120
114
  + }),
121
115
  + );
122
116
  ```
123
117
 
124
- ### Using the Processor
125
-
126
- 4. The `MicrosoftGraphOrgReaderProcessor` is not registered by default, so you
127
- have to register it in the catalog plugin:
128
-
129
- ```typescript
130
- // packages/backend/src/plugins/catalog.ts
131
- builder.addProcessor(
132
- MicrosoftGraphOrgReaderProcessor.fromConfig(env.config, {
133
- logger: env.logger,
134
- }),
135
- );
136
- ```
137
-
138
- 5. Add a location that ingests from Microsoft Graph:
139
-
140
- ```yaml
141
- # app-config.yaml
142
- catalog:
143
- locations:
144
- - type: microsoft-graph-org
145
- target: https://graph.microsoft.com/v1.0
146
- rules:
147
- - allow: [Group, User]
148
-
149
- ```
150
-
151
118
  ## Customize the Processor or Entity Provider
152
119
 
153
- In case you want to customize the ingested entities, both the `MicrosoftGraphOrgReaderProcessor`
154
- and the `MicrosoftGraphOrgEntityProvider` allows to pass transformers for users,
155
- groups and the organization.
120
+ In case you want to customize the ingested entities, the `MicrosoftGraphOrgEntityProvider`
121
+ allows to pass transformers for users, groups and the organization.
156
122
 
157
123
  1. Create a transformer:
158
124
 
@@ -179,13 +145,17 @@ export async function myGroupTransformer(
179
145
  }
180
146
  ```
181
147
 
182
- 2. Configure the processor with the transformer:
148
+ 2. Add the transformer:
183
149
 
184
- ```ts
185
- builder.addProcessor(
186
- MicrosoftGraphOrgReaderProcessor.fromConfig(env.config, {
187
- logger: env.logger,
188
- groupTransformer: myGroupTransformer,
189
- }),
190
- );
150
+ ```diff
151
+ builder.addEntityProvider(
152
+ MicrosoftGraphOrgEntityProvider.fromConfig(env.config, {
153
+ logger: env.logger,
154
+ schedule: env.scheduler.createScheduledTaskRunner({
155
+ frequency: { minutes: 5 },
156
+ timeout: { minutes: 3 },
157
+ }),
158
+ + groupTransformer: myGroupTransformer,
159
+ }),
160
+ );
191
161
  ```
package/config.d.ts CHANGED
@@ -25,6 +25,7 @@ export interface Config {
25
25
  processors?: {
26
26
  /**
27
27
  * MicrosoftGraphOrgReaderProcessor configuration
28
+ * @deprecated Use `catalog.providers.microsoftGraphOrg` instead.
28
29
  */
29
30
  microsoftGraphOrg?: {
30
31
  /**
@@ -102,5 +103,173 @@ export interface Config {
102
103
  }>;
103
104
  };
104
105
  };
106
+ /**
107
+ * List of provider-specific options and attributes
108
+ */
109
+ providers?: {
110
+ /**
111
+ * MicrosoftGraphOrgEntityProvider configuration.
112
+ */
113
+ microsoftGraphOrg?:
114
+ | {
115
+ /**
116
+ * The prefix of the target that this matches on, e.g.
117
+ * "https://graph.microsoft.com/v1.0", with no trailing slash.
118
+ */
119
+ target: string;
120
+ /**
121
+ * The auth authority used.
122
+ *
123
+ * Default value "https://login.microsoftonline.com"
124
+ */
125
+ authority?: string;
126
+ /**
127
+ * The tenant whose org data we are interested in.
128
+ */
129
+ tenantId: string;
130
+ /**
131
+ * The OAuth client ID to use for authenticating requests.
132
+ */
133
+ clientId: string;
134
+ /**
135
+ * The OAuth client secret to use for authenticating requests.
136
+ *
137
+ * @visibility secret
138
+ */
139
+ clientSecret: string;
140
+
141
+ user?: {
142
+ /**
143
+ * The "expand" argument to apply to users.
144
+ *
145
+ * E.g. "manager".
146
+ */
147
+ expand?: string;
148
+ /**
149
+ * The filter to apply to extract users.
150
+ *
151
+ * E.g. "accountEnabled eq true and userType eq 'member'"
152
+ */
153
+ filter?: string;
154
+ };
155
+
156
+ group?: {
157
+ /**
158
+ * The "expand" argument to apply to groups.
159
+ *
160
+ * E.g. "member".
161
+ */
162
+ expand?: string;
163
+ /**
164
+ * The filter to apply to extract groups.
165
+ *
166
+ * E.g. "securityEnabled eq false and mailEnabled eq true"
167
+ */
168
+ filter?: string;
169
+ /**
170
+ * The search criteria to apply to extract users by groups memberships.
171
+ *
172
+ * E.g. "\"displayName:-team\"" would only match groups which contain '-team'
173
+ */
174
+ search?: string;
175
+ /**
176
+ * The fields to be fetched on query.
177
+ *
178
+ * E.g. ["id", "displayName", "description"]
179
+ */
180
+ select?: string[];
181
+ };
182
+
183
+ userGroupMember?: {
184
+ /**
185
+ * The filter to apply to extract users by groups memberships.
186
+ *
187
+ * E.g. "displayName eq 'Backstage Users'"
188
+ */
189
+ filter?: string;
190
+ /**
191
+ * The search criteria to apply to extract groups.
192
+ *
193
+ * E.g. "\"displayName:-team\"" would only match groups which contain '-team'
194
+ */
195
+ search?: string;
196
+ };
197
+ }
198
+ | Record<
199
+ string,
200
+ {
201
+ /**
202
+ * The prefix of the target that this matches on, e.g.
203
+ * "https://graph.microsoft.com/v1.0", with no trailing slash.
204
+ */
205
+ target: string;
206
+ /**
207
+ * The auth authority used.
208
+ *
209
+ * Default value "https://login.microsoftonline.com"
210
+ */
211
+ authority?: string;
212
+ /**
213
+ * The tenant whose org data we are interested in.
214
+ */
215
+ tenantId: string;
216
+ /**
217
+ * The OAuth client ID to use for authenticating requests.
218
+ */
219
+ clientId: string;
220
+ /**
221
+ * The OAuth client secret to use for authenticating requests.
222
+ *
223
+ * @visibility secret
224
+ */
225
+ clientSecret: string;
226
+
227
+ user?: {
228
+ /**
229
+ * The filter to apply to extract users.
230
+ *
231
+ * E.g. "accountEnabled eq true and userType eq 'member'"
232
+ */
233
+ filter?: string;
234
+ };
235
+
236
+ group?: {
237
+ /**
238
+ * The filter to apply to extract groups.
239
+ *
240
+ * E.g. "securityEnabled eq false and mailEnabled eq true"
241
+ */
242
+ filter?: string;
243
+ /**
244
+ * The search criteria to apply to extract users by groups memberships.
245
+ *
246
+ * E.g. "\"displayName:-team\"" would only match groups which contain '-team'
247
+ */
248
+ search?: string;
249
+ /**
250
+ * The fields to be fetched on query.
251
+ *
252
+ * E.g. ["id", "displayName", "description"]
253
+ */
254
+ select?: string[];
255
+ };
256
+
257
+ userGroupMember?: {
258
+ /**
259
+ * The filter to apply to extract users by groups memberships.
260
+ *
261
+ * E.g. "displayName eq 'Backstage Users'"
262
+ */
263
+ filter?: string;
264
+ /**
265
+ * The search criteria to apply to extract groups.
266
+ *
267
+ * E.g. "\"displayName:-team\"" would only match groups which contain '-team'
268
+ */
269
+ search?: string;
270
+ };
271
+ }
272
+ >;
273
+ };
105
274
  };
106
275
  }
package/dist/index.cjs.js CHANGED
@@ -175,13 +175,16 @@ class MicrosoftGraphClient {
175
175
  }
176
176
  }
177
177
 
178
+ const DEFAULT_AUTHORITY = "https://login.microsoftonline.com";
179
+ const DEFAULT_PROVIDER_ID = "default";
180
+ const DEFAULT_TARGET = "https://graph.microsoft.com/v1.0";
178
181
  function readMicrosoftGraphConfig(config) {
179
182
  var _a;
180
183
  const providers = [];
181
184
  const providerConfigs = (_a = config.getOptionalConfigArray("providers")) != null ? _a : [];
182
185
  for (const providerConfig of providerConfigs) {
183
186
  const target = lodash.trimEnd(providerConfig.getString("target"), "/");
184
- const authority = providerConfig.getOptionalString("authority") ? lodash.trimEnd(providerConfig.getOptionalString("authority"), "/") : "https://login.microsoftonline.com";
187
+ const authority = providerConfig.getOptionalString("authority") ? lodash.trimEnd(providerConfig.getOptionalString("authority"), "/") : DEFAULT_AUTHORITY;
185
188
  const tenantId = providerConfig.getString("tenantId");
186
189
  const clientId = providerConfig.getString("clientId");
187
190
  const clientSecret = providerConfig.getString("clientSecret");
@@ -204,6 +207,7 @@ function readMicrosoftGraphConfig(config) {
204
207
  throw new Error(`queryMode must be one of: basic, advanced`);
205
208
  }
206
209
  providers.push({
210
+ id: target,
207
211
  target,
208
212
  authority,
209
213
  tenantId,
@@ -222,6 +226,57 @@ function readMicrosoftGraphConfig(config) {
222
226
  }
223
227
  return providers;
224
228
  }
229
+ function readProviderConfigs(config) {
230
+ const providersConfig = config.getOptionalConfig("catalog.providers.microsoftGraphOrg");
231
+ if (!providersConfig) {
232
+ return [];
233
+ }
234
+ if (providersConfig.has("clientId")) {
235
+ return [readProviderConfig(DEFAULT_PROVIDER_ID, providersConfig)];
236
+ }
237
+ return providersConfig.keys().map((id) => {
238
+ const providerConfig = providersConfig.getConfig(id);
239
+ return readProviderConfig(id, providerConfig);
240
+ });
241
+ }
242
+ function readProviderConfig(id, config) {
243
+ var _a, _b;
244
+ const target = lodash.trimEnd((_a = config.getOptionalString("target")) != null ? _a : DEFAULT_TARGET, "/");
245
+ const authority = lodash.trimEnd((_b = config.getOptionalString("authority")) != null ? _b : DEFAULT_AUTHORITY, "/");
246
+ const clientId = config.getString("clientId");
247
+ const clientSecret = config.getString("clientSecret");
248
+ const tenantId = config.getString("tenantId");
249
+ const userExpand = config.getOptionalString("user.expand");
250
+ const userFilter = config.getOptionalString("user.filter");
251
+ const groupExpand = config.getOptionalString("group.expand");
252
+ const groupFilter = config.getOptionalString("group.filter");
253
+ const groupSearch = config.getOptionalString("group.search");
254
+ const groupSelect = config.getOptionalStringArray("group.select");
255
+ const userGroupMemberFilter = config.getOptionalString("userGroupMember.filter");
256
+ const userGroupMemberSearch = config.getOptionalString("userGroupMember.search");
257
+ if (userFilter && userGroupMemberFilter) {
258
+ throw new Error(`userFilter and userGroupMemberFilter are mutually exclusive, only one can be specified.`);
259
+ }
260
+ if (userFilter && userGroupMemberSearch) {
261
+ throw new Error(`userGroupMemberSearch cannot be specified when userFilter is defined.`);
262
+ }
263
+ return {
264
+ id,
265
+ target,
266
+ authority,
267
+ clientId,
268
+ clientSecret,
269
+ tenantId,
270
+ userExpand,
271
+ userFilter,
272
+ groupExpand,
273
+ groupFilter,
274
+ groupSearch,
275
+ groupSelect,
276
+ userGroupMemberFilter,
277
+ userGroupMemberSearch
278
+ };
279
+ }
225
280
 
226
281
  const MICROSOFT_EMAIL_ANNOTATION = "microsoft.com/email";
227
282
  const MICROSOFT_GRAPH_TENANT_ID_ANNOTATION = "graph.microsoft.com/tenant-id";
@@ -626,6 +681,32 @@ class MicrosoftGraphOrgEntityProvider {
626
681
  this.options = options;
627
682
  }
628
683
  static fromConfig(configRoot, options) {
684
+ if ("id" in options) {
685
+ return [
686
+ MicrosoftGraphOrgEntityProvider.fromLegacyConfig(configRoot, options)
687
+ ];
688
+ }
689
+ function getTransformer(id, transformers) {
690
+ if (["undefined", "function"].includes(typeof transformers)) {
691
+ return transformers;
692
+ }
693
+ return transformers[id];
694
+ }
695
+ return readProviderConfigs(configRoot).map((providerConfig) => {
696
+ const provider = new MicrosoftGraphOrgEntityProvider({
697
+ id: providerConfig.id,
698
+ provider: providerConfig,
699
+ logger: options.logger,
700
+ userTransformer: getTransformer(providerConfig.id, options.userTransformer),
701
+ groupTransformer: getTransformer(providerConfig.id, options.groupTransformer),
702
+ organizationTransformer: getTransformer(providerConfig.id, options.organizationTransformer)
703
+ });
704
+ provider.schedule(options.schedule);
705
+ return provider;
706
+ });
707
+ }
708
+ static fromLegacyConfig(configRoot, options) {
709
+ options.logger.warn('Deprecated msgraph config "catalog.processors.microsoftGraphOrg" used. Use "catalog.providers.microsoftGraphOrg" instead. More info at https://github.com/backstage/backstage/blob/master/.changeset/long-bananas-rescue.md');
629
710
  const config = configRoot.getOptionalConfig("catalog.processors.microsoftGraphOrg");
630
711
  const providers = config ? readMicrosoftGraphConfig(config) : [];
631
712
  const provider = providers.find((p) => options.target.startsWith(p.target));
@@ -750,6 +831,7 @@ class MicrosoftGraphOrgReaderProcessor {
750
831
  });
751
832
  }
752
833
  constructor(options) {
834
+ options.logger.warn("MicrosoftGraphOrgReaderProcessor is deprecated. Please use MicrosoftGraphOrgEntityProvider instead. More info at https://github.com/backstage/backstage/blob/master/.changeset/long-bananas-rescue.md");
753
835
  this.providers = options.providers;
754
836
  this.logger = options.logger;
755
837
  this.userTransformer = options.userTransformer;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/microsoftGraph/client.ts","../src/microsoftGraph/config.ts","../src/microsoftGraph/constants.ts","../src/microsoftGraph/helper.ts","../src/microsoftGraph/org.ts","../src/microsoftGraph/read.ts","../src/processors/MicrosoftGraphOrgEntityProvider.ts","../src/processors/MicrosoftGraphOrgReaderProcessor.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as msal from '@azure/msal-node';\nimport * as MicrosoftGraph from '@microsoft/microsoft-graph-types';\nimport fetch, { Response } from 'node-fetch';\nimport qs from 'qs';\nimport { MicrosoftGraphProviderConfig } from './config';\n\n/**\n * OData (Open Data Protocol) Query\n *\n * {@link https://docs.microsoft.com/en-us/odata/concepts/queryoptions-overview}\n * {@link https://docs.microsoft.com/en-us/graph/query-parameters}\n * @public\n */\nexport type ODataQuery = {\n /**\n * search resources within a collection matching a free-text search expression.\n */\n search?: string;\n /**\n * filter a collection of resources\n */\n filter?: string;\n /**\n * specifies the related resources or media streams to be included in line with retrieved resources\n */\n expand?: string;\n /**\n * request a specific set of properties for each entity or complex type\n */\n select?: string[];\n /**\n * Retrieves the total count of matching resources.\n */\n count?: boolean;\n};\n\n/**\n * Extends the base msgraph types to include the odata type.\n *\n * @public\n */\nexport type GroupMember =\n | (MicrosoftGraph.Group & { '@odata.type': '#microsoft.graph.user' })\n | (MicrosoftGraph.User & { '@odata.type': '#microsoft.graph.group' });\n\n/**\n * A HTTP Client that communicates with Microsoft Graph API.\n * Simplify Authentication and API calls to get `User` and `Group` from Azure Active Directory\n *\n * Uses `msal-node` for authentication\n *\n * @public\n */\nexport class MicrosoftGraphClient {\n /**\n * Factory method that instantiate `msal` client and return\n * an instance of `MicrosoftGraphClient`\n *\n * @public\n *\n * @param config - Configuration for Interacting with Graph API\n */\n static create(config: MicrosoftGraphProviderConfig): MicrosoftGraphClient {\n const clientConfig: msal.Configuration = {\n auth: {\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n authority: `${config.authority}/${config.tenantId}`,\n },\n };\n const pca = new msal.ConfidentialClientApplication(clientConfig);\n return new MicrosoftGraphClient(config.target, pca);\n }\n\n /**\n * @param baseUrl - baseUrl of Graph API {@link MicrosoftGraphProviderConfig.target}\n * @param pca - instance of `msal.ConfidentialClientApplication` that is used to acquire token for Graph API calls\n *\n */\n constructor(\n private readonly baseUrl: string,\n private readonly pca: msal.ConfidentialClientApplication,\n ) {}\n\n /**\n * Get a collection of resource from Graph API and\n * return an `AsyncIterable` of that resource\n *\n * @public\n * @param path - Resource in Microsoft Graph\n * @param query - OData Query {@link ODataQuery}\n * @param queryMode - Mode to use while querying. Some features are only available at \"advanced\".\n */\n async *requestCollection<T>(\n path: string,\n query?: ODataQuery,\n queryMode?: 'basic' | 'advanced',\n ): AsyncIterable<T> {\n // upgrade to advanced query mode transparently when \"search\" is used\n // to stay backwards compatible.\n const appliedQueryMode = query?.search ? 'advanced' : queryMode ?? 'basic';\n\n // not needed for \"search\"\n // as of https://docs.microsoft.com/en-us/graph/aad-advanced-queries?tabs=http\n // even though a few other places say the opposite\n // - https://docs.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http#request-headers\n // - https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties\n if (appliedQueryMode === 'advanced' && (query?.filter || query?.select)) {\n query.count = true;\n }\n const headers: Record<string, string> =\n appliedQueryMode === 'advanced'\n ? {\n // Eventual consistency is required for advanced querying capabilities\n // like \"$search\" or parts of \"$filter\".\n // If a new user/group is not found, it'll eventually be imported on a subsequent read\n ConsistencyLevel: 'eventual',\n }\n : {};\n\n let response = await this.requestApi(path, query, headers);\n\n for (;;) {\n if (response.status !== 200) {\n await this.handleError(path, response);\n }\n\n const result = await response.json();\n\n // Graph API return array of collections\n const elements: T[] = result.value;\n\n yield* elements;\n\n // Follow cursor to the next page if one is available\n if (!result['@odata.nextLink']) {\n return;\n }\n\n response = await this.requestRaw(result['@odata.nextLink'], headers);\n }\n }\n\n /**\n * Abstract on top of {@link MicrosoftGraphClient.requestRaw}\n *\n * @public\n * @param path - Resource in Microsoft Graph\n * @param query - OData Query {@link ODataQuery}\n * @param headers - optional HTTP headers\n */\n async requestApi(\n path: string,\n query?: ODataQuery,\n headers?: Record<string, string>,\n ): Promise<Response> {\n const queryString = qs.stringify(\n {\n $search: query?.search,\n $filter: query?.filter,\n $select: query?.select?.join(','),\n $expand: query?.expand,\n $count: query?.count,\n },\n {\n addQueryPrefix: true,\n // Microsoft Graph doesn't like an encoded query string\n encode: false,\n },\n );\n\n return await this.requestRaw(\n `${this.baseUrl}/${path}${queryString}`,\n headers,\n );\n }\n\n /**\n * Makes a HTTP call to Graph API with token\n *\n * @param url - HTTP Endpoint of Graph API\n * @param headers - optional HTTP headers\n */\n async requestRaw(\n url: string,\n headers?: Record<string, string>,\n ): Promise<Response> {\n // Make sure that we always have a valid access token (might be cached)\n const token = await this.pca.acquireTokenByClientCredential({\n scopes: ['https://graph.microsoft.com/.default'],\n });\n\n if (!token) {\n throw new Error('Error while requesting token for Microsoft Graph');\n }\n\n return await fetch(url, {\n headers: {\n ...headers,\n Authorization: `Bearer ${token.accessToken}`,\n },\n });\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/user | User}\n * from Graph API\n *\n * @public\n * @param userId - The unique identifier for the `User` resource\n * @param query - OData Query {@link ODataQuery}\n *\n */\n async getUserProfile(\n userId: string,\n query?: ODataQuery,\n ): Promise<MicrosoftGraph.User> {\n const response = await this.requestApi(`users/${userId}`, query);\n\n if (response.status !== 200) {\n await this.handleError('user profile', response);\n }\n\n return await response.json();\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/profilephoto | profilePhoto}\n * of `User` from Graph API with size limit\n *\n * @param userId - The unique identifier for the `User` resource\n * @param maxSize - Maximum pixel height of the photo\n *\n */\n async getUserPhotoWithSizeLimit(\n userId: string,\n maxSize: number,\n ): Promise<string | undefined> {\n return await this.getPhotoWithSizeLimit('users', userId, maxSize);\n }\n\n async getUserPhoto(\n userId: string,\n sizeId?: string,\n ): Promise<string | undefined> {\n return await this.getPhoto('users', userId, sizeId);\n }\n\n /**\n * Get a collection of\n * {@link https://docs.microsoft.com/en-us/graph/api/resources/user | User}\n * from Graph API and return as `AsyncIterable`\n *\n * @public\n * @param query - OData Query {@link ODataQuery}\n * @param queryMode - Mode to use while querying. Some features are only available at \"advanced\".\n */\n async *getUsers(\n query?: ODataQuery,\n queryMode?: 'basic' | 'advanced',\n ): AsyncIterable<MicrosoftGraph.User> {\n yield* this.requestCollection<MicrosoftGraph.User>(\n `users`,\n query,\n queryMode,\n );\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/profilephoto | profilePhoto}\n * of `Group` from Graph API with size limit\n *\n * @param groupId - The unique identifier for the `Group` resource\n * @param maxSize - Maximum pixel height of the photo\n *\n */\n async getGroupPhotoWithSizeLimit(\n groupId: string,\n maxSize: number,\n ): Promise<string | undefined> {\n return await this.getPhotoWithSizeLimit('groups', groupId, maxSize);\n }\n\n async getGroupPhoto(\n groupId: string,\n sizeId?: string,\n ): Promise<string | undefined> {\n return await this.getPhoto('groups', groupId, sizeId);\n }\n\n /**\n * Get a collection of\n * {@link https://docs.microsoft.com/en-us/graph/api/resources/group | Group}\n * from Graph API and return as `AsyncIterable`\n *\n * @public\n * @param query - OData Query {@link ODataQuery}\n * @param queryMode - Mode to use while querying. Some features are only available at \"advanced\".\n */\n async *getGroups(\n query?: ODataQuery,\n queryMode?: 'basic' | 'advanced',\n ): AsyncIterable<MicrosoftGraph.Group> {\n yield* this.requestCollection<MicrosoftGraph.Group>(\n `groups`,\n query,\n queryMode,\n );\n }\n\n /**\n * Get a collection of\n * {@link https://docs.microsoft.com/en-us/graph/api/resources/user | User}\n * belonging to a `Group` from Graph API and return as `AsyncIterable`\n * @public\n * @param groupId - The unique identifier for the `Group` resource\n *\n */\n async *getGroupMembers(groupId: string): AsyncIterable<GroupMember> {\n yield* this.requestCollection<GroupMember>(`groups/${groupId}/members`);\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/organization | Organization}\n * from Graph API\n * @public\n * @param tenantId - The unique identifier for the `Organization` resource\n *\n */\n async getOrganization(\n tenantId: string,\n ): Promise<MicrosoftGraph.Organization> {\n const response = await this.requestApi(`organization/${tenantId}`);\n\n if (response.status !== 200) {\n await this.handleError(`organization/${tenantId}`, response);\n }\n\n return await response.json();\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/profilephoto | profilePhoto}\n * from Graph API\n *\n * @param entityName - type of parent resource, either `User` or `Group`\n * @param id - The unique identifier for the {@link entityName | entityName} resource\n * @param maxSize - Maximum pixel height of the photo\n *\n */\n private async getPhotoWithSizeLimit(\n entityName: string,\n id: string,\n maxSize: number,\n ): Promise<string | undefined> {\n const response = await this.requestApi(`${entityName}/${id}/photos`);\n\n if (response.status === 404) {\n return undefined;\n } else if (response.status !== 200) {\n await this.handleError(`${entityName} photos`, response);\n }\n\n const result = await response.json();\n const photos = result.value as MicrosoftGraph.ProfilePhoto[];\n let selectedPhoto: MicrosoftGraph.ProfilePhoto | undefined = undefined;\n\n // Find the biggest picture that is smaller than the max size\n for (const p of photos) {\n if (\n !selectedPhoto ||\n (p.height! >= selectedPhoto.height! && p.height! <= maxSize)\n ) {\n selectedPhoto = p;\n }\n }\n\n if (!selectedPhoto) {\n return undefined;\n }\n\n return await this.getPhoto(entityName, id, selectedPhoto.id!);\n }\n\n private async getPhoto(\n entityName: string,\n id: string,\n sizeId?: string,\n ): Promise<string | undefined> {\n const path = sizeId\n ? `${entityName}/${id}/photos/${sizeId}/$value`\n : `${entityName}/${id}/photo/$value`;\n const response = await this.requestApi(path);\n\n if (response.status === 404) {\n return undefined;\n } else if (response.status !== 200) {\n await this.handleError('photo', response);\n }\n\n return `data:image/jpeg;base64,${Buffer.from(\n await response.arrayBuffer(),\n ).toString('base64')}`;\n }\n\n private async handleError(path: string, response: Response): Promise<void> {\n const result = await response.json();\n const error = result.error as MicrosoftGraph.PublicError;\n\n throw new Error(\n `Error while reading ${path} from Microsoft Graph: ${error.code} - ${error.message}`,\n );\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { trimEnd } from 'lodash';\n\n/**\n * The configuration parameters for a single Microsoft Graph provider.\n *\n * @public\n */\nexport type MicrosoftGraphProviderConfig = {\n /**\n * The prefix of the target that this matches on, e.g.\n * \"https://graph.microsoft.com/v1.0\", with no trailing slash.\n */\n target: string;\n /**\n * The auth authority used.\n *\n * E.g. \"https://login.microsoftonline.com\"\n */\n authority?: string;\n /**\n * The tenant whose org data we are interested in.\n */\n tenantId: string;\n /**\n * The OAuth client ID to use for authenticating requests.\n */\n clientId: string;\n /**\n * The OAuth client secret to use for authenticating requests.\n */\n clientSecret: string;\n /**\n * The filter to apply to extract users.\n *\n * E.g. \"accountEnabled eq true and userType eq 'member'\"\n */\n userFilter?: string;\n /**\n * The \"expand\" argument to apply to users.\n *\n * E.g. \"manager\"\n */\n userExpand?: string;\n /**\n * The filter to apply to extract users by groups memberships.\n *\n * E.g. \"displayName eq 'Backstage Users'\"\n */\n userGroupMemberFilter?: string;\n /**\n * The search criteria to apply to extract users by groups memberships.\n *\n * E.g. \"\\\"displayName:-team\\\"\" would only match groups which contain '-team'\n */\n userGroupMemberSearch?: string;\n /**\n * The \"expand\" argument to apply to groups.\n *\n * E.g. \"member\"\n */\n groupExpand?: string;\n /**\n * The filter to apply to extract groups.\n *\n * E.g. \"securityEnabled eq false and mailEnabled eq true\"\n */\n groupFilter?: string;\n /**\n * The search criteria to apply to extract groups.\n *\n * E.g. \"\\\"displayName:-team\\\"\" would only match groups which contain '-team'\n */\n groupSearch?: string;\n\n /**\n * The fields to be fetched on query.\n *\n * E.g. [\"id\", \"displayName\", \"description\"]\n */\n groupSelect?: string[];\n\n /**\n * By default, the Microsoft Graph API only provides the basic feature set\n * for querying. Certain features are limited to advanced query capabilities\n * (see https://docs.microsoft.com/en-us/graph/aad-advanced-queries)\n * and need to be enabled.\n *\n * Some features like `$expand` are not available for advanced queries, though.\n */\n queryMode?: 'basic' | 'advanced';\n};\n\n/**\n * Parses configuration.\n *\n * @param config - The root of the msgraph config hierarchy\n *\n * @public\n */\nexport function readMicrosoftGraphConfig(\n config: Config,\n): MicrosoftGraphProviderConfig[] {\n const providers: MicrosoftGraphProviderConfig[] = [];\n const providerConfigs = config.getOptionalConfigArray('providers') ?? [];\n\n for (const providerConfig of providerConfigs) {\n const target = trimEnd(providerConfig.getString('target'), '/');\n\n const authority = providerConfig.getOptionalString('authority')\n ? trimEnd(providerConfig.getOptionalString('authority'), '/')\n : 'https://login.microsoftonline.com';\n const tenantId = providerConfig.getString('tenantId');\n const clientId = providerConfig.getString('clientId');\n const clientSecret = providerConfig.getString('clientSecret');\n\n const userExpand = providerConfig.getOptionalString('userExpand');\n const userFilter = providerConfig.getOptionalString('userFilter');\n const userGroupMemberFilter = providerConfig.getOptionalString(\n 'userGroupMemberFilter',\n );\n const userGroupMemberSearch = providerConfig.getOptionalString(\n 'userGroupMemberSearch',\n );\n const groupExpand = providerConfig.getOptionalString('groupExpand');\n const groupFilter = providerConfig.getOptionalString('groupFilter');\n const groupSearch = providerConfig.getOptionalString('groupSearch');\n\n if (userFilter && userGroupMemberFilter) {\n throw new Error(\n `userFilter and userGroupMemberFilter are mutually exclusive, only one can be specified.`,\n );\n }\n if (userFilter && userGroupMemberSearch) {\n throw new Error(\n `userGroupMemberSearch cannot be specified when userFilter is defined.`,\n );\n }\n\n const groupSelect = providerConfig.getOptionalStringArray('groupSelect');\n const queryMode = providerConfig.getOptionalString('queryMode');\n if (\n queryMode !== undefined &&\n queryMode !== 'basic' &&\n queryMode !== 'advanced'\n ) {\n throw new Error(`queryMode must be one of: basic, advanced`);\n }\n\n providers.push({\n target,\n authority,\n tenantId,\n clientId,\n clientSecret,\n userExpand,\n userFilter,\n userGroupMemberFilter,\n userGroupMemberSearch,\n groupExpand,\n groupFilter,\n groupSearch,\n groupSelect,\n queryMode,\n });\n }\n\n return providers;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The (primary) user email. Also used by the Microsoft auth provider to resolve the User entity.\n *\n * @public\n */\nexport const MICROSOFT_EMAIL_ANNOTATION = 'microsoft.com/email';\n\n/**\n * The tenant id used by the Microsoft Graph API\n *\n * @public\n */\nexport const MICROSOFT_GRAPH_TENANT_ID_ANNOTATION =\n 'graph.microsoft.com/tenant-id';\n\n/**\n * The group id used by the Microsoft Graph API\n *\n * @public\n */\nexport const MICROSOFT_GRAPH_GROUP_ID_ANNOTATION =\n 'graph.microsoft.com/group-id';\n\n/**\n * The user id used by the Microsoft Graph API\n *\n * @public\n */\nexport const MICROSOFT_GRAPH_USER_ID_ANNOTATION = 'graph.microsoft.com/user-id';\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Takes an input string and cleans it up to become suitable as an entity name.\n *\n * @public\n */\nexport function normalizeEntityName(name: string): string {\n let cleaned = name\n .trim()\n .toLocaleLowerCase()\n .replace(/[^a-zA-Z0-9_\\-\\.]/g, '_');\n\n // invalid to end with _\n while (cleaned.endsWith('_')) {\n cleaned = cleaned.substring(0, cleaned.length - 1);\n }\n\n // cleans up format for groups like 'my group (Reader)'\n while (cleaned.includes('__')) {\n // replaceAll from node.js >= 15\n cleaned = cleaned.replace('__', '_');\n }\n\n return cleaned;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { GroupEntity, UserEntity } from '@backstage/catalog-model';\n\n// TODO: Copied from plugin-catalog-backend, but we could also export them from\n// there. Or move them to catalog-model.\n\nexport function buildOrgHierarchy(groups: GroupEntity[]) {\n const groupsByName = new Map(groups.map(g => [g.metadata.name, g]));\n\n //\n // Make sure that g.parent.children contain g\n //\n\n for (const group of groups) {\n const selfName = group.metadata.name;\n const parentName = group.spec.parent;\n if (parentName) {\n const parent = groupsByName.get(parentName);\n if (parent && !parent.spec.children.includes(selfName)) {\n parent.spec.children.push(selfName);\n }\n }\n }\n\n //\n // Make sure that g.children.parent is g\n //\n\n for (const group of groups) {\n const selfName = group.metadata.name;\n for (const childName of group.spec.children) {\n const child = groupsByName.get(childName);\n if (child && !child.spec.parent) {\n child.spec.parent = selfName;\n }\n }\n }\n}\n\n// Ensure that users have their transitive group memberships. Requires that\n// the groups were previously processed with buildOrgHierarchy()\nexport function buildMemberOf(groups: GroupEntity[], users: UserEntity[]) {\n const groupsByName = new Map(groups.map(g => [g.metadata.name, g]));\n\n users.forEach(user => {\n const transitiveMemberOf = new Set<string>();\n\n const todo = [\n ...(user.spec.memberOf ?? []),\n ...groups\n .filter(g => g.spec.members?.includes(user.metadata.name))\n .map(g => g.metadata.name),\n ];\n\n for (;;) {\n const current = todo.pop();\n if (!current) {\n break;\n }\n\n if (!transitiveMemberOf.has(current)) {\n transitiveMemberOf.add(current);\n const group = groupsByName.get(current);\n if (group?.spec.parent) {\n todo.push(group.spec.parent);\n }\n }\n }\n\n user.spec.memberOf = [...transitiveMemberOf];\n });\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n GroupEntity,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport * as MicrosoftGraph from '@microsoft/microsoft-graph-types';\nimport limiterFactory from 'p-limit';\nimport { Logger } from 'winston';\nimport { MicrosoftGraphClient } from './client';\nimport {\n MICROSOFT_EMAIL_ANNOTATION,\n MICROSOFT_GRAPH_GROUP_ID_ANNOTATION,\n MICROSOFT_GRAPH_TENANT_ID_ANNOTATION,\n MICROSOFT_GRAPH_USER_ID_ANNOTATION,\n} from './constants';\nimport { normalizeEntityName } from './helper';\nimport { buildMemberOf, buildOrgHierarchy } from './org';\nimport {\n GroupTransformer,\n OrganizationTransformer,\n UserTransformer,\n} from './types';\n\n/**\n * The default implementation of the transformation from a graph user entry to\n * a User entity.\n *\n * @public\n */\nexport async function defaultUserTransformer(\n user: MicrosoftGraph.User,\n userPhoto?: string,\n): Promise<UserEntity | undefined> {\n if (!user.id || !user.displayName || !user.mail) {\n return undefined;\n }\n\n const name = normalizeEntityName(user.mail);\n const entity: UserEntity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'User',\n metadata: {\n name,\n annotations: {\n [MICROSOFT_EMAIL_ANNOTATION]: user.mail!,\n [MICROSOFT_GRAPH_USER_ID_ANNOTATION]: user.id!,\n },\n },\n spec: {\n profile: {\n displayName: user.displayName!,\n email: user.mail!,\n\n // TODO: Additional fields?\n // jobTitle: user.jobTitle || undefined,\n // officeLocation: user.officeLocation || undefined,\n // mobilePhone: user.mobilePhone || undefined,\n },\n memberOf: [],\n },\n };\n\n if (userPhoto) {\n entity.spec.profile!.picture = userPhoto;\n }\n\n return entity;\n}\n\nexport async function readMicrosoftGraphUsers(\n client: MicrosoftGraphClient,\n options: {\n queryMode?: 'basic' | 'advanced';\n userFilter?: string;\n userExpand?: string;\n transformer?: UserTransformer;\n logger: Logger;\n },\n): Promise<{\n users: UserEntity[]; // With all relations empty\n}> {\n const users: UserEntity[] = [];\n const limiter = limiterFactory(10);\n\n const transformer = options.transformer ?? defaultUserTransformer;\n const promises: Promise<void>[] = [];\n\n for await (const user of client.getUsers(\n {\n filter: options.userFilter,\n expand: options.userExpand,\n },\n options.queryMode,\n )) {\n // Process all users in parallel, otherwise it can take quite some time\n promises.push(\n limiter(async () => {\n let userPhoto;\n try {\n userPhoto = await client.getUserPhotoWithSizeLimit(\n user.id!,\n // We are limiting the photo size, as users with full resolution photos\n // can make the Backstage API slow\n 120,\n );\n } catch (e) {\n options.logger.warn(`Unable to load photo for ${user.id}`);\n }\n\n const entity = await transformer(user, userPhoto);\n\n if (!entity) {\n return;\n }\n\n users.push(entity);\n }),\n );\n }\n\n // Wait for all users and photos to be downloaded\n await Promise.all(promises);\n\n return { users };\n}\n\nexport async function readMicrosoftGraphUsersInGroups(\n client: MicrosoftGraphClient,\n options: {\n queryMode?: 'basic' | 'advanced';\n userExpand?: string;\n userGroupMemberSearch?: string;\n userGroupMemberFilter?: string;\n groupExpand?: string;\n transformer?: UserTransformer;\n logger: Logger;\n },\n): Promise<{\n users: UserEntity[]; // With all relations empty\n}> {\n const users: UserEntity[] = [];\n\n const limiter = limiterFactory(10);\n\n const transformer = options.transformer ?? defaultUserTransformer;\n const userGroupMemberPromises: Promise<void>[] = [];\n const userPromises: Promise<void>[] = [];\n\n const groupMemberUsers: Set<string> = new Set();\n\n for await (const group of client.getGroups(\n {\n expand: options.groupExpand,\n search: options.userGroupMemberSearch,\n filter: options.userGroupMemberFilter,\n },\n options.queryMode,\n )) {\n // Process all groups in parallel, otherwise it can take quite some time\n userGroupMemberPromises.push(\n limiter(async () => {\n for await (const member of client.getGroupMembers(group.id!)) {\n if (!member.id) {\n continue;\n }\n\n if (member['@odata.type'] === '#microsoft.graph.user') {\n groupMemberUsers.add(member.id);\n }\n }\n }),\n );\n }\n\n // Wait for all group members\n await Promise.all(userGroupMemberPromises);\n\n options.logger.info(`groupMemberUsers ${groupMemberUsers.size}`);\n for (const userId of groupMemberUsers) {\n // Process all users in parallel, otherwise it can take quite some time\n userPromises.push(\n limiter(async () => {\n let user;\n let userPhoto;\n try {\n user = await client.getUserProfile(userId, {\n expand: options.userExpand,\n });\n } catch (e) {\n options.logger.warn(`Unable to load user for ${userId}`);\n }\n if (user) {\n try {\n userPhoto = await client.getUserPhotoWithSizeLimit(\n user.id!,\n // We are limiting the photo size, as users with full resolution photos\n // can make the Backstage API slow\n 120,\n );\n } catch (e) {\n options.logger.warn(`Unable to load userphoto for ${userId}`);\n }\n\n const entity = await transformer(user, userPhoto);\n\n if (!entity) {\n return;\n }\n users.push(entity);\n }\n }),\n );\n }\n\n // Wait for all users and photos to be downloaded\n await Promise.all(userPromises);\n\n return { users };\n}\n\n/**\n * The default implementation of the transformation from a graph organization\n * entry to a Group entity.\n *\n * @public\n */\nexport async function defaultOrganizationTransformer(\n organization: MicrosoftGraph.Organization,\n): Promise<GroupEntity | undefined> {\n if (!organization.id || !organization.displayName) {\n return undefined;\n }\n\n const name = normalizeEntityName(organization.displayName!);\n return {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'Group',\n metadata: {\n name: name,\n description: organization.displayName!,\n annotations: {\n [MICROSOFT_GRAPH_TENANT_ID_ANNOTATION]: organization.id!,\n },\n },\n spec: {\n type: 'root',\n profile: {\n displayName: organization.displayName!,\n },\n children: [],\n },\n };\n}\n\nexport async function readMicrosoftGraphOrganization(\n client: MicrosoftGraphClient,\n tenantId: string,\n options?: { transformer?: OrganizationTransformer },\n): Promise<{\n rootGroup?: GroupEntity; // With all relations empty\n}> {\n // For now we expect a single root organization\n const organization = await client.getOrganization(tenantId);\n const transformer = options?.transformer ?? defaultOrganizationTransformer;\n const rootGroup = await transformer(organization);\n\n return { rootGroup };\n}\n\nfunction extractGroupName(group: MicrosoftGraph.Group): string {\n if (group.securityEnabled) {\n return group.displayName as string;\n }\n return (group.mailNickname || group.displayName) as string;\n}\n\n/**\n * The default implementation of the transformation from a graph group entry to\n * a Group entity.\n *\n * @public\n */\nexport async function defaultGroupTransformer(\n group: MicrosoftGraph.Group,\n groupPhoto?: string,\n): Promise<GroupEntity | undefined> {\n if (!group.id || !group.displayName) {\n return undefined;\n }\n\n const name = normalizeEntityName(extractGroupName(group));\n const entity: GroupEntity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'Group',\n metadata: {\n name: name,\n annotations: {\n [MICROSOFT_GRAPH_GROUP_ID_ANNOTATION]: group.id,\n },\n },\n spec: {\n type: 'team',\n profile: {},\n children: [],\n },\n };\n\n if (group.description) {\n entity.metadata.description = group.description;\n }\n if (group.displayName) {\n entity.spec.profile!.displayName = group.displayName;\n }\n if (group.mail) {\n entity.spec.profile!.email = group.mail;\n }\n if (groupPhoto) {\n entity.spec.profile!.picture = groupPhoto;\n }\n\n return entity;\n}\n\nexport async function readMicrosoftGraphGroups(\n client: MicrosoftGraphClient,\n tenantId: string,\n options?: {\n queryMode?: 'basic' | 'advanced';\n groupExpand?: string;\n groupFilter?: string;\n groupSearch?: string;\n groupSelect?: string[];\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n },\n): Promise<{\n groups: GroupEntity[]; // With all relations empty\n rootGroup: GroupEntity | undefined; // With all relations empty\n groupMember: Map<string, Set<string>>;\n groupMemberOf: Map<string, Set<string>>;\n}> {\n const groups: GroupEntity[] = [];\n const groupMember: Map<string, Set<string>> = new Map();\n const groupMemberOf: Map<string, Set<string>> = new Map();\n const limiter = limiterFactory(10);\n\n const { rootGroup } = await readMicrosoftGraphOrganization(client, tenantId, {\n transformer: options?.organizationTransformer,\n });\n if (rootGroup) {\n groupMember.set(rootGroup.metadata.name, new Set<string>());\n groups.push(rootGroup);\n }\n\n const transformer = options?.groupTransformer ?? defaultGroupTransformer;\n const promises: Promise<void>[] = [];\n\n for await (const group of client.getGroups(\n {\n expand: options?.groupExpand,\n search: options?.groupSearch,\n filter: options?.groupFilter,\n select: options?.groupSelect,\n },\n options?.queryMode,\n )) {\n // Process all groups in parallel, otherwise it can take quite some time\n promises.push(\n limiter(async () => {\n // TODO: Loading groups photos doesn't work right now as Microsoft Graph\n // doesn't allows this yet: https://microsoftgraph.uservoice.com/forums/920506-microsoft-graph-feature-requests/suggestions/37884922-allow-application-to-set-or-update-a-group-s-photo\n /* const groupPhoto = await client.getGroupPhotoWithSizeLimit(\n group.id!,\n // We are limiting the photo size, as groups with full resolution photos\n // can make the Backstage API slow\n 120,\n );*/\n\n const entity = await transformer(group /* , groupPhoto*/);\n\n if (!entity) {\n return;\n }\n\n for await (const member of client.getGroupMembers(group.id!)) {\n if (!member.id) {\n continue;\n }\n\n if (member['@odata.type'] === '#microsoft.graph.user') {\n ensureItem(groupMemberOf, member.id, group.id!);\n }\n\n if (member['@odata.type'] === '#microsoft.graph.group') {\n ensureItem(groupMember, group.id!, member.id);\n }\n }\n\n groups.push(entity);\n }),\n );\n }\n\n // Wait for all group members and photos to be loaded\n await Promise.all(promises);\n\n return {\n groups,\n rootGroup,\n groupMember,\n groupMemberOf,\n };\n}\n\nexport function resolveRelations(\n rootGroup: GroupEntity | undefined,\n groups: GroupEntity[],\n users: UserEntity[],\n groupMember: Map<string, Set<string>>,\n groupMemberOf: Map<string, Set<string>>,\n) {\n // Build reference lookup tables, we reference them by the id the the graph\n const groupMap: Map<string, GroupEntity> = new Map(); // by group-id or tenant-id\n\n for (const group of groups) {\n if (group.metadata.annotations![MICROSOFT_GRAPH_GROUP_ID_ANNOTATION]) {\n groupMap.set(\n group.metadata.annotations![MICROSOFT_GRAPH_GROUP_ID_ANNOTATION],\n group,\n );\n }\n if (group.metadata.annotations![MICROSOFT_GRAPH_TENANT_ID_ANNOTATION]) {\n groupMap.set(\n group.metadata.annotations![MICROSOFT_GRAPH_TENANT_ID_ANNOTATION],\n group,\n );\n }\n }\n\n // Resolve all member relationships into the reverse direction\n const parentGroups = new Map<string, Set<string>>();\n\n groupMember.forEach((members, groupId) =>\n members.forEach(m => ensureItem(parentGroups, m, groupId)),\n );\n\n // Make sure every group (except root) has at least one parent. If the parent is missing, add the root.\n if (rootGroup) {\n const tenantId =\n rootGroup.metadata.annotations![MICROSOFT_GRAPH_TENANT_ID_ANNOTATION];\n\n groups.forEach(group => {\n const groupId =\n group.metadata.annotations![MICROSOFT_GRAPH_GROUP_ID_ANNOTATION];\n\n if (!groupId) {\n return;\n }\n\n if (retrieveItems(parentGroups, groupId).size === 0) {\n ensureItem(parentGroups, groupId, tenantId);\n ensureItem(groupMember, tenantId, groupId);\n }\n });\n }\n\n groups.forEach(group => {\n const id =\n group.metadata.annotations![MICROSOFT_GRAPH_GROUP_ID_ANNOTATION] ??\n group.metadata.annotations![MICROSOFT_GRAPH_TENANT_ID_ANNOTATION];\n\n retrieveItems(groupMember, id).forEach(m => {\n const childGroup = groupMap.get(m);\n if (childGroup) {\n group.spec.children.push(stringifyEntityRef(childGroup));\n }\n });\n\n retrieveItems(parentGroups, id).forEach(p => {\n const parentGroup = groupMap.get(p);\n if (parentGroup) {\n // TODO: Only having a single parent group might not match every companies model, but fine for now.\n group.spec.parent = stringifyEntityRef(parentGroup);\n }\n });\n });\n\n // Make sure that all groups have proper parents and children\n buildOrgHierarchy(groups);\n\n // Set relations for all users\n users.forEach(user => {\n const id = user.metadata.annotations![MICROSOFT_GRAPH_USER_ID_ANNOTATION];\n\n retrieveItems(groupMemberOf, id).forEach(p => {\n const parentGroup = groupMap.get(p);\n if (parentGroup) {\n if (!user.spec.memberOf) {\n user.spec.memberOf = [];\n }\n user.spec.memberOf.push(stringifyEntityRef(parentGroup));\n }\n });\n });\n\n // Make sure all transitive memberships are available\n buildMemberOf(groups, users);\n}\n\n/**\n * Reads an entire org as Group and User entities.\n *\n * @public\n */\nexport async function readMicrosoftGraphOrg(\n client: MicrosoftGraphClient,\n tenantId: string,\n options: {\n userExpand?: string;\n userFilter?: string;\n userGroupMemberSearch?: string;\n userGroupMemberFilter?: string;\n groupExpand?: string;\n groupSearch?: string;\n groupFilter?: string;\n groupSelect?: string[];\n queryMode?: 'basic' | 'advanced';\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n logger: Logger;\n },\n): Promise<{ users: UserEntity[]; groups: GroupEntity[] }> {\n const users: UserEntity[] = [];\n\n if (options.userGroupMemberFilter || options.userGroupMemberSearch) {\n const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups(\n client,\n {\n queryMode: options.queryMode,\n userGroupMemberFilter: options.userGroupMemberFilter,\n userGroupMemberSearch: options.userGroupMemberSearch,\n transformer: options.userTransformer,\n logger: options.logger,\n },\n );\n users.push(...usersInGroups);\n } else {\n const { users: usersWithFilter } = await readMicrosoftGraphUsers(client, {\n queryMode: options.queryMode,\n userFilter: options.userFilter,\n userExpand: options.userExpand,\n transformer: options.userTransformer,\n logger: options.logger,\n });\n users.push(...usersWithFilter);\n }\n const { groups, rootGroup, groupMember, groupMemberOf } =\n await readMicrosoftGraphGroups(client, tenantId, {\n queryMode: options.queryMode,\n groupSearch: options.groupSearch,\n groupFilter: options.groupFilter,\n groupSelect: options.groupSelect,\n groupTransformer: options.groupTransformer,\n organizationTransformer: options.organizationTransformer,\n });\n\n resolveRelations(rootGroup, groups, users, groupMember, groupMemberOf);\n users.sort((a, b) => a.metadata.name.localeCompare(b.metadata.name));\n groups.sort((a, b) => a.metadata.name.localeCompare(b.metadata.name));\n\n return { users, groups };\n}\n\nfunction ensureItem(\n target: Map<string, Set<string>>,\n key: string,\n value: string,\n) {\n let set = target.get(key);\n if (!set) {\n set = new Set();\n target.set(key, set);\n }\n set!.add(value);\n}\n\nfunction retrieveItems(\n target: Map<string, Set<string>>,\n key: string,\n): Set<string> {\n return target.get(key) ?? new Set();\n}\n","/*\n * Copyright 2021 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 { TaskRunner } from '@backstage/backend-tasks';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n Entity,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-backend';\nimport { merge } from 'lodash';\nimport * as uuid from 'uuid';\nimport { Logger } from 'winston';\nimport {\n GroupTransformer,\n MicrosoftGraphClient,\n MicrosoftGraphProviderConfig,\n MICROSOFT_GRAPH_GROUP_ID_ANNOTATION,\n MICROSOFT_GRAPH_TENANT_ID_ANNOTATION,\n MICROSOFT_GRAPH_USER_ID_ANNOTATION,\n OrganizationTransformer,\n readMicrosoftGraphConfig,\n readMicrosoftGraphOrg,\n UserTransformer,\n} from '../microsoftGraph';\n\n/**\n * Options for {@link MicrosoftGraphOrgEntityProvider}.\n *\n * @public\n */\nexport interface MicrosoftGraphOrgEntityProviderOptions {\n /**\n * A unique, stable identifier for this provider.\n *\n * @example \"production\"\n */\n id: string;\n\n /**\n * The target that this provider should consume.\n *\n * Should exactly match the \"target\" field of one of the providers\n * configuration entries.\n */\n target: string;\n\n /**\n * The logger to use.\n */\n logger: Logger;\n\n /**\n * The refresh schedule to use.\n *\n * @remarks\n *\n * If you pass in 'manual', you are responsible for calling the `read` method\n * manually at some interval.\n *\n * But more commonly you will pass in the result of\n * {@link @backstage/backend-tasks#PluginTaskScheduler.createScheduledTaskRunner}\n * to enable automatic scheduling of tasks.\n */\n schedule: 'manual' | TaskRunner;\n\n /**\n * The function that transforms a user entry in msgraph to an entity.\n */\n userTransformer?: UserTransformer;\n\n /**\n * The function that transforms a group entry in msgraph to an entity.\n */\n groupTransformer?: GroupTransformer;\n\n /**\n * The function that transforms an organization entry in msgraph to an entity.\n */\n organizationTransformer?: OrganizationTransformer;\n}\n\n/**\n * Reads user and group entries out of Microsoft Graph, and provides them as\n * User and Group entities for the catalog.\n *\n * @public\n */\nexport class MicrosoftGraphOrgEntityProvider implements EntityProvider {\n private connection?: EntityProviderConnection;\n private scheduleFn?: () => Promise<void>;\n\n static fromConfig(\n configRoot: Config,\n options: MicrosoftGraphOrgEntityProviderOptions,\n ) {\n const config = configRoot.getOptionalConfig(\n 'catalog.processors.microsoftGraphOrg',\n );\n const providers = config ? readMicrosoftGraphConfig(config) : [];\n const provider = providers.find(p => options.target.startsWith(p.target));\n\n if (!provider) {\n throw new Error(\n `There is no Microsoft Graph Org provider that matches \"${options.target}\". Please add a configuration entry for it under \"catalog.processors.microsoftGraphOrg.providers\".`,\n );\n }\n\n const logger = options.logger.child({\n target: options.target,\n });\n\n const result = new MicrosoftGraphOrgEntityProvider({\n id: options.id,\n userTransformer: options.userTransformer,\n groupTransformer: options.groupTransformer,\n organizationTransformer: options.organizationTransformer,\n logger,\n provider,\n });\n\n result.schedule(options.schedule);\n\n return result;\n }\n\n constructor(\n private options: {\n id: string;\n provider: MicrosoftGraphProviderConfig;\n logger: Logger;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n },\n ) {}\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */\n getProviderName() {\n return `MicrosoftGraphOrgEntityProvider:${this.options.id}`;\n }\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */\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?: Logger }) {\n if (!this.connection) {\n throw new Error('Not initialized');\n }\n\n const logger = options?.logger ?? this.options.logger;\n const provider = this.options.provider;\n const { markReadComplete } = trackProgress(logger);\n const client = MicrosoftGraphClient.create(this.options.provider);\n const { users, groups } = await readMicrosoftGraphOrg(\n client,\n provider.tenantId,\n {\n userFilter: provider.userFilter,\n userGroupMemberFilter: provider.userGroupMemberFilter,\n userGroupMemberSearch: provider.userGroupMemberSearch,\n groupFilter: provider.groupFilter,\n groupSearch: provider.groupSearch,\n groupSelect: provider.groupSelect,\n queryMode: provider.queryMode,\n groupTransformer: this.options.groupTransformer,\n userTransformer: this.options.userTransformer,\n organizationTransformer: this.options.organizationTransformer,\n logger: logger,\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: `msgraph-org-provider:${this.options.id}`,\n entity: withLocations(this.options.id, entity),\n })),\n });\n\n markCommitComplete();\n }\n\n private schedule(\n schedule: MicrosoftGraphOrgEntityProviderOptions['schedule'],\n ) {\n if (schedule === 'manual') {\n return;\n }\n\n this.scheduleFn = async () => {\n const id = `${this.getProviderName()}:refresh`;\n await schedule.run({\n id,\n fn: async () => {\n const logger = this.options.logger.child({\n class: MicrosoftGraphOrgEntityProvider.prototype.constructor.name,\n taskId: id,\n taskInstanceId: uuid.v4(),\n });\n\n try {\n await this.read({ logger });\n } catch (error) {\n logger.error(error);\n }\n },\n });\n };\n }\n}\n\n// Helps wrap the timing and logging behaviors\nfunction trackProgress(logger: Logger) {\n let timestamp = Date.now();\n let summary: string;\n\n logger.info('Reading msgraph users and groups');\n\n function markReadComplete(read: { users: unknown[]; groups: unknown[] }) {\n summary = `${read.users.length} msgraph users and ${read.groups.length} msgraph 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\n// Makes sure that emitted entities have a proper location based on their uuid\nexport function withLocations(providerId: string, entity: Entity): Entity {\n const uid =\n entity.metadata.annotations?.[MICROSOFT_GRAPH_USER_ID_ANNOTATION] ||\n entity.metadata.annotations?.[MICROSOFT_GRAPH_GROUP_ID_ANNOTATION] ||\n entity.metadata.annotations?.[MICROSOFT_GRAPH_TENANT_ID_ANNOTATION] ||\n entity.metadata.name;\n const location = `msgraph:${providerId}/${encodeURIComponent(uid)}`;\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 * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport {\n CatalogProcessor,\n CatalogProcessorEmit,\n LocationSpec,\n processingResult,\n} from '@backstage/plugin-catalog-backend';\nimport { Logger } from 'winston';\nimport {\n GroupTransformer,\n MicrosoftGraphClient,\n MicrosoftGraphProviderConfig,\n OrganizationTransformer,\n readMicrosoftGraphConfig,\n readMicrosoftGraphOrg,\n UserTransformer,\n} from '../microsoftGraph';\n\n/**\n * Extracts teams and users out of a the Microsoft Graph API.\n *\n * @public\n */\nexport class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {\n private readonly providers: MicrosoftGraphProviderConfig[];\n private readonly logger: Logger;\n private readonly userTransformer?: UserTransformer;\n private readonly groupTransformer?: GroupTransformer;\n private readonly organizationTransformer?: OrganizationTransformer;\n\n static fromConfig(\n config: Config,\n options: {\n logger: Logger;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n },\n ) {\n const c = config.getOptionalConfig('catalog.processors.microsoftGraphOrg');\n return new MicrosoftGraphOrgReaderProcessor({\n ...options,\n providers: c ? readMicrosoftGraphConfig(c) : [],\n });\n }\n\n constructor(options: {\n providers: MicrosoftGraphProviderConfig[];\n logger: Logger;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n }) {\n this.providers = options.providers;\n this.logger = options.logger;\n this.userTransformer = options.userTransformer;\n this.groupTransformer = options.groupTransformer;\n this.organizationTransformer = options.organizationTransformer;\n }\n\n getProcessorName(): string {\n return 'MicrosoftGraphOrgReaderProcessor';\n }\n\n async readLocation(\n location: LocationSpec,\n _optional: boolean,\n emit: CatalogProcessorEmit,\n ): Promise<boolean> {\n if (location.type !== 'microsoft-graph-org') {\n return false;\n }\n\n const provider = this.providers.find(p =>\n location.target.startsWith(p.target),\n );\n if (!provider) {\n throw new Error(\n `There is no Microsoft Graph Org provider that matches ${location.target}. Please add a configuration entry for it under catalog.processors.microsoftGraphOrg.providers.`,\n );\n }\n\n // Read out all the raw data\n const startTimestamp = Date.now();\n this.logger.info('Reading Microsoft Graph users and groups');\n\n // We create a client each time as we need one that matches the specific provider\n const client = MicrosoftGraphClient.create(provider);\n const { users, groups } = await readMicrosoftGraphOrg(\n client,\n provider.tenantId,\n {\n userExpand: provider.userExpand,\n userFilter: provider.userFilter,\n userGroupMemberFilter: provider.userGroupMemberFilter,\n userGroupMemberSearch: provider.userGroupMemberSearch,\n groupExpand: provider.groupExpand,\n groupFilter: provider.groupFilter,\n groupSearch: provider.groupSearch,\n groupSelect: provider.groupSelect,\n queryMode: provider.queryMode,\n userTransformer: this.userTransformer,\n groupTransformer: this.groupTransformer,\n organizationTransformer: this.organizationTransformer,\n logger: this.logger,\n },\n );\n\n const duration = ((Date.now() - startTimestamp) / 1000).toFixed(1);\n this.logger.debug(\n `Read ${users.length} users and ${groups.length} groups from Microsoft Graph in ${duration} seconds`,\n );\n\n // Done!\n for (const group of groups) {\n emit(processingResult.entity(location, group));\n }\n for (const user of users) {\n emit(processingResult.entity(location, user));\n }\n\n return true;\n }\n}\n"],"names":["msal","qs","fetch","trimEnd","limiterFactory","stringifyEntityRef","uuid","merge","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION","processingResult"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/microsoftGraph/client.ts","../src/microsoftGraph/config.ts","../src/microsoftGraph/constants.ts","../src/microsoftGraph/helper.ts","../src/microsoftGraph/org.ts","../src/microsoftGraph/read.ts","../src/processors/MicrosoftGraphOrgEntityProvider.ts","../src/processors/MicrosoftGraphOrgReaderProcessor.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as msal from '@azure/msal-node';\nimport * as MicrosoftGraph from '@microsoft/microsoft-graph-types';\nimport fetch, { Response } from 'node-fetch';\nimport qs from 'qs';\nimport { MicrosoftGraphProviderConfig } from './config';\n\n/**\n * OData (Open Data Protocol) Query\n *\n * {@link https://docs.microsoft.com/en-us/odata/concepts/queryoptions-overview}\n * {@link https://docs.microsoft.com/en-us/graph/query-parameters}\n * @public\n */\nexport type ODataQuery = {\n /**\n * search resources within a collection matching a free-text search expression.\n */\n search?: string;\n /**\n * filter a collection of resources\n */\n filter?: string;\n /**\n * specifies the related resources or media streams to be included in line with retrieved resources\n */\n expand?: string;\n /**\n * request a specific set of properties for each entity or complex type\n */\n select?: string[];\n /**\n * Retrieves the total count of matching resources.\n */\n count?: boolean;\n};\n\n/**\n * Extends the base msgraph types to include the odata type.\n *\n * @public\n */\nexport type GroupMember =\n | (MicrosoftGraph.Group & { '@odata.type': '#microsoft.graph.user' })\n | (MicrosoftGraph.User & { '@odata.type': '#microsoft.graph.group' });\n\n/**\n * A HTTP Client that communicates with Microsoft Graph API.\n * Simplify Authentication and API calls to get `User` and `Group` from Azure Active Directory\n *\n * Uses `msal-node` for authentication\n *\n * @public\n */\nexport class MicrosoftGraphClient {\n /**\n * Factory method that instantiate `msal` client and return\n * an instance of `MicrosoftGraphClient`\n *\n * @public\n *\n * @param config - Configuration for Interacting with Graph API\n */\n static create(config: MicrosoftGraphProviderConfig): MicrosoftGraphClient {\n const clientConfig: msal.Configuration = {\n auth: {\n clientId: config.clientId,\n clientSecret: config.clientSecret,\n authority: `${config.authority}/${config.tenantId}`,\n },\n };\n const pca = new msal.ConfidentialClientApplication(clientConfig);\n return new MicrosoftGraphClient(config.target, pca);\n }\n\n /**\n * @param baseUrl - baseUrl of Graph API {@link MicrosoftGraphProviderConfig.target}\n * @param pca - instance of `msal.ConfidentialClientApplication` that is used to acquire token for Graph API calls\n *\n */\n constructor(\n private readonly baseUrl: string,\n private readonly pca: msal.ConfidentialClientApplication,\n ) {}\n\n /**\n * Get a collection of resource from Graph API and\n * return an `AsyncIterable` of that resource\n *\n * @public\n * @param path - Resource in Microsoft Graph\n * @param query - OData Query {@link ODataQuery}\n * @param queryMode - Mode to use while querying. Some features are only available at \"advanced\".\n */\n async *requestCollection<T>(\n path: string,\n query?: ODataQuery,\n queryMode?: 'basic' | 'advanced',\n ): AsyncIterable<T> {\n // upgrade to advanced query mode transparently when \"search\" is used\n // to stay backwards compatible.\n const appliedQueryMode = query?.search ? 'advanced' : queryMode ?? 'basic';\n\n // not needed for \"search\"\n // as of https://docs.microsoft.com/en-us/graph/aad-advanced-queries?tabs=http\n // even though a few other places say the opposite\n // - https://docs.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http#request-headers\n // - https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0#properties\n if (appliedQueryMode === 'advanced' && (query?.filter || query?.select)) {\n query.count = true;\n }\n const headers: Record<string, string> =\n appliedQueryMode === 'advanced'\n ? {\n // Eventual consistency is required for advanced querying capabilities\n // like \"$search\" or parts of \"$filter\".\n // If a new user/group is not found, it'll eventually be imported on a subsequent read\n ConsistencyLevel: 'eventual',\n }\n : {};\n\n let response = await this.requestApi(path, query, headers);\n\n for (;;) {\n if (response.status !== 200) {\n await this.handleError(path, response);\n }\n\n const result = await response.json();\n\n // Graph API return array of collections\n const elements: T[] = result.value;\n\n yield* elements;\n\n // Follow cursor to the next page if one is available\n if (!result['@odata.nextLink']) {\n return;\n }\n\n response = await this.requestRaw(result['@odata.nextLink'], headers);\n }\n }\n\n /**\n * Abstract on top of {@link MicrosoftGraphClient.requestRaw}\n *\n * @public\n * @param path - Resource in Microsoft Graph\n * @param query - OData Query {@link ODataQuery}\n * @param headers - optional HTTP headers\n */\n async requestApi(\n path: string,\n query?: ODataQuery,\n headers?: Record<string, string>,\n ): Promise<Response> {\n const queryString = qs.stringify(\n {\n $search: query?.search,\n $filter: query?.filter,\n $select: query?.select?.join(','),\n $expand: query?.expand,\n $count: query?.count,\n },\n {\n addQueryPrefix: true,\n // Microsoft Graph doesn't like an encoded query string\n encode: false,\n },\n );\n\n return await this.requestRaw(\n `${this.baseUrl}/${path}${queryString}`,\n headers,\n );\n }\n\n /**\n * Makes a HTTP call to Graph API with token\n *\n * @param url - HTTP Endpoint of Graph API\n * @param headers - optional HTTP headers\n */\n async requestRaw(\n url: string,\n headers?: Record<string, string>,\n ): Promise<Response> {\n // Make sure that we always have a valid access token (might be cached)\n const token = await this.pca.acquireTokenByClientCredential({\n scopes: ['https://graph.microsoft.com/.default'],\n });\n\n if (!token) {\n throw new Error('Error while requesting token for Microsoft Graph');\n }\n\n return await fetch(url, {\n headers: {\n ...headers,\n Authorization: `Bearer ${token.accessToken}`,\n },\n });\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/user | User}\n * from Graph API\n *\n * @public\n * @param userId - The unique identifier for the `User` resource\n * @param query - OData Query {@link ODataQuery}\n *\n */\n async getUserProfile(\n userId: string,\n query?: ODataQuery,\n ): Promise<MicrosoftGraph.User> {\n const response = await this.requestApi(`users/${userId}`, query);\n\n if (response.status !== 200) {\n await this.handleError('user profile', response);\n }\n\n return await response.json();\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/profilephoto | profilePhoto}\n * of `User` from Graph API with size limit\n *\n * @param userId - The unique identifier for the `User` resource\n * @param maxSize - Maximum pixel height of the photo\n *\n */\n async getUserPhotoWithSizeLimit(\n userId: string,\n maxSize: number,\n ): Promise<string | undefined> {\n return await this.getPhotoWithSizeLimit('users', userId, maxSize);\n }\n\n async getUserPhoto(\n userId: string,\n sizeId?: string,\n ): Promise<string | undefined> {\n return await this.getPhoto('users', userId, sizeId);\n }\n\n /**\n * Get a collection of\n * {@link https://docs.microsoft.com/en-us/graph/api/resources/user | User}\n * from Graph API and return as `AsyncIterable`\n *\n * @public\n * @param query - OData Query {@link ODataQuery}\n * @param queryMode - Mode to use while querying. Some features are only available at \"advanced\".\n */\n async *getUsers(\n query?: ODataQuery,\n queryMode?: 'basic' | 'advanced',\n ): AsyncIterable<MicrosoftGraph.User> {\n yield* this.requestCollection<MicrosoftGraph.User>(\n `users`,\n query,\n queryMode,\n );\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/profilephoto | profilePhoto}\n * of `Group` from Graph API with size limit\n *\n * @param groupId - The unique identifier for the `Group` resource\n * @param maxSize - Maximum pixel height of the photo\n *\n */\n async getGroupPhotoWithSizeLimit(\n groupId: string,\n maxSize: number,\n ): Promise<string | undefined> {\n return await this.getPhotoWithSizeLimit('groups', groupId, maxSize);\n }\n\n async getGroupPhoto(\n groupId: string,\n sizeId?: string,\n ): Promise<string | undefined> {\n return await this.getPhoto('groups', groupId, sizeId);\n }\n\n /**\n * Get a collection of\n * {@link https://docs.microsoft.com/en-us/graph/api/resources/group | Group}\n * from Graph API and return as `AsyncIterable`\n *\n * @public\n * @param query - OData Query {@link ODataQuery}\n * @param queryMode - Mode to use while querying. Some features are only available at \"advanced\".\n */\n async *getGroups(\n query?: ODataQuery,\n queryMode?: 'basic' | 'advanced',\n ): AsyncIterable<MicrosoftGraph.Group> {\n yield* this.requestCollection<MicrosoftGraph.Group>(\n `groups`,\n query,\n queryMode,\n );\n }\n\n /**\n * Get a collection of\n * {@link https://docs.microsoft.com/en-us/graph/api/resources/user | User}\n * belonging to a `Group` from Graph API and return as `AsyncIterable`\n * @public\n * @param groupId - The unique identifier for the `Group` resource\n *\n */\n async *getGroupMembers(groupId: string): AsyncIterable<GroupMember> {\n yield* this.requestCollection<GroupMember>(`groups/${groupId}/members`);\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/organization | Organization}\n * from Graph API\n * @public\n * @param tenantId - The unique identifier for the `Organization` resource\n *\n */\n async getOrganization(\n tenantId: string,\n ): Promise<MicrosoftGraph.Organization> {\n const response = await this.requestApi(`organization/${tenantId}`);\n\n if (response.status !== 200) {\n await this.handleError(`organization/${tenantId}`, response);\n }\n\n return await response.json();\n }\n\n /**\n * Get {@link https://docs.microsoft.com/en-us/graph/api/resources/profilephoto | profilePhoto}\n * from Graph API\n *\n * @param entityName - type of parent resource, either `User` or `Group`\n * @param id - The unique identifier for the {@link entityName | entityName} resource\n * @param maxSize - Maximum pixel height of the photo\n *\n */\n private async getPhotoWithSizeLimit(\n entityName: string,\n id: string,\n maxSize: number,\n ): Promise<string | undefined> {\n const response = await this.requestApi(`${entityName}/${id}/photos`);\n\n if (response.status === 404) {\n return undefined;\n } else if (response.status !== 200) {\n await this.handleError(`${entityName} photos`, response);\n }\n\n const result = await response.json();\n const photos = result.value as MicrosoftGraph.ProfilePhoto[];\n let selectedPhoto: MicrosoftGraph.ProfilePhoto | undefined = undefined;\n\n // Find the biggest picture that is smaller than the max size\n for (const p of photos) {\n if (\n !selectedPhoto ||\n (p.height! >= selectedPhoto.height! && p.height! <= maxSize)\n ) {\n selectedPhoto = p;\n }\n }\n\n if (!selectedPhoto) {\n return undefined;\n }\n\n return await this.getPhoto(entityName, id, selectedPhoto.id!);\n }\n\n private async getPhoto(\n entityName: string,\n id: string,\n sizeId?: string,\n ): Promise<string | undefined> {\n const path = sizeId\n ? `${entityName}/${id}/photos/${sizeId}/$value`\n : `${entityName}/${id}/photo/$value`;\n const response = await this.requestApi(path);\n\n if (response.status === 404) {\n return undefined;\n } else if (response.status !== 200) {\n await this.handleError('photo', response);\n }\n\n return `data:image/jpeg;base64,${Buffer.from(\n await response.arrayBuffer(),\n ).toString('base64')}`;\n }\n\n private async handleError(path: string, response: Response): Promise<void> {\n const result = await response.json();\n const error = result.error as MicrosoftGraph.PublicError;\n\n throw new Error(\n `Error while reading ${path} from Microsoft Graph: ${error.code} - ${error.message}`,\n );\n }\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { trimEnd } from 'lodash';\n\nconst DEFAULT_AUTHORITY = 'https://login.microsoftonline.com';\nconst DEFAULT_PROVIDER_ID = 'default';\nconst DEFAULT_TARGET = 'https://graph.microsoft.com/v1.0';\n\n/**\n * The configuration parameters for a single Microsoft Graph provider.\n *\n * @public\n */\nexport type MicrosoftGraphProviderConfig = {\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 prefix of the target that this matches on, e.g.\n * \"https://graph.microsoft.com/v1.0\", with no trailing slash.\n */\n target: string;\n /**\n * The auth authority used.\n *\n * E.g. \"https://login.microsoftonline.com\"\n */\n authority?: string;\n /**\n * The tenant whose org data we are interested in.\n */\n tenantId: string;\n /**\n * The OAuth client ID to use for authenticating requests.\n */\n clientId: string;\n /**\n * The OAuth client secret to use for authenticating requests.\n */\n clientSecret: string;\n /**\n * The filter to apply to extract users.\n *\n * E.g. \"accountEnabled eq true and userType eq 'member'\"\n */\n userFilter?: string;\n /**\n * The \"expand\" argument to apply to users.\n *\n * E.g. \"manager\".\n */\n userExpand?: string;\n /**\n * The filter to apply to extract users by groups memberships.\n *\n * E.g. \"displayName eq 'Backstage Users'\"\n */\n userGroupMemberFilter?: string;\n /**\n * The search criteria to apply to extract users by groups memberships.\n *\n * E.g. \"\\\"displayName:-team\\\"\" would only match groups which contain '-team'\n */\n userGroupMemberSearch?: string;\n /**\n * The \"expand\" argument to apply to groups.\n *\n * E.g. \"member\".\n */\n groupExpand?: string;\n /**\n * The filter to apply to extract groups.\n *\n * E.g. \"securityEnabled eq false and mailEnabled eq true\"\n */\n groupFilter?: string;\n /**\n * The search criteria to apply to extract groups.\n *\n * E.g. \"\\\"displayName:-team\\\"\" would only match groups which contain '-team'\n */\n groupSearch?: string;\n\n /**\n * The fields to be fetched on query.\n *\n * E.g. [\"id\", \"displayName\", \"description\"]\n */\n groupSelect?: string[];\n\n /**\n * By default, the Microsoft Graph API only provides the basic feature set\n * for querying. Certain features are limited to advanced query capabilities\n * (see https://docs.microsoft.com/en-us/graph/aad-advanced-queries)\n * and need to be enabled.\n *\n * Some features like `$expand` are not available for advanced queries, though.\n */\n queryMode?: 'basic' | 'advanced';\n};\n\n/**\n * Parses configuration.\n *\n * @param config - The root of the msgraph config hierarchy\n *\n * @public\n * @deprecated Replaced by not exported `readProviderConfigs` and kept for backwards compatibility only.\n */\nexport function readMicrosoftGraphConfig(\n config: Config,\n): MicrosoftGraphProviderConfig[] {\n const providers: MicrosoftGraphProviderConfig[] = [];\n const providerConfigs = config.getOptionalConfigArray('providers') ?? [];\n\n for (const providerConfig of providerConfigs) {\n const target = trimEnd(providerConfig.getString('target'), '/');\n\n const authority = providerConfig.getOptionalString('authority')\n ? trimEnd(providerConfig.getOptionalString('authority'), '/')\n : DEFAULT_AUTHORITY;\n const tenantId = providerConfig.getString('tenantId');\n const clientId = providerConfig.getString('clientId');\n const clientSecret = providerConfig.getString('clientSecret');\n\n const userExpand = providerConfig.getOptionalString('userExpand');\n const userFilter = providerConfig.getOptionalString('userFilter');\n const userGroupMemberFilter = providerConfig.getOptionalString(\n 'userGroupMemberFilter',\n );\n const userGroupMemberSearch = providerConfig.getOptionalString(\n 'userGroupMemberSearch',\n );\n const groupExpand = providerConfig.getOptionalString('groupExpand');\n const groupFilter = providerConfig.getOptionalString('groupFilter');\n const groupSearch = providerConfig.getOptionalString('groupSearch');\n\n if (userFilter && userGroupMemberFilter) {\n throw new Error(\n `userFilter and userGroupMemberFilter are mutually exclusive, only one can be specified.`,\n );\n }\n if (userFilter && userGroupMemberSearch) {\n throw new Error(\n `userGroupMemberSearch cannot be specified when userFilter is defined.`,\n );\n }\n\n const groupSelect = providerConfig.getOptionalStringArray('groupSelect');\n const queryMode = providerConfig.getOptionalString('queryMode');\n if (\n queryMode !== undefined &&\n queryMode !== 'basic' &&\n queryMode !== 'advanced'\n ) {\n throw new Error(`queryMode must be one of: basic, advanced`);\n }\n\n providers.push({\n id: target,\n target,\n authority,\n tenantId,\n clientId,\n clientSecret,\n userExpand,\n userFilter,\n userGroupMemberFilter,\n userGroupMemberSearch,\n groupExpand,\n groupFilter,\n groupSearch,\n groupSelect,\n queryMode,\n });\n }\n\n return providers;\n}\n\nexport function readProviderConfigs(\n config: Config,\n): MicrosoftGraphProviderConfig[] {\n const providersConfig = config.getOptionalConfig(\n 'catalog.providers.microsoftGraphOrg',\n );\n if (!providersConfig) {\n return [];\n }\n\n if (providersConfig.has('clientId')) {\n // simple/single config variant\n return [readProviderConfig(DEFAULT_PROVIDER_ID, providersConfig)];\n }\n\n return providersConfig.keys().map(id => {\n const providerConfig = providersConfig.getConfig(id);\n\n return readProviderConfig(id, providerConfig);\n });\n}\n\nexport function readProviderConfig(\n id: string,\n config: Config,\n): MicrosoftGraphProviderConfig {\n const target = trimEnd(\n config.getOptionalString('target') ?? DEFAULT_TARGET,\n '/',\n );\n const authority = trimEnd(\n config.getOptionalString('authority') ?? DEFAULT_AUTHORITY,\n '/',\n );\n\n const clientId = config.getString('clientId');\n const clientSecret = config.getString('clientSecret');\n const tenantId = config.getString('tenantId');\n\n const userExpand = config.getOptionalString('user.expand');\n const userFilter = config.getOptionalString('user.filter');\n\n const groupExpand = config.getOptionalString('group.expand');\n const groupFilter = config.getOptionalString('group.filter');\n const groupSearch = config.getOptionalString('group.search');\n const groupSelect = config.getOptionalStringArray('group.select');\n\n const userGroupMemberFilter = config.getOptionalString(\n 'userGroupMember.filter',\n );\n const userGroupMemberSearch = config.getOptionalString(\n 'userGroupMember.search',\n );\n\n if (userFilter && userGroupMemberFilter) {\n throw new Error(\n `userFilter and userGroupMemberFilter are mutually exclusive, only one can be specified.`,\n );\n }\n if (userFilter && userGroupMemberSearch) {\n throw new Error(\n `userGroupMemberSearch cannot be specified when userFilter is defined.`,\n );\n }\n\n return {\n id,\n target,\n authority,\n clientId,\n clientSecret,\n tenantId,\n userExpand,\n userFilter,\n groupExpand,\n groupFilter,\n groupSearch,\n groupSelect,\n userGroupMemberFilter,\n userGroupMemberSearch,\n };\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The (primary) user email. Also used by the Microsoft auth provider to resolve the User entity.\n *\n * @public\n */\nexport const MICROSOFT_EMAIL_ANNOTATION = 'microsoft.com/email';\n\n/**\n * The tenant id used by the Microsoft Graph API\n *\n * @public\n */\nexport const MICROSOFT_GRAPH_TENANT_ID_ANNOTATION =\n 'graph.microsoft.com/tenant-id';\n\n/**\n * The group id used by the Microsoft Graph API\n *\n * @public\n */\nexport const MICROSOFT_GRAPH_GROUP_ID_ANNOTATION =\n 'graph.microsoft.com/group-id';\n\n/**\n * The user id used by the Microsoft Graph API\n *\n * @public\n */\nexport const MICROSOFT_GRAPH_USER_ID_ANNOTATION = 'graph.microsoft.com/user-id';\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Takes an input string and cleans it up to become suitable as an entity name.\n *\n * @public\n */\nexport function normalizeEntityName(name: string): string {\n let cleaned = name\n .trim()\n .toLocaleLowerCase()\n .replace(/[^a-zA-Z0-9_\\-\\.]/g, '_');\n\n // invalid to end with _\n while (cleaned.endsWith('_')) {\n cleaned = cleaned.substring(0, cleaned.length - 1);\n }\n\n // cleans up format for groups like 'my group (Reader)'\n while (cleaned.includes('__')) {\n // replaceAll from node.js >= 15\n cleaned = cleaned.replace('__', '_');\n }\n\n return cleaned;\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { GroupEntity, UserEntity } from '@backstage/catalog-model';\n\n// TODO: Copied from plugin-catalog-backend, but we could also export them from\n// there. Or move them to catalog-model.\n\nexport function buildOrgHierarchy(groups: GroupEntity[]) {\n const groupsByName = new Map(groups.map(g => [g.metadata.name, g]));\n\n //\n // Make sure that g.parent.children contain g\n //\n\n for (const group of groups) {\n const selfName = group.metadata.name;\n const parentName = group.spec.parent;\n if (parentName) {\n const parent = groupsByName.get(parentName);\n if (parent && !parent.spec.children.includes(selfName)) {\n parent.spec.children.push(selfName);\n }\n }\n }\n\n //\n // Make sure that g.children.parent is g\n //\n\n for (const group of groups) {\n const selfName = group.metadata.name;\n for (const childName of group.spec.children) {\n const child = groupsByName.get(childName);\n if (child && !child.spec.parent) {\n child.spec.parent = selfName;\n }\n }\n }\n}\n\n// Ensure that users have their transitive group memberships. Requires that\n// the groups were previously processed with buildOrgHierarchy()\nexport function buildMemberOf(groups: GroupEntity[], users: UserEntity[]) {\n const groupsByName = new Map(groups.map(g => [g.metadata.name, g]));\n\n users.forEach(user => {\n const transitiveMemberOf = new Set<string>();\n\n const todo = [\n ...(user.spec.memberOf ?? []),\n ...groups\n .filter(g => g.spec.members?.includes(user.metadata.name))\n .map(g => g.metadata.name),\n ];\n\n for (;;) {\n const current = todo.pop();\n if (!current) {\n break;\n }\n\n if (!transitiveMemberOf.has(current)) {\n transitiveMemberOf.add(current);\n const group = groupsByName.get(current);\n if (group?.spec.parent) {\n todo.push(group.spec.parent);\n }\n }\n }\n\n user.spec.memberOf = [...transitiveMemberOf];\n });\n}\n","/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n GroupEntity,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport * as MicrosoftGraph from '@microsoft/microsoft-graph-types';\nimport limiterFactory from 'p-limit';\nimport { Logger } from 'winston';\nimport { MicrosoftGraphClient } from './client';\nimport {\n MICROSOFT_EMAIL_ANNOTATION,\n MICROSOFT_GRAPH_GROUP_ID_ANNOTATION,\n MICROSOFT_GRAPH_TENANT_ID_ANNOTATION,\n MICROSOFT_GRAPH_USER_ID_ANNOTATION,\n} from './constants';\nimport { normalizeEntityName } from './helper';\nimport { buildMemberOf, buildOrgHierarchy } from './org';\nimport {\n GroupTransformer,\n OrganizationTransformer,\n UserTransformer,\n} from './types';\n\n/**\n * The default implementation of the transformation from a graph user entry to\n * a User entity.\n *\n * @public\n */\nexport async function defaultUserTransformer(\n user: MicrosoftGraph.User,\n userPhoto?: string,\n): Promise<UserEntity | undefined> {\n if (!user.id || !user.displayName || !user.mail) {\n return undefined;\n }\n\n const name = normalizeEntityName(user.mail);\n const entity: UserEntity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'User',\n metadata: {\n name,\n annotations: {\n [MICROSOFT_EMAIL_ANNOTATION]: user.mail!,\n [MICROSOFT_GRAPH_USER_ID_ANNOTATION]: user.id!,\n },\n },\n spec: {\n profile: {\n displayName: user.displayName!,\n email: user.mail!,\n\n // TODO: Additional fields?\n // jobTitle: user.jobTitle || undefined,\n // officeLocation: user.officeLocation || undefined,\n // mobilePhone: user.mobilePhone || undefined,\n },\n memberOf: [],\n },\n };\n\n if (userPhoto) {\n entity.spec.profile!.picture = userPhoto;\n }\n\n return entity;\n}\n\nexport async function readMicrosoftGraphUsers(\n client: MicrosoftGraphClient,\n options: {\n queryMode?: 'basic' | 'advanced';\n userFilter?: string;\n userExpand?: string;\n transformer?: UserTransformer;\n logger: Logger;\n },\n): Promise<{\n users: UserEntity[]; // With all relations empty\n}> {\n const users: UserEntity[] = [];\n const limiter = limiterFactory(10);\n\n const transformer = options.transformer ?? defaultUserTransformer;\n const promises: Promise<void>[] = [];\n\n for await (const user of client.getUsers(\n {\n filter: options.userFilter,\n expand: options.userExpand,\n },\n options.queryMode,\n )) {\n // Process all users in parallel, otherwise it can take quite some time\n promises.push(\n limiter(async () => {\n let userPhoto;\n try {\n userPhoto = await client.getUserPhotoWithSizeLimit(\n user.id!,\n // We are limiting the photo size, as users with full resolution photos\n // can make the Backstage API slow\n 120,\n );\n } catch (e) {\n options.logger.warn(`Unable to load photo for ${user.id}`);\n }\n\n const entity = await transformer(user, userPhoto);\n\n if (!entity) {\n return;\n }\n\n users.push(entity);\n }),\n );\n }\n\n // Wait for all users and photos to be downloaded\n await Promise.all(promises);\n\n return { users };\n}\n\nexport async function readMicrosoftGraphUsersInGroups(\n client: MicrosoftGraphClient,\n options: {\n queryMode?: 'basic' | 'advanced';\n userExpand?: string;\n userGroupMemberSearch?: string;\n userGroupMemberFilter?: string;\n groupExpand?: string;\n transformer?: UserTransformer;\n logger: Logger;\n },\n): Promise<{\n users: UserEntity[]; // With all relations empty\n}> {\n const users: UserEntity[] = [];\n\n const limiter = limiterFactory(10);\n\n const transformer = options.transformer ?? defaultUserTransformer;\n const userGroupMemberPromises: Promise<void>[] = [];\n const userPromises: Promise<void>[] = [];\n\n const groupMemberUsers: Set<string> = new Set();\n\n for await (const group of client.getGroups(\n {\n expand: options.groupExpand,\n search: options.userGroupMemberSearch,\n filter: options.userGroupMemberFilter,\n },\n options.queryMode,\n )) {\n // Process all groups in parallel, otherwise it can take quite some time\n userGroupMemberPromises.push(\n limiter(async () => {\n for await (const member of client.getGroupMembers(group.id!)) {\n if (!member.id) {\n continue;\n }\n\n if (member['@odata.type'] === '#microsoft.graph.user') {\n groupMemberUsers.add(member.id);\n }\n }\n }),\n );\n }\n\n // Wait for all group members\n await Promise.all(userGroupMemberPromises);\n\n options.logger.info(`groupMemberUsers ${groupMemberUsers.size}`);\n for (const userId of groupMemberUsers) {\n // Process all users in parallel, otherwise it can take quite some time\n userPromises.push(\n limiter(async () => {\n let user;\n let userPhoto;\n try {\n user = await client.getUserProfile(userId, {\n expand: options.userExpand,\n });\n } catch (e) {\n options.logger.warn(`Unable to load user for ${userId}`);\n }\n if (user) {\n try {\n userPhoto = await client.getUserPhotoWithSizeLimit(\n user.id!,\n // We are limiting the photo size, as users with full resolution photos\n // can make the Backstage API slow\n 120,\n );\n } catch (e) {\n options.logger.warn(`Unable to load userphoto for ${userId}`);\n }\n\n const entity = await transformer(user, userPhoto);\n\n if (!entity) {\n return;\n }\n users.push(entity);\n }\n }),\n );\n }\n\n // Wait for all users and photos to be downloaded\n await Promise.all(userPromises);\n\n return { users };\n}\n\n/**\n * The default implementation of the transformation from a graph organization\n * entry to a Group entity.\n *\n * @public\n */\nexport async function defaultOrganizationTransformer(\n organization: MicrosoftGraph.Organization,\n): Promise<GroupEntity | undefined> {\n if (!organization.id || !organization.displayName) {\n return undefined;\n }\n\n const name = normalizeEntityName(organization.displayName!);\n return {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'Group',\n metadata: {\n name: name,\n description: organization.displayName!,\n annotations: {\n [MICROSOFT_GRAPH_TENANT_ID_ANNOTATION]: organization.id!,\n },\n },\n spec: {\n type: 'root',\n profile: {\n displayName: organization.displayName!,\n },\n children: [],\n },\n };\n}\n\nexport async function readMicrosoftGraphOrganization(\n client: MicrosoftGraphClient,\n tenantId: string,\n options?: { transformer?: OrganizationTransformer },\n): Promise<{\n rootGroup?: GroupEntity; // With all relations empty\n}> {\n // For now we expect a single root organization\n const organization = await client.getOrganization(tenantId);\n const transformer = options?.transformer ?? defaultOrganizationTransformer;\n const rootGroup = await transformer(organization);\n\n return { rootGroup };\n}\n\nfunction extractGroupName(group: MicrosoftGraph.Group): string {\n if (group.securityEnabled) {\n return group.displayName as string;\n }\n return (group.mailNickname || group.displayName) as string;\n}\n\n/**\n * The default implementation of the transformation from a graph group entry to\n * a Group entity.\n *\n * @public\n */\nexport async function defaultGroupTransformer(\n group: MicrosoftGraph.Group,\n groupPhoto?: string,\n): Promise<GroupEntity | undefined> {\n if (!group.id || !group.displayName) {\n return undefined;\n }\n\n const name = normalizeEntityName(extractGroupName(group));\n const entity: GroupEntity = {\n apiVersion: 'backstage.io/v1alpha1',\n kind: 'Group',\n metadata: {\n name: name,\n annotations: {\n [MICROSOFT_GRAPH_GROUP_ID_ANNOTATION]: group.id,\n },\n },\n spec: {\n type: 'team',\n profile: {},\n children: [],\n },\n };\n\n if (group.description) {\n entity.metadata.description = group.description;\n }\n if (group.displayName) {\n entity.spec.profile!.displayName = group.displayName;\n }\n if (group.mail) {\n entity.spec.profile!.email = group.mail;\n }\n if (groupPhoto) {\n entity.spec.profile!.picture = groupPhoto;\n }\n\n return entity;\n}\n\nexport async function readMicrosoftGraphGroups(\n client: MicrosoftGraphClient,\n tenantId: string,\n options?: {\n queryMode?: 'basic' | 'advanced';\n groupExpand?: string;\n groupFilter?: string;\n groupSearch?: string;\n groupSelect?: string[];\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n },\n): Promise<{\n groups: GroupEntity[]; // With all relations empty\n rootGroup: GroupEntity | undefined; // With all relations empty\n groupMember: Map<string, Set<string>>;\n groupMemberOf: Map<string, Set<string>>;\n}> {\n const groups: GroupEntity[] = [];\n const groupMember: Map<string, Set<string>> = new Map();\n const groupMemberOf: Map<string, Set<string>> = new Map();\n const limiter = limiterFactory(10);\n\n const { rootGroup } = await readMicrosoftGraphOrganization(client, tenantId, {\n transformer: options?.organizationTransformer,\n });\n if (rootGroup) {\n groupMember.set(rootGroup.metadata.name, new Set<string>());\n groups.push(rootGroup);\n }\n\n const transformer = options?.groupTransformer ?? defaultGroupTransformer;\n const promises: Promise<void>[] = [];\n\n for await (const group of client.getGroups(\n {\n expand: options?.groupExpand,\n search: options?.groupSearch,\n filter: options?.groupFilter,\n select: options?.groupSelect,\n },\n options?.queryMode,\n )) {\n // Process all groups in parallel, otherwise it can take quite some time\n promises.push(\n limiter(async () => {\n // TODO: Loading groups photos doesn't work right now as Microsoft Graph\n // doesn't allows this yet: https://microsoftgraph.uservoice.com/forums/920506-microsoft-graph-feature-requests/suggestions/37884922-allow-application-to-set-or-update-a-group-s-photo\n /* const groupPhoto = await client.getGroupPhotoWithSizeLimit(\n group.id!,\n // We are limiting the photo size, as groups with full resolution photos\n // can make the Backstage API slow\n 120,\n );*/\n\n const entity = await transformer(group /* , groupPhoto*/);\n\n if (!entity) {\n return;\n }\n\n for await (const member of client.getGroupMembers(group.id!)) {\n if (!member.id) {\n continue;\n }\n\n if (member['@odata.type'] === '#microsoft.graph.user') {\n ensureItem(groupMemberOf, member.id, group.id!);\n }\n\n if (member['@odata.type'] === '#microsoft.graph.group') {\n ensureItem(groupMember, group.id!, member.id);\n }\n }\n\n groups.push(entity);\n }),\n );\n }\n\n // Wait for all group members and photos to be loaded\n await Promise.all(promises);\n\n return {\n groups,\n rootGroup,\n groupMember,\n groupMemberOf,\n };\n}\n\nexport function resolveRelations(\n rootGroup: GroupEntity | undefined,\n groups: GroupEntity[],\n users: UserEntity[],\n groupMember: Map<string, Set<string>>,\n groupMemberOf: Map<string, Set<string>>,\n) {\n // Build reference lookup tables, we reference them by the id the the graph\n const groupMap: Map<string, GroupEntity> = new Map(); // by group-id or tenant-id\n\n for (const group of groups) {\n if (group.metadata.annotations![MICROSOFT_GRAPH_GROUP_ID_ANNOTATION]) {\n groupMap.set(\n group.metadata.annotations![MICROSOFT_GRAPH_GROUP_ID_ANNOTATION],\n group,\n );\n }\n if (group.metadata.annotations![MICROSOFT_GRAPH_TENANT_ID_ANNOTATION]) {\n groupMap.set(\n group.metadata.annotations![MICROSOFT_GRAPH_TENANT_ID_ANNOTATION],\n group,\n );\n }\n }\n\n // Resolve all member relationships into the reverse direction\n const parentGroups = new Map<string, Set<string>>();\n\n groupMember.forEach((members, groupId) =>\n members.forEach(m => ensureItem(parentGroups, m, groupId)),\n );\n\n // Make sure every group (except root) has at least one parent. If the parent is missing, add the root.\n if (rootGroup) {\n const tenantId =\n rootGroup.metadata.annotations![MICROSOFT_GRAPH_TENANT_ID_ANNOTATION];\n\n groups.forEach(group => {\n const groupId =\n group.metadata.annotations![MICROSOFT_GRAPH_GROUP_ID_ANNOTATION];\n\n if (!groupId) {\n return;\n }\n\n if (retrieveItems(parentGroups, groupId).size === 0) {\n ensureItem(parentGroups, groupId, tenantId);\n ensureItem(groupMember, tenantId, groupId);\n }\n });\n }\n\n groups.forEach(group => {\n const id =\n group.metadata.annotations![MICROSOFT_GRAPH_GROUP_ID_ANNOTATION] ??\n group.metadata.annotations![MICROSOFT_GRAPH_TENANT_ID_ANNOTATION];\n\n retrieveItems(groupMember, id).forEach(m => {\n const childGroup = groupMap.get(m);\n if (childGroup) {\n group.spec.children.push(stringifyEntityRef(childGroup));\n }\n });\n\n retrieveItems(parentGroups, id).forEach(p => {\n const parentGroup = groupMap.get(p);\n if (parentGroup) {\n // TODO: Only having a single parent group might not match every companies model, but fine for now.\n group.spec.parent = stringifyEntityRef(parentGroup);\n }\n });\n });\n\n // Make sure that all groups have proper parents and children\n buildOrgHierarchy(groups);\n\n // Set relations for all users\n users.forEach(user => {\n const id = user.metadata.annotations![MICROSOFT_GRAPH_USER_ID_ANNOTATION];\n\n retrieveItems(groupMemberOf, id).forEach(p => {\n const parentGroup = groupMap.get(p);\n if (parentGroup) {\n if (!user.spec.memberOf) {\n user.spec.memberOf = [];\n }\n user.spec.memberOf.push(stringifyEntityRef(parentGroup));\n }\n });\n });\n\n // Make sure all transitive memberships are available\n buildMemberOf(groups, users);\n}\n\n/**\n * Reads an entire org as Group and User entities.\n *\n * @public\n */\nexport async function readMicrosoftGraphOrg(\n client: MicrosoftGraphClient,\n tenantId: string,\n options: {\n userExpand?: string;\n userFilter?: string;\n userGroupMemberSearch?: string;\n userGroupMemberFilter?: string;\n groupExpand?: string;\n groupSearch?: string;\n groupFilter?: string;\n groupSelect?: string[];\n queryMode?: 'basic' | 'advanced';\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n logger: Logger;\n },\n): Promise<{ users: UserEntity[]; groups: GroupEntity[] }> {\n const users: UserEntity[] = [];\n\n if (options.userGroupMemberFilter || options.userGroupMemberSearch) {\n const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups(\n client,\n {\n queryMode: options.queryMode,\n userGroupMemberFilter: options.userGroupMemberFilter,\n userGroupMemberSearch: options.userGroupMemberSearch,\n transformer: options.userTransformer,\n logger: options.logger,\n },\n );\n users.push(...usersInGroups);\n } else {\n const { users: usersWithFilter } = await readMicrosoftGraphUsers(client, {\n queryMode: options.queryMode,\n userFilter: options.userFilter,\n userExpand: options.userExpand,\n transformer: options.userTransformer,\n logger: options.logger,\n });\n users.push(...usersWithFilter);\n }\n const { groups, rootGroup, groupMember, groupMemberOf } =\n await readMicrosoftGraphGroups(client, tenantId, {\n queryMode: options.queryMode,\n groupSearch: options.groupSearch,\n groupFilter: options.groupFilter,\n groupSelect: options.groupSelect,\n groupTransformer: options.groupTransformer,\n organizationTransformer: options.organizationTransformer,\n });\n\n resolveRelations(rootGroup, groups, users, groupMember, groupMemberOf);\n users.sort((a, b) => a.metadata.name.localeCompare(b.metadata.name));\n groups.sort((a, b) => a.metadata.name.localeCompare(b.metadata.name));\n\n return { users, groups };\n}\n\nfunction ensureItem(\n target: Map<string, Set<string>>,\n key: string,\n value: string,\n) {\n let set = target.get(key);\n if (!set) {\n set = new Set();\n target.set(key, set);\n }\n set!.add(value);\n}\n\nfunction retrieveItems(\n target: Map<string, Set<string>>,\n key: string,\n): Set<string> {\n return target.get(key) ?? new Set();\n}\n","/*\n * Copyright 2021 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 { TaskRunner } from '@backstage/backend-tasks';\nimport {\n ANNOTATION_LOCATION,\n ANNOTATION_ORIGIN_LOCATION,\n Entity,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport {\n EntityProvider,\n EntityProviderConnection,\n} from '@backstage/plugin-catalog-backend';\nimport { merge } from 'lodash';\nimport * as uuid from 'uuid';\nimport { Logger } from 'winston';\nimport {\n GroupTransformer,\n MicrosoftGraphClient,\n MicrosoftGraphProviderConfig,\n MICROSOFT_GRAPH_GROUP_ID_ANNOTATION,\n MICROSOFT_GRAPH_TENANT_ID_ANNOTATION,\n MICROSOFT_GRAPH_USER_ID_ANNOTATION,\n OrganizationTransformer,\n readMicrosoftGraphConfig,\n readMicrosoftGraphOrg,\n UserTransformer,\n} from '../microsoftGraph';\nimport { readProviderConfigs } from '../microsoftGraph/config';\n\n/**\n * Options for {@link MicrosoftGraphOrgEntityProvider}.\n *\n * @public\n */\nexport type MicrosoftGraphOrgEntityProviderOptions =\n | MicrosoftGraphOrgEntityProviderLegacyOptions\n | {\n /**\n * The logger to use.\n */\n logger: Logger;\n\n /**\n * The refresh schedule to use.\n *\n * @remarks\n *\n * If you pass in 'manual', you are responsible for calling the `read` method\n * manually at some interval.\n *\n * But more commonly you will pass in the result of\n * {@link @backstage/backend-tasks#PluginTaskScheduler.createScheduledTaskRunner}\n * to enable automatic scheduling of tasks.\n */\n schedule: 'manual' | TaskRunner;\n\n /**\n * The function that transforms a user entry in msgraph to an entity.\n * Optionally, you can pass separate transformers per provider ID.\n */\n userTransformer?: UserTransformer | Record<string, UserTransformer>;\n\n /**\n * The function that transforms a group entry in msgraph to an entity.\n * Optionally, you can pass separate transformers per provider ID.\n */\n groupTransformer?: GroupTransformer | Record<string, GroupTransformer>;\n\n /**\n * The function that transforms an organization entry in msgraph to an entity.\n * Optionally, you can pass separate transformers per provider ID.\n */\n organizationTransformer?:\n | OrganizationTransformer\n | Record<string, OrganizationTransformer>;\n };\n\n/**\n * Legacy options for {@link MicrosoftGraphOrgEntityProvider}\n * based on `catalog.processors.microsoftGraphOrg`.\n *\n * @public\n * @deprecated This interface exists for backwards compatibility only and will be removed in the future.\n */\nexport interface MicrosoftGraphOrgEntityProviderLegacyOptions {\n /**\n * A unique, stable identifier for this provider.\n *\n * @example \"production\"\n */\n id: string;\n\n /**\n * The target that this provider should consume.\n *\n * Should exactly match the \"target\" field of one of the provider\n * configuration entries.\n */\n target: string;\n\n /**\n * The logger to use.\n */\n logger: Logger;\n\n /**\n * The refresh schedule to use.\n *\n * @remarks\n *\n * If you pass in 'manual', you are responsible for calling the `read` method\n * manually at some interval.\n *\n * But more commonly you will pass in the result of\n * {@link @backstage/backend-tasks#PluginTaskScheduler.createScheduledTaskRunner}\n * to enable automatic scheduling of tasks.\n */\n schedule: 'manual' | TaskRunner;\n\n /**\n * The function that transforms a user entry in msgraph to an entity.\n */\n userTransformer?: UserTransformer;\n\n /**\n * The function that transforms a group entry in msgraph to an entity.\n */\n groupTransformer?: GroupTransformer;\n\n /**\n * The function that transforms an organization entry in msgraph to an entity.\n */\n organizationTransformer?: OrganizationTransformer;\n}\n\n/**\n * Reads user and group entries out of Microsoft Graph, and provides them as\n * User and Group entities for the catalog.\n *\n * @public\n */\nexport class MicrosoftGraphOrgEntityProvider implements EntityProvider {\n private connection?: EntityProviderConnection;\n private scheduleFn?: () => Promise<void>;\n\n static fromConfig(\n configRoot: Config,\n options: MicrosoftGraphOrgEntityProviderOptions,\n ): MicrosoftGraphOrgEntityProvider[] {\n if ('id' in options) {\n return [\n MicrosoftGraphOrgEntityProvider.fromLegacyConfig(configRoot, options),\n ];\n }\n\n function getTransformer<T extends Function>(\n id: string,\n transformers?: T | Record<string, T>,\n ): T | undefined {\n if (['undefined', 'function'].includes(typeof transformers)) {\n return transformers as T;\n }\n\n return (transformers as Record<string, T>)[id];\n }\n\n return readProviderConfigs(configRoot).map(providerConfig => {\n const provider = new MicrosoftGraphOrgEntityProvider({\n id: providerConfig.id,\n provider: providerConfig,\n logger: options.logger,\n userTransformer: getTransformer(\n providerConfig.id,\n options.userTransformer,\n ),\n groupTransformer: getTransformer(\n providerConfig.id,\n options.groupTransformer,\n ),\n organizationTransformer: getTransformer(\n providerConfig.id,\n options.organizationTransformer,\n ),\n });\n provider.schedule(options.schedule);\n\n return provider;\n });\n }\n\n /**\n * @deprecated Exists for backwards compatibility only and will be removed in the future.\n */\n private static fromLegacyConfig(\n configRoot: Config,\n options: MicrosoftGraphOrgEntityProviderLegacyOptions,\n ): MicrosoftGraphOrgEntityProvider {\n options.logger.warn(\n 'Deprecated msgraph config \"catalog.processors.microsoftGraphOrg\" used. Use \"catalog.providers.microsoftGraphOrg\" instead. More info at https://github.com/backstage/backstage/blob/master/.changeset/long-bananas-rescue.md',\n );\n const config = configRoot.getOptionalConfig(\n 'catalog.processors.microsoftGraphOrg',\n );\n const providers = config ? readMicrosoftGraphConfig(config) : [];\n const provider = providers.find(p => options.target.startsWith(p.target));\n\n if (!provider) {\n throw new Error(\n `There is no Microsoft Graph Org provider that matches \"${options.target}\". Please add a configuration entry for it under \"catalog.processors.microsoftGraphOrg.providers\".`,\n );\n }\n\n const logger = options.logger.child({\n target: options.target,\n });\n\n const result = new MicrosoftGraphOrgEntityProvider({\n id: options.id,\n userTransformer: options.userTransformer,\n groupTransformer: options.groupTransformer,\n organizationTransformer: options.organizationTransformer,\n logger,\n provider,\n });\n\n result.schedule(options.schedule);\n\n return result;\n }\n\n constructor(\n private options: {\n id: string;\n provider: MicrosoftGraphProviderConfig;\n logger: Logger;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n },\n ) {}\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */\n getProviderName() {\n return `MicrosoftGraphOrgEntityProvider:${this.options.id}`;\n }\n\n /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */\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?: Logger }) {\n if (!this.connection) {\n throw new Error('Not initialized');\n }\n\n const logger = options?.logger ?? this.options.logger;\n const provider = this.options.provider;\n const { markReadComplete } = trackProgress(logger);\n const client = MicrosoftGraphClient.create(this.options.provider);\n const { users, groups } = await readMicrosoftGraphOrg(\n client,\n provider.tenantId,\n {\n userFilter: provider.userFilter,\n userGroupMemberFilter: provider.userGroupMemberFilter,\n userGroupMemberSearch: provider.userGroupMemberSearch,\n groupFilter: provider.groupFilter,\n groupSearch: provider.groupSearch,\n groupSelect: provider.groupSelect,\n queryMode: provider.queryMode,\n groupTransformer: this.options.groupTransformer,\n userTransformer: this.options.userTransformer,\n organizationTransformer: this.options.organizationTransformer,\n logger: logger,\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: `msgraph-org-provider:${this.options.id}`,\n entity: withLocations(this.options.id, entity),\n })),\n });\n\n markCommitComplete();\n }\n\n private schedule(\n schedule: MicrosoftGraphOrgEntityProviderOptions['schedule'],\n ) {\n if (schedule === 'manual') {\n return;\n }\n\n this.scheduleFn = async () => {\n const id = `${this.getProviderName()}:refresh`;\n await schedule.run({\n id,\n fn: async () => {\n const logger = this.options.logger.child({\n class: MicrosoftGraphOrgEntityProvider.prototype.constructor.name,\n taskId: id,\n taskInstanceId: uuid.v4(),\n });\n\n try {\n await this.read({ logger });\n } catch (error) {\n logger.error(error);\n }\n },\n });\n };\n }\n}\n\n// Helps wrap the timing and logging behaviors\nfunction trackProgress(logger: Logger) {\n let timestamp = Date.now();\n let summary: string;\n\n logger.info('Reading msgraph users and groups');\n\n function markReadComplete(read: { users: unknown[]; groups: unknown[] }) {\n summary = `${read.users.length} msgraph users and ${read.groups.length} msgraph 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\n// Makes sure that emitted entities have a proper location based on their uuid\nexport function withLocations(providerId: string, entity: Entity): Entity {\n const uid =\n entity.metadata.annotations?.[MICROSOFT_GRAPH_USER_ID_ANNOTATION] ||\n entity.metadata.annotations?.[MICROSOFT_GRAPH_GROUP_ID_ANNOTATION] ||\n entity.metadata.annotations?.[MICROSOFT_GRAPH_TENANT_ID_ANNOTATION] ||\n entity.metadata.name;\n const location = `msgraph:${providerId}/${encodeURIComponent(uid)}`;\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 * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport {\n CatalogProcessor,\n CatalogProcessorEmit,\n LocationSpec,\n processingResult,\n} from '@backstage/plugin-catalog-backend';\nimport { Logger } from 'winston';\nimport {\n GroupTransformer,\n MicrosoftGraphClient,\n MicrosoftGraphProviderConfig,\n OrganizationTransformer,\n readMicrosoftGraphConfig,\n readMicrosoftGraphOrg,\n UserTransformer,\n} from '../microsoftGraph';\n\n/**\n * Extracts teams and users out of the Microsoft Graph API.\n *\n * @public\n * @deprecated Use the MicrosoftGraphOrgEntityProvider instead.\n */\nexport class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {\n private readonly providers: MicrosoftGraphProviderConfig[];\n private readonly logger: Logger;\n private readonly userTransformer?: UserTransformer;\n private readonly groupTransformer?: GroupTransformer;\n private readonly organizationTransformer?: OrganizationTransformer;\n\n static fromConfig(\n config: Config,\n options: {\n logger: Logger;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n },\n ) {\n const c = config.getOptionalConfig('catalog.processors.microsoftGraphOrg');\n return new MicrosoftGraphOrgReaderProcessor({\n ...options,\n providers: c ? readMicrosoftGraphConfig(c) : [],\n });\n }\n\n constructor(options: {\n providers: MicrosoftGraphProviderConfig[];\n logger: Logger;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n }) {\n options.logger.warn(\n 'MicrosoftGraphOrgReaderProcessor is deprecated. Please use MicrosoftGraphOrgEntityProvider instead. More info at https://github.com/backstage/backstage/blob/master/.changeset/long-bananas-rescue.md',\n );\n this.providers = options.providers;\n this.logger = options.logger;\n this.userTransformer = options.userTransformer;\n this.groupTransformer = options.groupTransformer;\n this.organizationTransformer = options.organizationTransformer;\n }\n\n getProcessorName(): string {\n return 'MicrosoftGraphOrgReaderProcessor';\n }\n\n async readLocation(\n location: LocationSpec,\n _optional: boolean,\n emit: CatalogProcessorEmit,\n ): Promise<boolean> {\n if (location.type !== 'microsoft-graph-org') {\n return false;\n }\n\n const provider = this.providers.find(p =>\n location.target.startsWith(p.target),\n );\n if (!provider) {\n throw new Error(\n `There is no Microsoft Graph Org provider that matches ${location.target}. Please add a configuration entry for it under catalog.processors.microsoftGraphOrg.providers.`,\n );\n }\n\n // Read out all the raw data\n const startTimestamp = Date.now();\n this.logger.info('Reading Microsoft Graph users and groups');\n\n // We create a client each time as we need one that matches the specific provider\n const client = MicrosoftGraphClient.create(provider);\n const { users, groups } = await readMicrosoftGraphOrg(\n client,\n provider.tenantId,\n {\n userExpand: provider.userExpand,\n userFilter: provider.userFilter,\n userGroupMemberFilter: provider.userGroupMemberFilter,\n userGroupMemberSearch: provider.userGroupMemberSearch,\n groupExpand: provider.groupExpand,\n groupFilter: provider.groupFilter,\n groupSearch: provider.groupSearch,\n groupSelect: provider.groupSelect,\n queryMode: provider.queryMode,\n userTransformer: this.userTransformer,\n groupTransformer: this.groupTransformer,\n organizationTransformer: this.organizationTransformer,\n logger: this.logger,\n },\n );\n\n const duration = ((Date.now() - startTimestamp) / 1000).toFixed(1);\n this.logger.debug(\n `Read ${users.length} users and ${groups.length} groups from Microsoft Graph in ${duration} seconds`,\n );\n\n // Done!\n for (const group of groups) {\n emit(processingResult.entity(location, group));\n }\n for (const user of users) {\n emit(processingResult.entity(location, user));\n }\n\n return true;\n }\n}\n"],"names":["msal","qs","fetch","trimEnd","limiterFactory","stringifyEntityRef","uuid","merge","ANNOTATION_LOCATION","ANNOTATION_ORIGIN_LOCATION","processingResult"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -13,6 +13,10 @@ import { Response } from 'node-fetch';
13
13
  * @public
14
14
  */
15
15
  declare type MicrosoftGraphProviderConfig = {
16
+ /**
17
+ * Identifier of the provider which will be used i.e. at the location key for ingested entities.
18
+ */
19
+ id: string;
16
20
  /**
17
21
  * The prefix of the target that this matches on, e.g.
18
22
  * "https://graph.microsoft.com/v1.0", with no trailing slash.
@@ -45,7 +49,7 @@ declare type MicrosoftGraphProviderConfig = {
45
49
  /**
46
50
  * The "expand" argument to apply to users.
47
51
  *
48
- * E.g. "manager"
52
+ * E.g. "manager".
49
53
  */
50
54
  userExpand?: string;
51
55
  /**
@@ -63,7 +67,7 @@ declare type MicrosoftGraphProviderConfig = {
63
67
  /**
64
68
  * The "expand" argument to apply to groups.
65
69
  *
66
- * E.g. "member"
70
+ * E.g. "member".
67
71
  */
68
72
  groupExpand?: string;
69
73
  /**
@@ -100,6 +104,7 @@ declare type MicrosoftGraphProviderConfig = {
100
104
  * @param config - The root of the msgraph config hierarchy
101
105
  *
102
106
  * @public
107
+ * @deprecated Replaced by not exported `readProviderConfigs` and kept for backwards compatibility only.
103
108
  */
104
109
  declare function readMicrosoftGraphConfig(config: Config): MicrosoftGraphProviderConfig[];
105
110
 
@@ -376,7 +381,48 @@ declare function readMicrosoftGraphOrg(client: MicrosoftGraphClient, tenantId: s
376
381
  *
377
382
  * @public
378
383
  */
379
- interface MicrosoftGraphOrgEntityProviderOptions {
384
+ declare type MicrosoftGraphOrgEntityProviderOptions = MicrosoftGraphOrgEntityProviderLegacyOptions | {
385
+ /**
386
+ * The logger to use.
387
+ */
388
+ logger: Logger;
389
+ /**
390
+ * The refresh schedule to use.
391
+ *
392
+ * @remarks
393
+ *
394
+ * If you pass in 'manual', you are responsible for calling the `read` method
395
+ * manually at some interval.
396
+ *
397
+ * But more commonly you will pass in the result of
398
+ * {@link @backstage/backend-tasks#PluginTaskScheduler.createScheduledTaskRunner}
399
+ * to enable automatic scheduling of tasks.
400
+ */
401
+ schedule: 'manual' | TaskRunner;
402
+ /**
403
+ * The function that transforms a user entry in msgraph to an entity.
404
+ * Optionally, you can pass separate transformers per provider ID.
405
+ */
406
+ userTransformer?: UserTransformer | Record<string, UserTransformer>;
407
+ /**
408
+ * The function that transforms a group entry in msgraph to an entity.
409
+ * Optionally, you can pass separate transformers per provider ID.
410
+ */
411
+ groupTransformer?: GroupTransformer | Record<string, GroupTransformer>;
412
+ /**
413
+ * The function that transforms an organization entry in msgraph to an entity.
414
+ * Optionally, you can pass separate transformers per provider ID.
415
+ */
416
+ organizationTransformer?: OrganizationTransformer | Record<string, OrganizationTransformer>;
417
+ };
418
+ /**
419
+ * Legacy options for {@link MicrosoftGraphOrgEntityProvider}
420
+ * based on `catalog.processors.microsoftGraphOrg`.
421
+ *
422
+ * @public
423
+ * @deprecated This interface exists for backwards compatibility only and will be removed in the future.
424
+ */
425
+ interface MicrosoftGraphOrgEntityProviderLegacyOptions {
380
426
  /**
381
427
  * A unique, stable identifier for this provider.
382
428
  *
@@ -386,7 +432,7 @@ interface MicrosoftGraphOrgEntityProviderOptions {
386
432
  /**
387
433
  * The target that this provider should consume.
388
434
  *
389
- * Should exactly match the "target" field of one of the providers
435
+ * Should exactly match the "target" field of one of the provider
390
436
  * configuration entries.
391
437
  */
392
438
  target: string;
@@ -430,7 +476,11 @@ declare class MicrosoftGraphOrgEntityProvider implements EntityProvider {
430
476
  private options;
431
477
  private connection?;
432
478
  private scheduleFn?;
433
- static fromConfig(configRoot: Config, options: MicrosoftGraphOrgEntityProviderOptions): MicrosoftGraphOrgEntityProvider;
479
+ static fromConfig(configRoot: Config, options: MicrosoftGraphOrgEntityProviderOptions): MicrosoftGraphOrgEntityProvider[];
480
+ /**
481
+ * @deprecated Exists for backwards compatibility only and will be removed in the future.
482
+ */
483
+ private static fromLegacyConfig;
434
484
  constructor(options: {
435
485
  id: string;
436
486
  provider: MicrosoftGraphProviderConfig;
@@ -454,9 +504,10 @@ declare class MicrosoftGraphOrgEntityProvider implements EntityProvider {
454
504
  }
455
505
 
456
506
  /**
457
- * Extracts teams and users out of a the Microsoft Graph API.
507
+ * Extracts teams and users out of the Microsoft Graph API.
458
508
  *
459
509
  * @public
510
+ * @deprecated Use the MicrosoftGraphOrgEntityProvider instead.
460
511
  */
461
512
  declare class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {
462
513
  private readonly providers;
@@ -481,4 +532,4 @@ declare class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {
481
532
  readLocation(location: LocationSpec, _optional: boolean, emit: CatalogProcessorEmit): Promise<boolean>;
482
533
  }
483
534
 
484
- export { GroupMember, GroupTransformer, MICROSOFT_EMAIL_ANNOTATION, MICROSOFT_GRAPH_GROUP_ID_ANNOTATION, MICROSOFT_GRAPH_TENANT_ID_ANNOTATION, MICROSOFT_GRAPH_USER_ID_ANNOTATION, MicrosoftGraphClient, MicrosoftGraphOrgEntityProvider, MicrosoftGraphOrgEntityProviderOptions, MicrosoftGraphOrgReaderProcessor, MicrosoftGraphProviderConfig, ODataQuery, OrganizationTransformer, UserTransformer, defaultGroupTransformer, defaultOrganizationTransformer, defaultUserTransformer, normalizeEntityName, readMicrosoftGraphConfig, readMicrosoftGraphOrg };
535
+ export { GroupMember, GroupTransformer, MICROSOFT_EMAIL_ANNOTATION, MICROSOFT_GRAPH_GROUP_ID_ANNOTATION, MICROSOFT_GRAPH_TENANT_ID_ANNOTATION, MICROSOFT_GRAPH_USER_ID_ANNOTATION, MicrosoftGraphClient, MicrosoftGraphOrgEntityProvider, MicrosoftGraphOrgEntityProviderLegacyOptions, MicrosoftGraphOrgEntityProviderOptions, MicrosoftGraphOrgReaderProcessor, MicrosoftGraphProviderConfig, ODataQuery, OrganizationTransformer, UserTransformer, defaultGroupTransformer, defaultOrganizationTransformer, defaultUserTransformer, normalizeEntityName, readMicrosoftGraphConfig, readMicrosoftGraphOrg };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-backend-module-msgraph",
3
3
  "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph",
4
- "version": "0.3.3",
4
+ "version": "0.4.0-next.2",
5
5
  "main": "dist/index.cjs.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -34,10 +34,10 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@azure/msal-node": "^1.1.0",
37
- "@backstage/backend-tasks": "^0.3.2",
38
- "@backstage/catalog-model": "^1.0.3",
37
+ "@backstage/backend-tasks": "^0.3.3-next.3",
38
+ "@backstage/catalog-model": "^1.1.0-next.3",
39
39
  "@backstage/config": "^1.0.1",
40
- "@backstage/plugin-catalog-backend": "^1.2.0",
40
+ "@backstage/plugin-catalog-backend": "^1.3.0-next.3",
41
41
  "@microsoft/microsoft-graph-types": "^2.6.0",
42
42
  "@types/node-fetch": "^2.5.12",
43
43
  "lodash": "^4.17.21",
@@ -48,16 +48,16 @@
48
48
  "winston": "^3.2.1"
49
49
  },
50
50
  "devDependencies": {
51
- "@backstage/backend-common": "^0.14.0",
52
- "@backstage/backend-test-utils": "^0.1.25",
53
- "@backstage/cli": "^0.17.2",
51
+ "@backstage/backend-common": "^0.14.1-next.3",
52
+ "@backstage/backend-test-utils": "^0.1.26-next.3",
53
+ "@backstage/cli": "^0.18.0-next.3",
54
54
  "@types/lodash": "^4.14.151",
55
- "msw": "^0.42.0"
55
+ "msw": "^0.43.0"
56
56
  },
57
57
  "files": [
58
58
  "dist",
59
59
  "config.d.ts"
60
60
  ],
61
61
  "configSchema": "config.d.ts",
62
- "gitHead": "e42cb3887e41f756c16380d757d93feda27f40ee"
62
+ "gitHead": "291b3a07233061266d9f3ce431345bf19fa4bbd5"
63
63
  }