@backstage/plugin-catalog-backend-module-msgraph 0.2.10 → 0.2.13
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 +36 -0
- package/dist/index.cjs.js +31 -33
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +75 -3
- package/package.json +10 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend-module-msgraph
|
|
2
2
|
|
|
3
|
+
## 0.2.13
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @backstage/plugin-catalog-backend@0.21.0
|
|
9
|
+
- @backstage/config@0.1.13
|
|
10
|
+
- @backstage/catalog-model@0.9.10
|
|
11
|
+
|
|
12
|
+
## 0.2.13-next.0
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- @backstage/plugin-catalog-backend@0.21.0-next.0
|
|
18
|
+
- @backstage/config@0.1.13-next.0
|
|
19
|
+
- @backstage/catalog-model@0.9.10-next.0
|
|
20
|
+
|
|
21
|
+
## 0.2.12
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- 722681b1b1: Clean up API report
|
|
26
|
+
- Updated dependencies
|
|
27
|
+
- @backstage/config@0.1.12
|
|
28
|
+
- @backstage/plugin-catalog-backend@0.20.0
|
|
29
|
+
- @backstage/catalog-model@0.9.9
|
|
30
|
+
|
|
31
|
+
## 0.2.11
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- b055a6addc: Align on usage of `cross-fetch` vs `node-fetch` in frontend vs backend packages, and remove some unnecessary imports of either one of them
|
|
36
|
+
- Updated dependencies
|
|
37
|
+
- @backstage/plugin-catalog-backend@0.19.0
|
|
38
|
+
|
|
3
39
|
## 0.2.10
|
|
4
40
|
|
|
5
41
|
### Patch Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
var catalogModel = require('@backstage/catalog-model');
|
|
6
6
|
var lodash = require('lodash');
|
|
7
7
|
var msal = require('@azure/msal-node');
|
|
8
|
-
var fetch = require('
|
|
8
|
+
var fetch = require('node-fetch');
|
|
9
9
|
var qs = require('qs');
|
|
10
10
|
var limiterFactory = require('p-limit');
|
|
11
11
|
var pluginCatalogBackend = require('@backstage/plugin-catalog-backend');
|
|
@@ -21,14 +21,12 @@ function _interopNamespace(e) {
|
|
|
21
21
|
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
22
22
|
Object.defineProperty(n, k, d.get ? d : {
|
|
23
23
|
enumerable: true,
|
|
24
|
-
get: function () {
|
|
25
|
-
return e[k];
|
|
26
|
-
}
|
|
24
|
+
get: function () { return e[k]; }
|
|
27
25
|
});
|
|
28
26
|
}
|
|
29
27
|
});
|
|
30
28
|
}
|
|
31
|
-
n[
|
|
29
|
+
n["default"] = e;
|
|
32
30
|
return Object.freeze(n);
|
|
33
31
|
}
|
|
34
32
|
|
|
@@ -70,7 +68,7 @@ class MicrosoftGraphClient {
|
|
|
70
68
|
}
|
|
71
69
|
async requestApi(path, query) {
|
|
72
70
|
var _a, _b;
|
|
73
|
-
const queryString = qs__default[
|
|
71
|
+
const queryString = qs__default["default"].stringify({
|
|
74
72
|
$filter: query == null ? void 0 : query.filter,
|
|
75
73
|
$select: (_a = query == null ? void 0 : query.select) == null ? void 0 : _a.join(","),
|
|
76
74
|
$expand: (_b = query == null ? void 0 : query.expand) == null ? void 0 : _b.join(",")
|
|
@@ -87,7 +85,7 @@ class MicrosoftGraphClient {
|
|
|
87
85
|
if (!token) {
|
|
88
86
|
throw new Error("Error while requesting token for Microsoft Graph");
|
|
89
87
|
}
|
|
90
|
-
return await fetch__default[
|
|
88
|
+
return await fetch__default["default"](url, {
|
|
91
89
|
headers: {
|
|
92
90
|
Authorization: `Bearer ${token.accessToken}`
|
|
93
91
|
}
|
|
@@ -235,7 +233,7 @@ function buildOrgHierarchy(groups) {
|
|
|
235
233
|
function buildMemberOf(groups, users) {
|
|
236
234
|
const groupsByName = new Map(groups.map((g) => [g.metadata.name, g]));
|
|
237
235
|
users.forEach((user) => {
|
|
238
|
-
const transitiveMemberOf = new Set();
|
|
236
|
+
const transitiveMemberOf = /* @__PURE__ */ new Set();
|
|
239
237
|
const todo = [
|
|
240
238
|
...user.spec.memberOf,
|
|
241
239
|
...groups.filter((g) => {
|
|
@@ -290,7 +288,7 @@ async function defaultUserTransformer(user, userPhoto) {
|
|
|
290
288
|
async function readMicrosoftGraphUsers(client, options) {
|
|
291
289
|
var _a;
|
|
292
290
|
const users = [];
|
|
293
|
-
const limiter = limiterFactory__default[
|
|
291
|
+
const limiter = limiterFactory__default["default"](10);
|
|
294
292
|
const transformer = (_a = options == null ? void 0 : options.transformer) != null ? _a : defaultUserTransformer;
|
|
295
293
|
const promises = [];
|
|
296
294
|
for await (const user of client.getUsers({
|
|
@@ -311,16 +309,16 @@ async function readMicrosoftGraphUsers(client, options) {
|
|
|
311
309
|
}));
|
|
312
310
|
}
|
|
313
311
|
await Promise.all(promises);
|
|
314
|
-
return {users};
|
|
312
|
+
return { users };
|
|
315
313
|
}
|
|
316
314
|
async function readMicrosoftGraphUsersInGroups(client, options) {
|
|
317
315
|
var _a;
|
|
318
316
|
const users = [];
|
|
319
|
-
const limiter = limiterFactory__default[
|
|
317
|
+
const limiter = limiterFactory__default["default"](10);
|
|
320
318
|
const transformer = (_a = options == null ? void 0 : options.transformer) != null ? _a : defaultUserTransformer;
|
|
321
319
|
const userGroupMemberPromises = [];
|
|
322
320
|
const userPromises = [];
|
|
323
|
-
const groupMemberUsers = new Set();
|
|
321
|
+
const groupMemberUsers = /* @__PURE__ */ new Set();
|
|
324
322
|
for await (const group of client.getGroups({
|
|
325
323
|
filter: options == null ? void 0 : options.userGroupMemberFilter
|
|
326
324
|
})) {
|
|
@@ -361,7 +359,7 @@ async function readMicrosoftGraphUsersInGroups(client, options) {
|
|
|
361
359
|
}));
|
|
362
360
|
}
|
|
363
361
|
await Promise.all(userPromises);
|
|
364
|
-
return {users};
|
|
362
|
+
return { users };
|
|
365
363
|
}
|
|
366
364
|
async function defaultOrganizationTransformer(organization) {
|
|
367
365
|
if (!organization.id || !organization.displayName) {
|
|
@@ -392,7 +390,7 @@ async function readMicrosoftGraphOrganization(client, tenantId, options) {
|
|
|
392
390
|
const organization = await client.getOrganization(tenantId);
|
|
393
391
|
const transformer = (_a = options == null ? void 0 : options.transformer) != null ? _a : defaultOrganizationTransformer;
|
|
394
392
|
const rootGroup = await transformer(organization);
|
|
395
|
-
return {rootGroup};
|
|
393
|
+
return { rootGroup };
|
|
396
394
|
}
|
|
397
395
|
function extractGroupName(group) {
|
|
398
396
|
if (group.securityEnabled) {
|
|
@@ -437,14 +435,14 @@ async function defaultGroupTransformer(group, groupPhoto) {
|
|
|
437
435
|
async function readMicrosoftGraphGroups(client, tenantId, options) {
|
|
438
436
|
var _a;
|
|
439
437
|
const groups = [];
|
|
440
|
-
const groupMember = new Map();
|
|
441
|
-
const groupMemberOf = new Map();
|
|
442
|
-
const limiter = limiterFactory__default[
|
|
443
|
-
const {rootGroup} = await readMicrosoftGraphOrganization(client, tenantId, {
|
|
438
|
+
const groupMember = /* @__PURE__ */ new Map();
|
|
439
|
+
const groupMemberOf = /* @__PURE__ */ new Map();
|
|
440
|
+
const limiter = limiterFactory__default["default"](10);
|
|
441
|
+
const { rootGroup } = await readMicrosoftGraphOrganization(client, tenantId, {
|
|
444
442
|
transformer: options == null ? void 0 : options.organizationTransformer
|
|
445
443
|
});
|
|
446
444
|
if (rootGroup) {
|
|
447
|
-
groupMember.set(rootGroup.metadata.name, new Set());
|
|
445
|
+
groupMember.set(rootGroup.metadata.name, /* @__PURE__ */ new Set());
|
|
448
446
|
groups.push(rootGroup);
|
|
449
447
|
}
|
|
450
448
|
const transformer = (_a = options == null ? void 0 : options.groupTransformer) != null ? _a : defaultGroupTransformer;
|
|
@@ -480,7 +478,7 @@ async function readMicrosoftGraphGroups(client, tenantId, options) {
|
|
|
480
478
|
};
|
|
481
479
|
}
|
|
482
480
|
function resolveRelations(rootGroup, groups, users, groupMember, groupMemberOf) {
|
|
483
|
-
const groupMap = new Map();
|
|
481
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
484
482
|
for (const group of groups) {
|
|
485
483
|
if (group.metadata.annotations[MICROSOFT_GRAPH_GROUP_ID_ANNOTATION]) {
|
|
486
484
|
groupMap.set(group.metadata.annotations[MICROSOFT_GRAPH_GROUP_ID_ANNOTATION], group);
|
|
@@ -489,7 +487,7 @@ function resolveRelations(rootGroup, groups, users, groupMember, groupMemberOf)
|
|
|
489
487
|
groupMap.set(group.metadata.annotations[MICROSOFT_GRAPH_TENANT_ID_ANNOTATION], group);
|
|
490
488
|
}
|
|
491
489
|
}
|
|
492
|
-
const parentGroups = new Map();
|
|
490
|
+
const parentGroups = /* @__PURE__ */ new Map();
|
|
493
491
|
groupMember.forEach((members, groupId) => members.forEach((m) => ensureItem(parentGroups, m, groupId)));
|
|
494
492
|
if (rootGroup) {
|
|
495
493
|
const tenantId = rootGroup.metadata.annotations[MICROSOFT_GRAPH_TENANT_ID_ANNOTATION];
|
|
@@ -535,21 +533,21 @@ function resolveRelations(rootGroup, groups, users, groupMember, groupMemberOf)
|
|
|
535
533
|
async function readMicrosoftGraphOrg(client, tenantId, options) {
|
|
536
534
|
const users = [];
|
|
537
535
|
if (options.userGroupMemberFilter) {
|
|
538
|
-
const {users: usersInGroups} = await readMicrosoftGraphUsersInGroups(client, {
|
|
536
|
+
const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups(client, {
|
|
539
537
|
userGroupMemberFilter: options.userGroupMemberFilter,
|
|
540
538
|
transformer: options.userTransformer,
|
|
541
539
|
logger: options.logger
|
|
542
540
|
});
|
|
543
541
|
users.push(...usersInGroups);
|
|
544
542
|
} else {
|
|
545
|
-
const {users: usersWithFilter} = await readMicrosoftGraphUsers(client, {
|
|
543
|
+
const { users: usersWithFilter } = await readMicrosoftGraphUsers(client, {
|
|
546
544
|
userFilter: options.userFilter,
|
|
547
545
|
transformer: options.userTransformer,
|
|
548
546
|
logger: options.logger
|
|
549
547
|
});
|
|
550
548
|
users.push(...usersWithFilter);
|
|
551
549
|
}
|
|
552
|
-
const {groups, rootGroup, groupMember, groupMemberOf} = await readMicrosoftGraphGroups(client, tenantId, {
|
|
550
|
+
const { groups, rootGroup, groupMember, groupMemberOf } = await readMicrosoftGraphGroups(client, tenantId, {
|
|
553
551
|
groupFilter: options == null ? void 0 : options.groupFilter,
|
|
554
552
|
groupTransformer: options == null ? void 0 : options.groupTransformer,
|
|
555
553
|
organizationTransformer: options == null ? void 0 : options.organizationTransformer
|
|
@@ -557,19 +555,19 @@ async function readMicrosoftGraphOrg(client, tenantId, options) {
|
|
|
557
555
|
resolveRelations(rootGroup, groups, users, groupMember, groupMemberOf);
|
|
558
556
|
users.sort((a, b) => a.metadata.name.localeCompare(b.metadata.name));
|
|
559
557
|
groups.sort((a, b) => a.metadata.name.localeCompare(b.metadata.name));
|
|
560
|
-
return {users, groups};
|
|
558
|
+
return { users, groups };
|
|
561
559
|
}
|
|
562
560
|
function ensureItem(target, key, value) {
|
|
563
561
|
let set = target.get(key);
|
|
564
562
|
if (!set) {
|
|
565
|
-
set = new Set();
|
|
563
|
+
set = /* @__PURE__ */ new Set();
|
|
566
564
|
target.set(key, set);
|
|
567
565
|
}
|
|
568
566
|
set.add(value);
|
|
569
567
|
}
|
|
570
568
|
function retrieveItems(target, key) {
|
|
571
569
|
var _a;
|
|
572
|
-
return (_a = target.get(key)) != null ? _a : new Set();
|
|
570
|
+
return (_a = target.get(key)) != null ? _a : /* @__PURE__ */ new Set();
|
|
573
571
|
}
|
|
574
572
|
|
|
575
573
|
class MicrosoftGraphOrgEntityProvider {
|
|
@@ -606,9 +604,9 @@ class MicrosoftGraphOrgEntityProvider {
|
|
|
606
604
|
throw new Error("Not initialized");
|
|
607
605
|
}
|
|
608
606
|
const provider = this.options.provider;
|
|
609
|
-
const {markReadComplete} = trackProgress(this.options.logger);
|
|
607
|
+
const { markReadComplete } = trackProgress(this.options.logger);
|
|
610
608
|
const client = MicrosoftGraphClient.create(this.options.provider);
|
|
611
|
-
const {users, groups} = await readMicrosoftGraphOrg(client, provider.tenantId, {
|
|
609
|
+
const { users, groups } = await readMicrosoftGraphOrg(client, provider.tenantId, {
|
|
612
610
|
userFilter: provider.userFilter,
|
|
613
611
|
userGroupMemberFilter: provider.userGroupMemberFilter,
|
|
614
612
|
groupFilter: provider.groupFilter,
|
|
@@ -617,7 +615,7 @@ class MicrosoftGraphOrgEntityProvider {
|
|
|
617
615
|
organizationTransformer: this.options.organizationTransformer,
|
|
618
616
|
logger: this.options.logger
|
|
619
617
|
});
|
|
620
|
-
const {markCommitComplete} = markReadComplete({users, groups});
|
|
618
|
+
const { markCommitComplete } = markReadComplete({ users, groups });
|
|
621
619
|
await this.connection.applyMutation({
|
|
622
620
|
type: "full",
|
|
623
621
|
entities: [...users, ...groups].map((entity) => ({
|
|
@@ -637,13 +635,13 @@ function trackProgress(logger) {
|
|
|
637
635
|
const readDuration = ((Date.now() - timestamp) / 1e3).toFixed(1);
|
|
638
636
|
timestamp = Date.now();
|
|
639
637
|
logger.info(`Read ${summary} in ${readDuration} seconds. Committing...`);
|
|
640
|
-
return {markCommitComplete};
|
|
638
|
+
return { markCommitComplete };
|
|
641
639
|
}
|
|
642
640
|
function markCommitComplete() {
|
|
643
641
|
const commitDuration = ((Date.now() - timestamp) / 1e3).toFixed(1);
|
|
644
642
|
logger.info(`Committed ${summary} in ${commitDuration} seconds.`);
|
|
645
643
|
}
|
|
646
|
-
return {markReadComplete};
|
|
644
|
+
return { markReadComplete };
|
|
647
645
|
}
|
|
648
646
|
function withLocations(providerId, entity) {
|
|
649
647
|
var _a, _b, _c;
|
|
@@ -685,7 +683,7 @@ class MicrosoftGraphOrgReaderProcessor {
|
|
|
685
683
|
const startTimestamp = Date.now();
|
|
686
684
|
this.logger.info("Reading Microsoft Graph users and groups");
|
|
687
685
|
const client = MicrosoftGraphClient.create(provider);
|
|
688
|
-
const {users, groups} = await readMicrosoftGraphOrg(client, provider.tenantId, {
|
|
686
|
+
const { users, groups } = await readMicrosoftGraphOrg(client, provider.tenantId, {
|
|
689
687
|
userFilter: provider.userFilter,
|
|
690
688
|
userGroupMemberFilter: provider.userGroupMemberFilter,
|
|
691
689
|
groupFilter: provider.groupFilter,
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -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 from 'cross-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 * @public\n */\nexport type ODataQuery = {\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\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 *\n */\n async *requestCollection<T>(\n path: string,\n query?: ODataQuery,\n ): AsyncIterable<T> {\n let response = await this.requestApi(path, query);\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']);\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 */\n async requestApi(path: string, query?: ODataQuery): Promise<Response> {\n const queryString = qs.stringify(\n {\n $filter: query?.filter,\n $select: query?.select?.join(','),\n $expand: query?.expand?.join(','),\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(`${this.baseUrl}/${path}${queryString}`);\n }\n\n /**\n * Makes a HTTP call to Graph API with token\n *\n * @param url - HTTP Endpoint of Graph API\n */\n async requestRaw(url: string): 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 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 *\n */\n async getUserProfile(userId: string): Promise<MicrosoftGraph.User> {\n const response = await this.requestApi(`users/${userId}`);\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 *\n */\n async *getUsers(query?: ODataQuery): AsyncIterable<MicrosoftGraph.User> {\n yield* this.requestCollection<MicrosoftGraph.User>(`users`, query);\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 * @public\n * @param query - OData Query {@link ODataQuery}\n *\n */\n async *getGroups(query?: ODataQuery): AsyncIterable<MicrosoftGraph.Group> {\n yield* this.requestCollection<MicrosoftGraph.Group>(`groups`, query);\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 */\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 * @visibility secret\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 filter to apply to extract users by groups memberships.\n *\n * E.g. \"displayName eq 'Backstage Users'\"\n */\n userGroupMemberFilter?: 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\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 const userFilter = providerConfig.getOptionalString('userFilter');\n const userGroupMemberFilter = providerConfig.getOptionalString(\n 'userGroupMemberFilter',\n );\n const groupFilter = providerConfig.getOptionalString('groupFilter');\n\n if (userFilter && userGroupMemberFilter) {\n throw new Error(\n `userFilter and userGroupMemberFilter are mutually exclusive, only one can be specified.`,\n );\n }\n\n providers.push({\n target,\n authority,\n tenantId,\n clientId,\n clientSecret,\n userFilter,\n userGroupMemberFilter,\n groupFilter,\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 tenant id used by the Microsoft Graph API\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 */\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 */\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\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 */\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_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\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_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 userFilter?: 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 filter: options.userFilter,\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 userGroupMemberFilter?: 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 filter: options?.userGroupMemberFilter,\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 } 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\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\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 groupFilter?: 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 filter: options?.groupFilter,\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 user.spec.memberOf.push(stringifyEntityRef(parentGroup));\n }\n });\n });\n\n // Make sure all transitive memberships are available\n buildMemberOf(groups, users);\n}\n\nexport async function readMicrosoftGraphOrg(\n client: MicrosoftGraphClient,\n tenantId: string,\n options: {\n userFilter?: string;\n userGroupMemberFilter?: string;\n groupFilter?: string;\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) {\n const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups(\n client,\n {\n userGroupMemberFilter: options.userGroupMemberFilter,\n transformer: options.userTransformer,\n logger: options.logger,\n },\n );\n users.push(...usersInGroups);\n } else {\n const { users: usersWithFilter } = await readMicrosoftGraphUsers(client, {\n userFilter: options.userFilter,\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 groupFilter: options?.groupFilter,\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 */\nimport {\n Entity,\n LOCATION_ANNOTATION,\n ORIGIN_LOCATION_ANNOTATION,\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 { 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 * Reads user and group entries out of Microsoft Graph, and provides them as\n * User and Group entities for the catalog.\n */\nexport class MicrosoftGraphOrgEntityProvider implements EntityProvider {\n private connection?: EntityProviderConnection;\n\n static fromConfig(\n config: Config,\n options: {\n id: string;\n target: string;\n logger: Logger;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n },\n ) {\n const c = config.getOptionalConfig('catalog.processors.microsoftGraphOrg');\n const providers = c ? readMicrosoftGraphConfig(c) : [];\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 return 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\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 getProviderName() {\n return `MicrosoftGraphOrgEntityProvider:${this.options.id}`;\n }\n\n async connect(connection: EntityProviderConnection) {\n this.connection = connection;\n }\n\n async read() {\n if (!this.connection) {\n throw new Error('Not initialized');\n }\n\n const provider = this.options.provider;\n const { markReadComplete } = trackProgress(this.options.logger);\n const client = MicrosoftGraphClient.create(this.options.provider);\n\n const { users, groups } = await readMicrosoftGraphOrg(\n client,\n provider.tenantId,\n {\n userFilter: provider.userFilter,\n userGroupMemberFilter: provider.userGroupMemberFilter,\n groupFilter: provider.groupFilter,\n groupTransformer: this.options.groupTransformer,\n userTransformer: this.options.userTransformer,\n organizationTransformer: this.options.organizationTransformer,\n logger: this.options.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\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 uuid =\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(uuid)}`;\n return merge(\n {\n metadata: {\n annotations: {\n [LOCATION_ANNOTATION]: location,\n [ORIGIN_LOCATION_ANNOTATION]: 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 { LocationSpec } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport {\n CatalogProcessor,\n CatalogProcessorEmit,\n results,\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 */\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 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 of 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 userFilter: provider.userFilter,\n userGroupMemberFilter: provider.userGroupMemberFilter,\n groupFilter: provider.groupFilter,\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(results.entity(location, group));\n }\n for (const user of users) {\n emit(results.entity(location, user));\n }\n\n return true;\n }\n}\n"],"names":["msal","qs","fetch","trimEnd","limiterFactory","stringifyEntityRef","merge","LOCATION_ANNOTATION","ORIGIN_LOCATION_ANNOTATION","results"],"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 * @public\n */\nexport type ODataQuery = {\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\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 *\n */\n async *requestCollection<T>(\n path: string,\n query?: ODataQuery,\n ): AsyncIterable<T> {\n let response = await this.requestApi(path, query);\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']);\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 */\n async requestApi(path: string, query?: ODataQuery): Promise<Response> {\n const queryString = qs.stringify(\n {\n $filter: query?.filter,\n $select: query?.select?.join(','),\n $expand: query?.expand?.join(','),\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(`${this.baseUrl}/${path}${queryString}`);\n }\n\n /**\n * Makes a HTTP call to Graph API with token\n *\n * @param url - HTTP Endpoint of Graph API\n */\n async requestRaw(url: string): 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 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 *\n */\n async getUserProfile(userId: string): Promise<MicrosoftGraph.User> {\n const response = await this.requestApi(`users/${userId}`);\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 *\n */\n async *getUsers(query?: ODataQuery): AsyncIterable<MicrosoftGraph.User> {\n yield* this.requestCollection<MicrosoftGraph.User>(`users`, query);\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 * @public\n * @param query - OData Query {@link ODataQuery}\n *\n */\n async *getGroups(query?: ODataQuery): AsyncIterable<MicrosoftGraph.Group> {\n yield* this.requestCollection<MicrosoftGraph.Group>(`groups`, query);\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 filter to apply to extract users by groups memberships.\n *\n * E.g. \"displayName eq 'Backstage Users'\"\n */\n userGroupMemberFilter?: 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\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 const userFilter = providerConfig.getOptionalString('userFilter');\n const userGroupMemberFilter = providerConfig.getOptionalString(\n 'userGroupMemberFilter',\n );\n const groupFilter = providerConfig.getOptionalString('groupFilter');\n\n if (userFilter && userGroupMemberFilter) {\n throw new Error(\n `userFilter and userGroupMemberFilter are mutually exclusive, only one can be specified.`,\n );\n }\n\n providers.push({\n target,\n authority,\n tenantId,\n clientId,\n clientSecret,\n userFilter,\n userGroupMemberFilter,\n groupFilter,\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 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_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_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 userFilter?: 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 filter: options.userFilter,\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 userGroupMemberFilter?: 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 filter: options?.userGroupMemberFilter,\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 } 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 groupFilter?: 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 filter: options?.groupFilter,\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 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 userFilter?: string;\n userGroupMemberFilter?: string;\n groupFilter?: string;\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) {\n const { users: usersInGroups } = await readMicrosoftGraphUsersInGroups(\n client,\n {\n userGroupMemberFilter: options.userGroupMemberFilter,\n transformer: options.userTransformer,\n logger: options.logger,\n },\n );\n users.push(...usersInGroups);\n } else {\n const { users: usersWithFilter } = await readMicrosoftGraphUsers(client, {\n userFilter: options.userFilter,\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 groupFilter: options?.groupFilter,\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 {\n Entity,\n LOCATION_ANNOTATION,\n ORIGIN_LOCATION_ANNOTATION,\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 { 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 * 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\n static fromConfig(\n config: Config,\n options: {\n id: string;\n target: string;\n logger: Logger;\n userTransformer?: UserTransformer;\n groupTransformer?: GroupTransformer;\n organizationTransformer?: OrganizationTransformer;\n },\n ) {\n const c = config.getOptionalConfig('catalog.processors.microsoftGraphOrg');\n const providers = c ? readMicrosoftGraphConfig(c) : [];\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 return 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\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 }\n\n /**\n * Runs one complete ingestion loop. Call this method regularly at some\n * appropriate cadence.\n */\n async read() {\n if (!this.connection) {\n throw new Error('Not initialized');\n }\n\n const provider = this.options.provider;\n const { markReadComplete } = trackProgress(this.options.logger);\n const client = MicrosoftGraphClient.create(this.options.provider);\n\n const { users, groups } = await readMicrosoftGraphOrg(\n client,\n provider.tenantId,\n {\n userFilter: provider.userFilter,\n userGroupMemberFilter: provider.userGroupMemberFilter,\n groupFilter: provider.groupFilter,\n groupTransformer: this.options.groupTransformer,\n userTransformer: this.options.userTransformer,\n organizationTransformer: this.options.organizationTransformer,\n logger: this.options.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\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 uuid =\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(uuid)}`;\n return merge(\n {\n metadata: {\n annotations: {\n [LOCATION_ANNOTATION]: location,\n [ORIGIN_LOCATION_ANNOTATION]: 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 { LocationSpec } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport {\n CatalogProcessor,\n CatalogProcessorEmit,\n results,\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 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 of 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 userFilter: provider.userFilter,\n userGroupMemberFilter: provider.userGroupMemberFilter,\n groupFilter: provider.groupFilter,\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(results.entity(location, group));\n }\n for (const user of users) {\n emit(results.entity(location, user));\n }\n\n return true;\n }\n}\n"],"names":["msal","qs","fetch","trimEnd","limiterFactory","stringifyEntityRef","merge","LOCATION_ANNOTATION","ORIGIN_LOCATION_ANNOTATION","results"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,12 @@ import { Logger } from 'winston';
|
|
|
4
4
|
import { UserEntity, GroupEntity, LocationSpec } from '@backstage/catalog-model';
|
|
5
5
|
import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
|
|
6
6
|
import * as msal from '@azure/msal-node';
|
|
7
|
+
import { Response } from 'node-fetch';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* The configuration parameters for a single Microsoft Graph provider.
|
|
11
|
+
*
|
|
12
|
+
* @public
|
|
10
13
|
*/
|
|
11
14
|
declare type MicrosoftGraphProviderConfig = {
|
|
12
15
|
/**
|
|
@@ -30,8 +33,6 @@ declare type MicrosoftGraphProviderConfig = {
|
|
|
30
33
|
clientId: string;
|
|
31
34
|
/**
|
|
32
35
|
* The OAuth client secret to use for authenticating requests.
|
|
33
|
-
*
|
|
34
|
-
* @visibility secret
|
|
35
36
|
*/
|
|
36
37
|
clientSecret: string;
|
|
37
38
|
/**
|
|
@@ -53,6 +54,13 @@ declare type MicrosoftGraphProviderConfig = {
|
|
|
53
54
|
*/
|
|
54
55
|
groupFilter?: string;
|
|
55
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* Parses configuration.
|
|
59
|
+
*
|
|
60
|
+
* @param config - The root of the msgraph config hierarchy
|
|
61
|
+
*
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
56
64
|
declare function readMicrosoftGraphConfig(config: Config): MicrosoftGraphProviderConfig[];
|
|
57
65
|
|
|
58
66
|
/**
|
|
@@ -75,6 +83,11 @@ declare type ODataQuery = {
|
|
|
75
83
|
*/
|
|
76
84
|
select?: string[];
|
|
77
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* Extends the base msgraph types to include the odata type.
|
|
88
|
+
*
|
|
89
|
+
* @public
|
|
90
|
+
*/
|
|
78
91
|
declare type GroupMember = (MicrosoftGraph.Group & {
|
|
79
92
|
'@odata.type': '#microsoft.graph.user';
|
|
80
93
|
}) | (MicrosoftGraph.User & {
|
|
@@ -211,26 +224,75 @@ declare class MicrosoftGraphClient {
|
|
|
211
224
|
|
|
212
225
|
/**
|
|
213
226
|
* The tenant id used by the Microsoft Graph API
|
|
227
|
+
*
|
|
228
|
+
* @public
|
|
214
229
|
*/
|
|
215
230
|
declare const MICROSOFT_GRAPH_TENANT_ID_ANNOTATION = "graph.microsoft.com/tenant-id";
|
|
216
231
|
/**
|
|
217
232
|
* The group id used by the Microsoft Graph API
|
|
233
|
+
*
|
|
234
|
+
* @public
|
|
218
235
|
*/
|
|
219
236
|
declare const MICROSOFT_GRAPH_GROUP_ID_ANNOTATION = "graph.microsoft.com/group-id";
|
|
220
237
|
/**
|
|
221
238
|
* The user id used by the Microsoft Graph API
|
|
239
|
+
*
|
|
240
|
+
* @public
|
|
222
241
|
*/
|
|
223
242
|
declare const MICROSOFT_GRAPH_USER_ID_ANNOTATION = "graph.microsoft.com/user-id";
|
|
224
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Takes an input string and cleans it up to become suitable as an entity name.
|
|
246
|
+
*
|
|
247
|
+
* @public
|
|
248
|
+
*/
|
|
225
249
|
declare function normalizeEntityName(name: string): string;
|
|
226
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Customize the ingested User entity
|
|
253
|
+
*
|
|
254
|
+
* @public
|
|
255
|
+
*/
|
|
227
256
|
declare type UserTransformer = (user: MicrosoftGraph.User, userPhoto?: string) => Promise<UserEntity | undefined>;
|
|
257
|
+
/**
|
|
258
|
+
* Customize the ingested organization Group entity
|
|
259
|
+
*
|
|
260
|
+
* @public
|
|
261
|
+
*/
|
|
228
262
|
declare type OrganizationTransformer = (organization: MicrosoftGraph.Organization) => Promise<GroupEntity | undefined>;
|
|
263
|
+
/**
|
|
264
|
+
* Customize the ingested Group entity
|
|
265
|
+
*
|
|
266
|
+
* @public
|
|
267
|
+
*/
|
|
229
268
|
declare type GroupTransformer = (group: MicrosoftGraph.Group, groupPhoto?: string) => Promise<GroupEntity | undefined>;
|
|
230
269
|
|
|
270
|
+
/**
|
|
271
|
+
* The default implementation of the transformation from a graph user entry to
|
|
272
|
+
* a User entity.
|
|
273
|
+
*
|
|
274
|
+
* @public
|
|
275
|
+
*/
|
|
231
276
|
declare function defaultUserTransformer(user: MicrosoftGraph.User, userPhoto?: string): Promise<UserEntity | undefined>;
|
|
277
|
+
/**
|
|
278
|
+
* The default implementation of the transformation from a graph organization
|
|
279
|
+
* entry to a Group entity.
|
|
280
|
+
*
|
|
281
|
+
* @public
|
|
282
|
+
*/
|
|
232
283
|
declare function defaultOrganizationTransformer(organization: MicrosoftGraph.Organization): Promise<GroupEntity | undefined>;
|
|
284
|
+
/**
|
|
285
|
+
* The default implementation of the transformation from a graph group entry to
|
|
286
|
+
* a Group entity.
|
|
287
|
+
*
|
|
288
|
+
* @public
|
|
289
|
+
*/
|
|
233
290
|
declare function defaultGroupTransformer(group: MicrosoftGraph.Group, groupPhoto?: string): Promise<GroupEntity | undefined>;
|
|
291
|
+
/**
|
|
292
|
+
* Reads an entire org as Group and User entities.
|
|
293
|
+
*
|
|
294
|
+
* @public
|
|
295
|
+
*/
|
|
234
296
|
declare function readMicrosoftGraphOrg(client: MicrosoftGraphClient, tenantId: string, options: {
|
|
235
297
|
userFilter?: string;
|
|
236
298
|
userGroupMemberFilter?: string;
|
|
@@ -247,6 +309,8 @@ declare function readMicrosoftGraphOrg(client: MicrosoftGraphClient, tenantId: s
|
|
|
247
309
|
/**
|
|
248
310
|
* Reads user and group entries out of Microsoft Graph, and provides them as
|
|
249
311
|
* User and Group entities for the catalog.
|
|
312
|
+
*
|
|
313
|
+
* @public
|
|
250
314
|
*/
|
|
251
315
|
declare class MicrosoftGraphOrgEntityProvider implements EntityProvider {
|
|
252
316
|
private options;
|
|
@@ -267,13 +331,21 @@ declare class MicrosoftGraphOrgEntityProvider implements EntityProvider {
|
|
|
267
331
|
groupTransformer?: GroupTransformer;
|
|
268
332
|
organizationTransformer?: OrganizationTransformer;
|
|
269
333
|
});
|
|
334
|
+
/** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */
|
|
270
335
|
getProviderName(): string;
|
|
336
|
+
/** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */
|
|
271
337
|
connect(connection: EntityProviderConnection): Promise<void>;
|
|
338
|
+
/**
|
|
339
|
+
* Runs one complete ingestion loop. Call this method regularly at some
|
|
340
|
+
* appropriate cadence.
|
|
341
|
+
*/
|
|
272
342
|
read(): Promise<void>;
|
|
273
343
|
}
|
|
274
344
|
|
|
275
345
|
/**
|
|
276
346
|
* Extracts teams and users out of a the Microsoft Graph API.
|
|
347
|
+
*
|
|
348
|
+
* @public
|
|
277
349
|
*/
|
|
278
350
|
declare class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {
|
|
279
351
|
private readonly providers;
|
|
@@ -297,4 +369,4 @@ declare class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor {
|
|
|
297
369
|
readLocation(location: LocationSpec, _optional: boolean, emit: CatalogProcessorEmit): Promise<boolean>;
|
|
298
370
|
}
|
|
299
371
|
|
|
300
|
-
export { GroupTransformer, MICROSOFT_GRAPH_GROUP_ID_ANNOTATION, MICROSOFT_GRAPH_TENANT_ID_ANNOTATION, MICROSOFT_GRAPH_USER_ID_ANNOTATION, MicrosoftGraphClient, MicrosoftGraphOrgEntityProvider, MicrosoftGraphOrgReaderProcessor, MicrosoftGraphProviderConfig, OrganizationTransformer, UserTransformer, defaultGroupTransformer, defaultOrganizationTransformer, defaultUserTransformer, normalizeEntityName, readMicrosoftGraphConfig, readMicrosoftGraphOrg };
|
|
372
|
+
export { GroupTransformer, MICROSOFT_GRAPH_GROUP_ID_ANNOTATION, MICROSOFT_GRAPH_TENANT_ID_ANNOTATION, MICROSOFT_GRAPH_USER_ID_ANNOTATION, MicrosoftGraphClient, MicrosoftGraphOrgEntityProvider, 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 modules that helps integrate towards Microsoft Graph",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.13",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -30,20 +30,21 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@azure/msal-node": "^1.1.0",
|
|
33
|
-
"@backstage/catalog-model": "^0.9.
|
|
34
|
-
"@backstage/config": "^0.1.
|
|
35
|
-
"@backstage/plugin-catalog-backend": "^0.
|
|
33
|
+
"@backstage/catalog-model": "^0.9.10",
|
|
34
|
+
"@backstage/config": "^0.1.13",
|
|
35
|
+
"@backstage/plugin-catalog-backend": "^0.21.0",
|
|
36
36
|
"@microsoft/microsoft-graph-types": "^2.6.0",
|
|
37
|
-
"
|
|
37
|
+
"@types/node-fetch": "^2.5.12",
|
|
38
38
|
"lodash": "^4.17.21",
|
|
39
|
+
"node-fetch": "^2.6.1",
|
|
39
40
|
"p-limit": "^3.0.2",
|
|
40
41
|
"qs": "^6.9.4",
|
|
41
42
|
"winston": "^3.2.1"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
|
-
"@backstage/backend-common": "^0.
|
|
45
|
-
"@backstage/cli": "^0.
|
|
46
|
-
"@backstage/test-utils": "^0.
|
|
45
|
+
"@backstage/backend-common": "^0.10.4",
|
|
46
|
+
"@backstage/cli": "^0.12.0",
|
|
47
|
+
"@backstage/test-utils": "^0.2.3",
|
|
47
48
|
"@types/lodash": "^4.14.151",
|
|
48
49
|
"msw": "^0.35.0"
|
|
49
50
|
},
|
|
@@ -52,5 +53,5 @@
|
|
|
52
53
|
"config.d.ts"
|
|
53
54
|
],
|
|
54
55
|
"configSchema": "config.d.ts",
|
|
55
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "600d6e3c854bbfb12a0078ca6f726d1c0d1fea0b"
|
|
56
57
|
}
|