@mindline/sync 1.0.74 → 1.0.76

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.
@@ -2,6 +2,6 @@
2
2
  "ExpandedNodes": [
3
3
  ""
4
4
  ],
5
- "SelectedNode": "\\package.json",
5
+ "SelectedNode": "\\index.ts",
6
6
  "PreviewInSolutionExplorer": false
7
7
  }
package/.vs/slnx.sqlite CHANGED
Binary file
Binary file
@@ -2,34 +2,22 @@
2
2
  "Version": 1,
3
3
  "WorkspaceRootPath": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\",
4
4
  "Documents": [
5
- {
6
- "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}",
7
- "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:index.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}"
8
- },
9
5
  {
10
6
  "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.d.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}",
11
7
  "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:index.d.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}"
12
8
  },
13
9
  {
14
- "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\package.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
15
- "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:package.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
16
- },
17
- {
18
- "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\resources.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
19
- "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:resources.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
20
- },
21
- {
22
- "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\hybridspa.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}",
23
- "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:hybridspa.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}"
10
+ "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}",
11
+ "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:index.ts||{0F2454B1-A556-402D-A7D0-1FDE7F99DEE0}"
24
12
  }
25
13
  ],
26
14
  "DocumentGroupContainers": [
27
15
  {
28
- "Orientation": 0,
16
+ "Orientation": 1,
29
17
  "VerticalTabListWidth": 256,
30
18
  "DocumentGroups": [
31
19
  {
32
- "DockedWidth": 179,
20
+ "DockedHeight": 221,
33
21
  "SelectedChildIndex": -1,
34
22
  "Children": [
35
23
  {
@@ -67,83 +55,39 @@
67
55
  ]
68
56
  },
69
57
  {
70
- "DockedWidth": 186,
71
- "SelectedChildIndex": 3,
58
+ "DockedHeight": 299,
59
+ "SelectedChildIndex": 1,
72
60
  "Children": [
73
61
  {
74
62
  "$type": "Document",
75
- "DocumentIndex": 2,
76
- "Title": "package.json",
77
- "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\package.json",
78
- "RelativeDocumentMoniker": "package.json",
79
- "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\package.json",
80
- "RelativeToolTip": "package.json",
81
- "ViewState": "AQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
82
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
83
- "WhenOpened": "2024-03-30T18:10:14.136Z",
84
- "EditorCaption": ""
85
- },
86
- {
87
- "$type": "Document",
88
- "DocumentIndex": 4,
89
- "Title": "hybridspa.ts",
90
- "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\hybridspa.ts",
91
- "RelativeDocumentMoniker": "hybridspa.ts",
92
- "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\hybridspa.ts",
93
- "RelativeToolTip": "hybridspa.ts",
94
- "ViewState": "AQIAAGQBAAAAAAAAAIAmwAcCAAAEAAAA",
95
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|",
96
- "WhenOpened": "2023-11-25T02:18:34.862Z"
97
- },
98
- {
99
- "$type": "Document",
100
- "DocumentIndex": 3,
101
- "Title": "resources.json",
102
- "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\resources.json",
103
- "RelativeDocumentMoniker": "resources.json",
104
- "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\resources.json",
105
- "RelativeToolTip": "resources.json",
106
- "ViewState": "AQIAAAgAAAAAAAAAAAAUwDkAAAABAAAA",
107
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
108
- "WhenOpened": "2024-03-30T16:56:29.032Z",
109
- "EditorCaption": ""
110
- },
111
- {
112
- "$type": "Document",
113
- "DocumentIndex": 0,
63
+ "DocumentIndex": 1,
114
64
  "Title": "index.ts",
115
65
  "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.ts",
116
66
  "RelativeDocumentMoniker": "index.ts",
117
67
  "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.ts",
118
68
  "RelativeToolTip": "index.ts",
119
- "ViewState": "AQIAAOsBAAAAAAAAAAAAANEEAAAuAAAA",
69
+ "ViewState": "AQIAAMEAAAAAAAAAAAAAAE0FAAAAAAAA",
120
70
  "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|",
121
- "WhenOpened": "2023-12-05T02:10:16.04Z",
71
+ "WhenOpened": "2024-05-12T20:20:11.888Z",
122
72
  "EditorCaption": ""
123
- }
124
- ]
125
- },
126
- {
127
- "DockedWidth": 200,
128
- "SelectedChildIndex": 0,
129
- "Children": [
73
+ },
130
74
  {
131
75
  "$type": "Document",
132
- "DocumentIndex": 1,
76
+ "DocumentIndex": 0,
133
77
  "Title": "index.d.ts",
134
78
  "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.d.ts",
135
79
  "RelativeDocumentMoniker": "index.d.ts",
136
80
  "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.d.ts",
137
81
  "RelativeToolTip": "index.d.ts",
138
- "ViewState": "AQIAAPsAAAAAAAAAAAAAABcBAAAIAAAA",
82
+ "ViewState": "AQIAAK8AAAAAAAAAAAAAACUBAAAQAAAA",
139
83
  "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|",
140
- "WhenOpened": "2024-01-01T16:19:43.325Z",
84
+ "WhenOpened": "2024-03-31T03:51:46.193Z",
141
85
  "EditorCaption": ""
142
86
  }
143
87
  ]
144
88
  },
145
89
  {
146
- "DockedWidth": 221,
90
+ "DockedHeight": 200,
147
91
  "SelectedChildIndex": -1,
148
92
  "Children": [
149
93
  {
@@ -153,7 +97,7 @@
153
97
  ]
154
98
  },
155
99
  {
156
- "DockedWidth": 294,
100
+ "DockedHeight": 200,
157
101
  "SelectedChildIndex": -1,
158
102
  "Children": [
159
103
  {
package/actors.json ADDED
@@ -0,0 +1,20 @@
1
+ [
2
+ {
3
+ "type": "user",
4
+ "actor": "arvind@mindline.site",
5
+ "resource": "kv-syncproject",
6
+ "role": "Key Vault Certificate Officer",
7
+ "updatedby": "arvind@mindline.site",
8
+ "updatedon": "2023-09-06T15:36:45.7760714Z",
9
+ "resources": []
10
+ },
11
+ {
12
+ "type": "app",
13
+ "actor": "mindline1-sync-512169f5-38d1-4b57-be8b-967f2d0ae4c6",
14
+ "resource": "dev-subscription",
15
+ "role": "Contributor",
16
+ "updatedby": "arvind@mindline.site",
17
+ "updatedon": "2023-09-06T15:36:45.7760714Z",
18
+ "resources": []
19
+ }
20
+ ]
package/hybridspa.ts CHANGED
@@ -7,6 +7,9 @@ import {
7
7
  TenantConfigInfo,
8
8
  User
9
9
  } from "./index";
10
+ import {
11
+ AccountInfo
12
+ } from "@azure/msal-common";
10
13
  import {
11
14
  IPublicClientApplication,
12
15
  AuthenticationResult,
@@ -15,7 +18,7 @@ import { deserializeArray } from "class-transformer";
15
18
 
16
19
  // helper functions
17
20
  // TODO: this is where you want to trigger a re-authentication if token expires
18
- export async function defineHeaders(
21
+ async function mindlineDefineHeaders(
19
22
  instance: IPublicClientApplication,
20
23
  user: User
21
24
  ): Promise<Headers> {
@@ -23,42 +26,68 @@ export async function defineHeaders(
23
26
  headers.append("Content-Type", "application/json");
24
27
  headers.append("accept", "*/*");
25
28
  // authorization header - if needed, retrieve and cache access token
26
- if (user.accessToken == null || user.accessToken === "") {
29
+ if (user.mindlineAccessToken == null || user.mindlineAccessToken === "") {
27
30
  try {
28
- let response: AuthenticationResult = await instance.acquireTokenByCode({
29
- code: user.spacode,
31
+ let accounts: AccountInfo[] = instance.getAllAccounts();
32
+ let homeAccountId = user.oid + "." + user.tid;
33
+ let account: AccountInfo = null;
34
+ for (let i: number = 0; i < accounts.length; i++) {
35
+ if (accounts[i].homeAccountId == homeAccountId) {
36
+ account = accounts[i];
37
+ }
38
+ }
39
+ let response: AuthenticationResult = await instance.acquireTokenSilent({
40
+ scopes: ["api://8d95d21c-c378-4bb0-9f52-88c30d271e7a/Config.Write"],
41
+ account: account
30
42
  });
31
- user.accessToken = response.accessToken; // cache access token
32
- console.log("Front end token acquired: " + user.accessToken.slice(0, 20));
43
+ user.mindlineAccessToken = response.accessToken; // cache access token
44
+ console.log("Front end token acquired silently: " + user.mindlineAccessToken.slice(0, 20));
33
45
  }
34
46
  catch (error: any) {
35
- console.log("Front end token failure: " + error);
47
+ try {
48
+ console.log("Front end token silent acquisition failure: " + error);
49
+ // fallback to redirect if silent acquisition fails
50
+ let accounts: AccountInfo[] = instance.getAllAccounts();
51
+ let homeAccountId = user.oid + "." + user.tid;
52
+ let account: AccountInfo = null;
53
+ for (let i: number = 0; i < accounts.length; i++) {
54
+ if (accounts[i].homeAccountId == homeAccountId) {
55
+ account = accounts[i];
56
+ }
57
+ }
58
+ instance.acquireTokenRedirect({
59
+ scopes: ["api://8d95d21c-c378-4bb0-9f52-88c30d271e7a/Config.Write"],
60
+ account: account
61
+ });
62
+ }
63
+ catch (error: any) {
64
+ console.log("Front end token popup acquisition failure: " + error);
65
+ }
36
66
  }
37
67
  }
38
- headers.append("Authorization", `Bearer ${user.accessToken}`);
68
+ headers.append("Authorization", `Bearer ${user.mindlineAccessToken}`);
39
69
  return headers;
40
70
  }
41
- async function processErrors(response: Response): Promise<string> {
71
+ export async function processErrors(response: Response): Promise<string> {
42
72
  let errorString: string = "";
43
73
  if (response.status === 401) {
44
74
  errorString = response.statusText;
45
- }
46
- else {
47
- let data = await response.json();
48
- if (data.error !== undefined) {
49
- errorString = `Error: ${data.error} Message: ${data.message}`;
50
- } else if (data.errors !== undefined) {
51
- let errorArray = Object.keys(data.errors);
52
- let errorlist: string = "";
53
- errorString = errorArray.reduce(
54
- (acc, curr) => acc + curr + ": " + data.errors[curr] + " ",
55
- errorlist
56
- );
57
- } else if (data.title !== undefined) {
58
- errorString = data.title;
59
- } else {
60
- debugger;
61
- }
75
+ if (errorString != "") return errorString;
76
+ }
77
+ let data = await response.json();
78
+ if (data.error !== undefined) {
79
+ errorString = `Error: ${data.error.code} Message: ${data.error.message}`;
80
+ } else if (data.errors !== undefined) {
81
+ let errorArray = Object.keys(data.errors);
82
+ let errorlist: string = "";
83
+ errorString = errorArray.reduce(
84
+ (acc, curr) => acc + curr + ": " + data.errors[curr] + " ",
85
+ errorlist
86
+ );
87
+ } else if (data.title !== undefined) {
88
+ errorString = data.title;
89
+ } else {
90
+ debugger;
62
91
  }
63
92
  return errorString;
64
93
  }
@@ -83,7 +112,7 @@ export async function adminDelete(
83
112
  url.searchParams.append("workspaceId", workspaceId);
84
113
  url.searchParams.append("email", user.mail);
85
114
  // create headers
86
- const headers = await defineHeaders(instance, authorizedUser);
115
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
87
116
  // make endpoint call
88
117
  let options = { method: "DELETE", headers: headers };
89
118
  try {
@@ -129,7 +158,7 @@ export async function adminsGet(
129
158
  let url: URL = new URL(endpoint);
130
159
  url.searchParams.append("workspaceId", workspaceID);
131
160
  // create headers
132
- const headers = await defineHeaders(instance, user);
161
+ const headers = await mindlineDefineHeaders(instance, user);
133
162
  // make endpoint call
134
163
  let options = { method: "GET", headers: headers };
135
164
  try {
@@ -186,7 +215,7 @@ export async function adminPost(
186
215
  // create admin endpoint
187
216
  let endpoint: string = mindlineConfig.adminEndpoint();
188
217
  // create headers
189
- const headers = await defineHeaders(instance, authorizedUser);
218
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
190
219
  // create admin body
191
220
  let adminBody: string = `
192
221
  {"email": "${user.mail}",
@@ -225,7 +254,7 @@ export async function configConsentReadPut(instance: IPublicClientApplication, a
225
254
  let url: URL = new URL(endpoint);
226
255
  url.searchParams.append("configurationId", configId);
227
256
  // create headers
228
- const headers = await defineHeaders(instance, authorizedUser);
257
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
229
258
  // create body
230
259
  let configConsentReadBody: string = `
231
260
  {
@@ -266,7 +295,7 @@ export async function configConsentWritePut(instance: IPublicClientApplication,
266
295
  let url: URL = new URL(endpoint);
267
296
  url.searchParams.append("configurationId", configId);
268
297
  // create headers
269
- const headers = await defineHeaders(instance, authorizedUser);
298
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
270
299
  // create body
271
300
  let configConsentWriteBody: string = `
272
301
  {
@@ -318,7 +347,7 @@ export async function configDelete(
318
347
  url = new URL(mindlineConfig.configEndpoint());
319
348
  url.searchParams.append("configurationId", config.id);
320
349
  // create headers
321
- const headers = await defineHeaders(instance, authorizedUser);
350
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
322
351
  // make endpoint call
323
352
  let options = { method: "DELETE", headers: headers };
324
353
  try {
@@ -363,7 +392,7 @@ export async function configPost(
363
392
  // create no parameter config endpoint
364
393
  let endpoint: string = mindlineConfig.configEndpoint();
365
394
  // create config headers
366
- const headers = await defineHeaders(instance, authorizedUser);
395
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
367
396
  // create config body
368
397
  let configBody: string = `
369
398
  {
@@ -441,7 +470,7 @@ export async function configPut(
441
470
  let url: URL = new URL(endpoint);
442
471
  url.searchParams.append("configurationId", config.id);
443
472
  // create config headers
444
- const headers = await defineHeaders(instance, authorizedUser);
473
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
445
474
  // create config body
446
475
  let configBody: string = `
447
476
  {
@@ -517,7 +546,7 @@ export async function configsGet(
517
546
  let url: URL = new URL(endpoint);
518
547
  url.searchParams.append("workspaceId", workspaceID);
519
548
  // create headers
520
- const headers = await defineHeaders(instance, user);
549
+ const headers = await mindlineDefineHeaders(instance, user);
521
550
  // make endpoint call
522
551
  let options = { method: "GET", headers: headers };
523
552
  try {
@@ -574,7 +603,7 @@ export async function initPost(
574
603
  // create init endpoint
575
604
  let endpoint: string = mindlineConfig.initEndpoint();
576
605
  // create init headers
577
- const headers = await defineHeaders(instance, user);
606
+ const headers = await mindlineDefineHeaders(instance, user);
578
607
  // create init body
579
608
  let initBody: string = `
580
609
  {
@@ -634,7 +663,7 @@ export async function tenantDelete(
634
663
  url.searchParams.append("tenantId", tenant.tid);
635
664
  url.searchParams.append("workspaceId", workspaceId);
636
665
  // create headers
637
- const headers = await defineHeaders(instance, authorizedUser);
666
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
638
667
  // make tenant endpoint call
639
668
  let options = { method: "DELETE", headers: headers };
640
669
  try {
@@ -682,7 +711,7 @@ export async function tenantsGet(
682
711
  let url: URL = new URL(endpoint);
683
712
  url.searchParams.append("workspaceId", workspaceID);
684
713
  // create headers
685
- const headers = await defineHeaders(instance, user);
714
+ const headers = await mindlineDefineHeaders(instance, user);
686
715
  // make endpoint call
687
716
  let options = { method: "GET", headers: headers };
688
717
  try {
@@ -742,7 +771,7 @@ export async function tenantPost(
742
771
  let url: URL = new URL(endpoint);
743
772
  url.searchParams.append("workspaceId", workspaceId);
744
773
  // create tenant headers
745
- const headers = await defineHeaders(instance, addingUser);
774
+ const headers = await mindlineDefineHeaders(instance, addingUser);
746
775
  // create tenant body
747
776
  let tenantBody: string = `
748
777
  {"tenantId": "${tenant.tid}",
@@ -792,7 +821,7 @@ export async function workspacePut(instance: IPublicClientApplication, authorize
792
821
  url.searchParams.append("workspaceId", workspaceId);
793
822
  url.searchParams.append("workspaceName", workspaceName);
794
823
  // create workspace headers
795
- const headers = await defineHeaders(instance, authorizedUser);
824
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
796
825
  let options = { method: "PUT", headers: headers };
797
826
  // make config endpoint call
798
827
  try {
@@ -838,7 +867,7 @@ export async function workspacesGet(
838
867
  // create workspace endpoint
839
868
  let url: URL = new URL(endpoint);
840
869
  // create workspace headers
841
- const headers = await defineHeaders(instance, user);
870
+ const headers = await mindlineDefineHeaders(instance, user);
842
871
  // make workspace endpoint call
843
872
  let options = { method: "GET", headers: headers };
844
873
  try {
@@ -897,7 +926,7 @@ export async function readerPost(
897
926
  let url: URL = new URL(readerEndpoint);
898
927
  url.searchParams.append("configurationId", config.id);
899
928
  // create headers
900
- const headers = await defineHeaders(instance, authorizedUser);
929
+ const headers = await mindlineDefineHeaders(instance, authorizedUser);
901
930
  // make reader endpoint call
902
931
  let options = { method: "POST", headers: headers };
903
932
  try {
package/index.d.ts CHANGED
@@ -10,7 +10,12 @@ declare module "@mindline/sync" {
10
10
  version: string;
11
11
  array: Array<Object> | null;
12
12
  constructor();
13
- }
13
+ }
14
+ export class azureConfig {
15
+ // azure graph REST API endpoints
16
+ static azureElevateAccess: string;
17
+ static azureListRootAssignments: string";
18
+ };
14
19
  export class mindlineConfig {
15
20
  static environmentTag: string;
16
21
  // config API endpoints
@@ -72,7 +77,8 @@ declare module "@mindline/sync" {
72
77
  workspaceIDs: string;
73
78
  session: string;
74
79
  spacode: string;
75
- accessToken: string;
80
+ graphAccessToken: string;
81
+ mindlineAccessToken: string;
76
82
  loginHint: string;
77
83
  scopes: string[];
78
84
  authTS: Date;
@@ -279,9 +285,23 @@ declare module "@mindline/sync" {
279
285
  resources: ResourceNode[];
280
286
  constructor(type: string, resource: string, cost: number);
281
287
  }
282
- //
283
- // Azure AD Graph API
284
- //
288
+ export class ActorArray {
289
+ actorNodes: ActorNode[];
290
+ constructor(bClearLocalStorage: boolean);
291
+ // get initial data from localStorage or file
292
+ init(bClearLocalStorage: boolean): void;
293
+ }
294
+ export class ActorNode {
295
+ type: string;
296
+ actor: string;
297
+ resource: string;
298
+ role: string;
299
+ updatedby: string;
300
+ updatedon: string;
301
+ actors: ActorNode[];
302
+ constructor(type: string, actor: string, resource: string, role: string, updatedby: string, updatedon: string);
303
+ }
304
+ // ======================= Azure AD Graph API ===============================
285
305
  export function groupsGet(instance: IPublicClientApplication, user: User | undefined, groupSearchString: string): Promise<{ groups: Group[], error: string }>;
286
306
  export function oauth2PermissionGrantsGet(options: RequestInit, user: User, spid: string, oid: string): Promise<{grants: string, error: string}>;
287
307
  export function requestAdminConsent(admin: User, tct: TenantConfigType): void;
@@ -295,9 +315,7 @@ declare module "@mindline/sync" {
295
315
  export function userDelegatedScopesGet(instance: IPublicClientApplication, loggedInUser: User, tenant: Tenant): { scopes: string, id: string, error: string };
296
316
  export function userDelegatedScopesRemove(instance: IPublicClientApplication, loggedInUser: User, tenant: Tenant, scope: string): boolean;
297
317
  export function usersGet(instance: IPublicClientApplication, user: User | undefined): { users: string[], error: string };
298
- //
299
- // Mindline Config API
300
- //
318
+ // ======================= Mindline Config API ===============================
301
319
  export function configConsentForRead(instance: IPublicClientApplication, authorizedUser: User, configId: string, tid: string, consent: boolean): Promise<APIResult>;
302
320
  export function configConsentForWrite(instance: IPublicClientApplication, authorizedUser: User, configId: string, tid: string, consent: boolean): Promise<APIResult>;
303
321
  export function configEdit(
@@ -317,4 +335,7 @@ declare module "@mindline/sync" {
317
335
  export function userAdd(instance: IPublicClientApplication, authorizedUser: User, user: User, workspaceId: string): APIResult;
318
336
  export function userRemove(instance: IPublicClientApplication, authorizedUser: User, user: User, workspaceId: string): APIResult;
319
337
  export function workspaceEdit(instance: IPublicClientApplication, authorizedUser: User, workspaceId: string, workspaceName: string): Promise<APIResult>;
338
+ // ======================= Azure REST API ===============================
339
+ export function canListRootAssignments(instance: IPublicClientApplication, user: User): Promise<boolean>;
340
+ export function elevateGlobalAdminToUserAccessAdmin(instance: IPublicClientApplication, user: User): Promise<boolean>;
320
341
  }
package/index.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  //index.ts - published interface - AAD implementations, facade to Mindline Config API
2
2
  import * as signalR from "@microsoft/signalr"
3
+ import { AccountInfo } from "@azure/msal-common";
3
4
  import { IPublicClientApplication, AuthenticationResult } from "@azure/msal-browser"
4
5
  import { deserializeArray } from 'class-transformer';
5
- import { defineHeaders, adminDelete, adminPost, adminsGet, configConsentReadPut, configConsentWritePut, configDelete, configsGet, configPost, configPut, initPost, readerPost, tenantPost, tenantDelete, tenantsGet, workspacePut, workspacesGet } from './hybridspa';
6
+ import { processErrors, adminDelete, adminPost, adminsGet, configConsentReadPut, configConsentWritePut, configDelete, configsGet, configPost, configPut, initPost, readerPost, tenantPost, tenantDelete, tenantsGet, workspacePut, workspacesGet } from './hybridspa';
6
7
  import { version } from './package.json';
7
8
  import users from "./users.json";
8
9
  import tenants from "./tenants.json";
@@ -11,6 +12,7 @@ import workspaces from "./workspaces.json";
11
12
  import tasksData from "./tasks";
12
13
  import syncmilestones from './syncmilestones';
13
14
  import resources from './resources';
15
+ import actors from './actors';
14
16
  import { log } from "console";
15
17
  const FILTER_FIELD = "workspaceIDs";
16
18
  // called by unit tests
@@ -29,6 +31,11 @@ export class APIResult {
29
31
  array: Array<Object> | null;
30
32
  constructor() { this.result = true; this.status = 200; this.error = ""; this.version = version; this.array = null; }
31
33
  }
34
+ export class azureConfig {
35
+ // azure graph REST API endpoints
36
+ static azureElevateAccess: string = "https://management.azure.com/providers/Microsoft.Authorization/elevateAccess?api-version=2016-07-01";
37
+ static azureListRootAssignments: string = "https://management.azure.com/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01&$filter=principalId+eq+";
38
+ };
32
39
  export class mindlineConfig {
33
40
  static environmentTag: string = "dev";
34
41
  // config API endpoints
@@ -120,7 +127,9 @@ export class User {
120
127
  workspaceIDs: string;
121
128
  session: string; // button text
122
129
  spacode: string; // to get front end access token
123
- accessToken: string; // front end access token
130
+ graphAccessToken: string; // front end graph access token
131
+ mindlineAccessToken: string; // front end mindline access token
132
+ azureAccessToken: string; // front end azure access token
124
133
  loginHint: string; // to help sign out without prompt
125
134
  scopes: string[]; // to detect if incremental consent has happened
126
135
  authTS: Date; // timestamp user was authenticated
@@ -137,7 +146,9 @@ export class User {
137
146
  this.workspaceIDs = "";
138
147
  this.session = "Sign In";
139
148
  this.spacode = "";
140
- this.accessToken = "";
149
+ this.graphAccessToken = "";
150
+ this.mindlineAccessToken = "";
151
+ this.azureAccessToken = "";
141
152
  this.loginHint = "";
142
153
  this.scopes = new Array();
143
154
  this.authTS = new Date(0);
@@ -425,7 +436,7 @@ export class InitInfo {
425
436
  newuser.workspaceIDs = user.workspaceIDs;
426
437
  newuser.session = user.session;
427
438
  newuser.spacode = user.spacode;
428
- newuser.accessToken = user.accessToken;
439
+ newuser.graphAccessToken = user.graphAccessToken;
429
440
  newuser.loginHint = user.loginHint;
430
441
  newuser.scopes = user.scopes;
431
442
  newuser.authTS = new Date(user.authTS);
@@ -1231,36 +1242,53 @@ export class TenantNode {
1231
1242
  }
1232
1243
  export class ResourceArray {
1233
1244
  resourceNodes: ResourceNode[];
1234
- constructor(bClearLocalStorage: boolean) {
1245
+ constructor(bInitialize: boolean, bClearLocalStorage: boolean) {
1235
1246
  this.resourceNodes = new Array<ResourceNode>();
1236
- this.init(bClearLocalStorage);
1247
+ if (bInitialize) {
1248
+ this.init(bClearLocalStorage);
1249
+ }
1237
1250
  }
1238
- // get initial data from localStorage or file
1251
+ // get resource data from localStorage or file
1239
1252
  init(bClearLocalStorage: boolean): void {
1240
1253
  console.log(`Calling ResourceArray::init(bClearLocalStorage: ${bClearLocalStorage ? "true" : "false"})`);
1241
1254
  // if we have a non-empty string value stored, read it from localStorage
1242
1255
  if (storageAvailable("localStorage")) {
1243
- let result = localStorage.getItem("RBAC");
1256
+ let result = localStorage.getItem("ResourceArray");
1244
1257
  if (result != null && typeof result === "string" && result !== "") {
1245
1258
  if (bClearLocalStorage) {
1246
- localStorage.removeItem("RBAC");
1259
+ localStorage.removeItem("ResourceArray");
1247
1260
  }
1248
1261
  else {
1249
- let resourceArrayString: string = result;
1250
- let raFromLocalStorage: ResourceArray = JSON.parse(resourceArrayString);
1251
- this.resourceNodes = raFromLocalStorage.resourceNodes;
1262
+ // read entire object from localstorage
1263
+ let raString: string = result;
1264
+ let resourceArray: ResourceArray = JSON.parse(raString);
1265
+ this.resourceNodes = resourceArray.resourceNodes;
1252
1266
  return;
1253
1267
  }
1254
1268
  }
1255
1269
  }
1256
- // if storage unavailable or we were just asked to clear, read defaults to enable usable UI
1257
- var resourcesString = JSON.stringify(resources);
1270
+ // if storage unavailable or we were just asked to clear, read resources from file
1271
+ var resourceNodesString = JSON.stringify(resources);
1258
1272
  try {
1259
- this.resourceNodes = deserializeArray(ResourceNode, resourcesString);
1273
+ this.resourceNodes = deserializeArray(ResourceNode, resourceNodesString);
1260
1274
  } catch (e) {
1261
1275
  debugger;
1262
1276
  }
1263
1277
  }
1278
+ // read
1279
+ async read(instance: IPublicClientApplication, user: User): Promise<ResourceArray> {
1280
+ let resources: ResourceArray = new ResourceArray(false, false);
1281
+ resources.resourceNodes = await readResources(instance, user);
1282
+ return resources;
1283
+ }
1284
+ // save resource data to localstorage
1285
+ save(): void {
1286
+ // if we have localStorage, save resources
1287
+ if (storageAvailable("localStorage")) {
1288
+ let raString: string = JSON.stringify(this);
1289
+ localStorage.setItem("ResourceArray", raString);
1290
+ }
1291
+ }
1264
1292
  }
1265
1293
  export class ResourceNode {
1266
1294
  type: string;
@@ -1276,14 +1304,89 @@ export class ResourceNode {
1276
1304
  this.resources = new Array<ResourceNode>();
1277
1305
  }
1278
1306
  }
1307
+ export class ActorArray {
1308
+ actorNodes: ActorNode[];
1309
+ constructor(bClearLocalStorage: boolean) {
1310
+ this.actorNodes = new Array<ActorNode>();
1311
+ this.init(bClearLocalStorage);
1312
+ }
1313
+ // get initial data from localStorage or file
1314
+ init(bClearLocalStorage: boolean): void {
1315
+ console.log(`Calling ResourceArray::init(bClearLocalStorage: ${bClearLocalStorage ? "true" : "false"})`);
1316
+ // if we have a non-empty string value stored, read it from localStorage
1317
+ if (storageAvailable("localStorage")) {
1318
+ let result = localStorage.getItem("RBACActors");
1319
+ if (result != null && typeof result === "string" && result !== "") {
1320
+ if (bClearLocalStorage) {
1321
+ localStorage.removeItem("RBACActors");
1322
+ }
1323
+ else {
1324
+ let actorArrayString: string = result;
1325
+ let aaFromLocalStorage: ActorArray = JSON.parse(actorArrayString);
1326
+ this.actorNodes = aaFromLocalStorage.actorNodes;
1327
+ return;
1328
+ }
1329
+ }
1330
+ }
1331
+ // if storage unavailable or we were just asked to clear, read defaults to enable usable UI
1332
+ var actorsString = JSON.stringify(actors);
1333
+ try {
1334
+ this.actorNodes = deserializeArray(ActorNode, actorsString);
1335
+ } catch (e) {
1336
+ debugger;
1337
+ }
1338
+ }
1339
+ }
1340
+ export class ActorNode {
1341
+ type: string;
1342
+ actor: string;
1343
+ resource: string;
1344
+ role: string;
1345
+ updatedby: string;
1346
+ updatedon: string;
1347
+ actors: ActorNode[];
1348
+ constructor(type: string, actor: string, resource: string, role: string, updatedby: string, updatedon: string) {
1349
+ this.type = type;
1350
+ this.actor = resource;
1351
+ this.resource = resource;
1352
+ this.role = role;
1353
+ this.updatedby = updatedby;
1354
+ this.updatedon = updatedon;
1355
+ this.actors = new Array<ActorNode>();
1356
+ }
1357
+ }
1279
1358
  // ======================= Azure AD Graph API ===============================
1359
+ // TODO: this is where you want to trigger a re-authentication if token expires
1360
+ async function graphDefineHeaders(
1361
+ instance: IPublicClientApplication,
1362
+ user: User
1363
+ ): Promise<Headers> {
1364
+ const headers = new Headers();
1365
+ headers.append("Content-Type", "application/json");
1366
+ headers.append("accept", "*/*");
1367
+ // authorization header - if needed, retrieve and cache access token
1368
+ if (user.graphAccessToken == null || user.graphAccessToken === "") {
1369
+ try {
1370
+ let response: AuthenticationResult = await instance.acquireTokenByCode({
1371
+ code: user.spacode,
1372
+ });
1373
+ user.graphAccessToken = response.accessToken; // cache access token
1374
+ console.log("Front end token acquired: " + user.graphAccessToken.slice(0, 20));
1375
+ }
1376
+ catch (error: any) {
1377
+ console.log("Front end token failure: " + error);
1378
+ }
1379
+ }
1380
+ headers.append("Authorization", `Bearer ${user.graphAccessToken}`);
1381
+ return headers;
1382
+ }
1280
1383
  export async function groupsGet(instance: IPublicClientApplication, user: User | undefined, groupSearchString: string): Promise<{ groups: Group[], error: string }> {
1281
1384
  // need a logged in user to get graph users
1282
1385
  if (user == null || user.spacode == "") {
1283
1386
  return { groups: [], error: `500: invalid user passed to groupsGet` };
1284
1387
  }
1285
1388
  // create headers
1286
- const headers = await defineHeaders(instance, user);
1389
+ const headers = await graphDefineHeaders(instance, user);
1287
1390
  let options = { method: "GET", headers: headers };
1288
1391
  // make /groups endpoint call
1289
1392
  try {
@@ -1334,7 +1437,7 @@ export async function oauth2PermissionGrantsSet(instance: IPublicClientApplicati
1334
1437
  let grantsurl: string = getGraphEndpoint(loggedInUser.authority);
1335
1438
  grantsurl += graphConfig.graphOauth2PermissionGrantsPredicate + `/${id}`;
1336
1439
  let scopesBody: string = `{ "scope": "${scopes}" }`;
1337
- const headers = await defineHeaders(instance, loggedInUser);
1440
+ const headers = await graphDefineHeaders(instance, loggedInUser);
1338
1441
  let options: RequestInit = { method: "PATCH", headers: headers, body: scopesBody };
1339
1442
  let response = await fetch(grantsurl, options);
1340
1443
  if (response.status == 204 && response.statusText == "No Content") {
@@ -1520,12 +1623,12 @@ export async function tenantRelationshipsGetByDomain(loggedInUser: User, tenant:
1520
1623
  // do we already have a valid tenant name? if so, nothing to add
1521
1624
  if (tenant.name != null && tenant.name !== "") return false;
1522
1625
  // if needed, retrieve and cache access token
1523
- if (loggedInUser.accessToken != null && loggedInUser.accessToken === "") {
1626
+ if (loggedInUser.graphAccessToken != null && loggedInUser.graphAccessToken === "") {
1524
1627
  console.log(`tenantRelationshipsGetByDomain called with invalid logged in user: ${loggedInUser.name}`);
1525
1628
  try {
1526
- let response: AuthenticationResult = await instance.acquireTokenByCode({ code: loggedInUser.spacode });
1527
- loggedInUser.accessToken = response.accessToken; // cache access token on the user
1528
- console.log("tenantRelationshipsGetByDomain: Front end token acquired: " + loggedInUser.accessToken.slice(0, 20));
1629
+ let response: AuthenticationResult = await instance.acquireTokenByCode({ code: loggedInUser.spacode, scopes: ["user.read", "contacts.read", "CrossTenantInformation.ReadBasic.All"] });
1630
+ loggedInUser.graphAccessToken = response.accessToken; // cache access token on the user
1631
+ console.log("tenantRelationshipsGetByDomain: Front end token acquired: " + loggedInUser.graphAccessToken.slice(0, 20));
1529
1632
  }
1530
1633
  catch (error: any) {
1531
1634
  console.log("tenantRelationshipsGetByDomain: Front end token failure: " + error);
@@ -1534,7 +1637,7 @@ export async function tenantRelationshipsGetByDomain(loggedInUser: User, tenant:
1534
1637
  }
1535
1638
  // prepare Authorization headers as part of options
1536
1639
  const headers = new Headers();
1537
- const bearer = `Bearer ${loggedInUser.accessToken}`;
1640
+ const bearer = `Bearer ${loggedInUser.graphAccessToken}`;
1538
1641
  headers.append("Authorization", bearer);
1539
1642
  let options = { method: "GET", headers: headers };
1540
1643
  // make tenant endpoint call
@@ -1578,11 +1681,11 @@ export async function tenantRelationshipsGetById(loggedInUser: User, tenant: Ten
1578
1681
  console.log("**** tenantRelationshipsGetById");
1579
1682
  if (debug) debugger;
1580
1683
  // if needed, retrieve and cache access token
1581
- if (loggedInUser.accessToken === "") {
1684
+ if (loggedInUser.graphAccessToken === "") {
1582
1685
  try {
1583
- let response: AuthenticationResult = await instance.acquireTokenByCode({ code: loggedInUser.spacode });
1584
- loggedInUser.accessToken = response.accessToken; // cache access token
1585
- console.log("tenantRelationshipsGetById: Front end token acquired: " + loggedInUser.accessToken.slice(0, 20));
1686
+ let response: AuthenticationResult = await instance.acquireTokenByCode({ code: loggedInUser.spacode, scopes: ["user.read", "contacts.read", "CrossTenantInformation.ReadBasic.All"] });
1687
+ loggedInUser.graphAccessToken = response.accessToken; // cache access token
1688
+ console.log("tenantRelationshipsGetById: Front end token acquired: " + loggedInUser.graphAccessToken.slice(0, 20));
1586
1689
  }
1587
1690
  catch (error: any) {
1588
1691
  console.log("tenantRelationshipsGetById: Front end token failure: " + error);
@@ -1591,7 +1694,7 @@ export async function tenantRelationshipsGetById(loggedInUser: User, tenant: Ten
1591
1694
  }
1592
1695
  // prepare Authorization headers as part of options
1593
1696
  const headers = new Headers();
1594
- const bearer = `Bearer ${loggedInUser.accessToken}`;
1697
+ const bearer = `Bearer ${loggedInUser.graphAccessToken}`;
1595
1698
  headers.append("Authorization", bearer);
1596
1699
  let options = { method: "GET", headers: headers };
1597
1700
  // make tenant endpoint call
@@ -1648,7 +1751,7 @@ export async function tenantUnauthenticatedLookup(tenant: Tenant, debug: boolean
1648
1751
  openidEndpoint += "/.well-known/openid-configuration";
1649
1752
  console.log("Attempting GET from openid well-known endpoint: ", openidEndpoint);
1650
1753
  response = await fetch(openidEndpoint);
1651
- if (response.status == 200 && response.statusText == "OK") {
1754
+ if (response.status == 200) {
1652
1755
  let data = await response.json();
1653
1756
  if (data) {
1654
1757
  // store tenant ID and authority
@@ -1688,7 +1791,7 @@ export async function userDelegatedScopesGet(instance: IPublicClientApplication,
1688
1791
  return { scopes: null, id: null, error: `500: invalid parameter(s) passed to getUserDelegatedScopes` };
1689
1792
  }
1690
1793
  // create headers
1691
- const headers = await defineHeaders(instance, loggedInUser);
1794
+ const headers = await graphDefineHeaders(instance, loggedInUser);
1692
1795
  let options: RequestInit = { method: "GET", headers: headers };
1693
1796
  try {
1694
1797
  // first, cache Graph resource ID (service principal) for this tenant if we don't have it already
@@ -1744,12 +1847,12 @@ export async function userDelegatedScopesRemove(instance: IPublicClientApplicati
1744
1847
  export async function usersGet(instance: IPublicClientApplication, user: User | undefined): Promise<{ users: string[], error: string }> {
1745
1848
  // need a logged in user to get graph users
1746
1849
  if (user == null || user.spacode == "") {
1747
- return { users: [], error: `500: invalid user passed to groupsGet` };
1850
+ return { users: [], error: `500: invalid user passed to usersGet` };
1748
1851
  }
1749
1852
  // make /users endpoint call
1750
1853
  try {
1751
1854
  // create headers
1752
- const headers = await defineHeaders(instance, user);
1855
+ const headers = await graphDefineHeaders(instance, user);
1753
1856
  let options = { method: "GET", headers: headers };
1754
1857
  let usersEndpoint = getGraphEndpoint(user.authority);
1755
1858
  usersEndpoint += graphConfig.graphUsersPredicate;
@@ -1844,7 +1947,8 @@ export async function configsRefresh(instance: IPublicClientApplication, authori
1844
1947
  export async function initGet(instance: IPublicClientApplication, user: User, ii: InitInfo, tasks: TaskArray, debug: boolean): Promise<APIResult> {
1845
1948
  console.log(`>>>>>> initGet`);
1846
1949
  let result: APIResult = new APIResult();
1847
- if (debug) debugger;
1950
+ if (debug)
1951
+ debugger;
1848
1952
  // lookup authority for this user (the lookup call does it based on domain, but TID works as well to find authority)
1849
1953
  let tenant: Tenant = new Tenant();
1850
1954
  tenant.tid = user.tid;
@@ -2135,4 +2239,146 @@ async function workspaceInfoGet(instance: IPublicClientApplication, user: User,
2135
2239
  result.result = false;
2136
2240
  result.status = 500;
2137
2241
  return result;
2242
+ }
2243
+ // ======================= Azure REST API ===============================
2244
+ // TODO: this is where you want to trigger a re-authentication if token expires
2245
+ async function azureDefineHeaders(
2246
+ instance: IPublicClientApplication,
2247
+ user: User
2248
+ ): Promise<Headers> {
2249
+ const headers = new Headers();
2250
+ headers.append("Content-Type", "application/json");
2251
+ headers.append("accept", "*/*");
2252
+ // authorization header - if needed, retrieve and cache access token
2253
+ if (user.azureAccessToken == null || user.azureAccessToken === "") {
2254
+ try {
2255
+ let accounts: AccountInfo[] = instance.getAllAccounts();
2256
+ let homeAccountId = user.oid + "." + user.tid;
2257
+ let account: AccountInfo = null;
2258
+ for (let i: number = 0; i < accounts.length; i++) {
2259
+ if (accounts[i].homeAccountId == homeAccountId) {
2260
+ account = accounts[i];
2261
+ }
2262
+ }
2263
+ debugger;
2264
+ let response: AuthenticationResult = await instance.acquireTokenSilent({
2265
+ scopes: ["https://management.azure.com/user_impersonation"],
2266
+ account: account
2267
+ });
2268
+ user.azureAccessToken = response.accessToken; // cache access token
2269
+ console.log("Front end token acquired silently: " + user.azureAccessToken.slice(0, 20));
2270
+ }
2271
+ catch (error: any) {
2272
+ try {
2273
+ console.log("Front end token silent acquisition failure: " + error);
2274
+ // fallback to redirect if silent acquisition fails
2275
+ let accounts: AccountInfo[] = instance.getAllAccounts();
2276
+ let homeAccountId = user.oid + "." + user.tid;
2277
+ let account: AccountInfo = null;
2278
+ for (let i: number = 0; i < accounts.length; i++) {
2279
+ if (accounts[i].homeAccountId == homeAccountId) {
2280
+ account = accounts[i];
2281
+ }
2282
+ }
2283
+ instance.acquireTokenRedirect({
2284
+ scopes: ["https://management.azure.com/user_impersonation"],
2285
+ account: account
2286
+ });
2287
+ }
2288
+ catch (error: any) {
2289
+ console.log("Front end token popup acquisition failure: " + error);
2290
+ }
2291
+ }
2292
+ }
2293
+ headers.append("Authorization", `Bearer ${user.azureAccessToken}`);
2294
+ return headers;
2295
+ }
2296
+ export async function canListRootAssignments(instance: IPublicClientApplication, user: User): Promise<boolean> {
2297
+ // need a logged in user to call Azure REST API
2298
+ if (user == null || user.spacode == "") {
2299
+ return false;
2300
+ }
2301
+ // make Azure REST API call
2302
+ try {
2303
+ // create headers
2304
+ const headers = await azureDefineHeaders(instance, user);
2305
+ let options = { method: "GET", headers: headers };
2306
+ let listrootassignmentsEndpoint: string = azureConfig.azureListRootAssignments;
2307
+ listrootassignmentsEndpoint += "'";
2308
+ listrootassignmentsEndpoint += user.oid;
2309
+ listrootassignmentsEndpoint += "'";
2310
+ let response = await fetch(listrootassignmentsEndpoint, options);
2311
+ if (response.status == 200) {
2312
+ let data = await response.json();
2313
+ debugger;
2314
+ console.log("Successful call to Azure Resource Graph list root assignments");
2315
+ }
2316
+ else {
2317
+ console.log(await processErrors(response));
2318
+ return false;
2319
+ }
2320
+ }
2321
+ catch (error: any) {
2322
+ console.log(error);
2323
+ return false;
2324
+ }
2325
+ return true;
2326
+ }
2327
+ export async function elevateGlobalAdminToUserAccessAdmin(instance: IPublicClientApplication, user: User): Promise<boolean> {
2328
+ // need a logged in user to call Azure REST API
2329
+ if (user == null || user.spacode == "") {
2330
+ return false;
2331
+ }
2332
+ // make Azure REST API call
2333
+ try {
2334
+ // create headers
2335
+ const headers = await azureDefineHeaders(instance, user);
2336
+ let options = { method: "POST", headers: headers };
2337
+ let elevateaccessEndpoint: string = azureConfig.azureElevateAccess;
2338
+ let response = await fetch(elevateaccessEndpoint, options);
2339
+ if (response.status == 200) {
2340
+ console.log("Successful call to Azure Resource Graph list root assignments");
2341
+ }
2342
+ else {
2343
+ console.log(await processErrors(response));
2344
+ return false;
2345
+ }
2346
+ }
2347
+ catch (error: any) {
2348
+ console.log(error);
2349
+ return false;
2350
+ }
2351
+ return true;
2352
+ }
2353
+ async function readResources(instance: IPublicClientApplication, user: User): Promise<ResourceNode[]> {
2354
+ // need a logged in user to call Azure REST API
2355
+ let resources: ResourceNode[] = new Array<ResourceNode>();
2356
+ if (user == null || user.spacode == "") {
2357
+ return resources;
2358
+ }
2359
+ // make Azure REST API call
2360
+ try {
2361
+ // create headers
2362
+ const headers = await azureDefineHeaders(instance, user);
2363
+ let options = { method: "GET", headers: headers };
2364
+ let listrootassignmentsEndpoint: string = azureConfig.azureListRootAssignments;
2365
+ listrootassignmentsEndpoint += "'";
2366
+ listrootassignmentsEndpoint += user.oid;
2367
+ listrootassignmentsEndpoint += "'";
2368
+ let response = await fetch(listrootassignmentsEndpoint, options);
2369
+ if (response.status == 200) {
2370
+ let data = await response.json();
2371
+ debugger;
2372
+ console.log("Successful call to Azure Resource Graph list root assignments");
2373
+ }
2374
+ else {
2375
+ console.log(await processErrors(response));
2376
+ return false;
2377
+ }
2378
+ }
2379
+ catch (error: any) {
2380
+ console.log(error);
2381
+ return false;
2382
+ }
2383
+ return true;
2138
2384
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mindline/sync",
3
3
  "type": "module",
4
- "version": "1.0.74",
4
+ "version": "1.0.76",
5
5
  "types": "index.d.ts",
6
6
  "exports": "./index.ts",
7
7
  "description": "sync is a node.js package encapsulating javscript classes required for configuring Mindline sync service.",