@mindline/sync 1.0.68 → 1.0.70

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,5 @@
2
2
  "ExpandedNodes": [
3
3
  ""
4
4
  ],
5
- "SelectedNode": "\\README.md",
6
5
  "PreviewInSolutionExplorer": false
7
6
  }
package/.vs/slnx.sqlite CHANGED
Binary file
Binary file
@@ -2,10 +2,6 @@
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\\README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
7
- "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:README.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
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}"
@@ -64,57 +60,46 @@
64
60
  },
65
61
  {
66
62
  "DockedWidth": 200,
67
- "SelectedChildIndex": 0,
63
+ "SelectedChildIndex": 2,
68
64
  "Children": [
69
65
  {
70
66
  "$type": "Document",
71
- "DocumentIndex": 0,
72
- "Title": "README.md",
73
- "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\README.md",
74
- "RelativeDocumentMoniker": "README.md",
75
- "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\README.md",
76
- "RelativeToolTip": "README.md",
77
- "ViewState": "AQIAAAAAAAAAAAAAAAAAABoAAAASAAAA",
78
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
79
- "WhenOpened": "2024-01-05T21:52:59.93Z",
67
+ "DocumentIndex": 2,
68
+ "Title": "hybridspa.ts",
69
+ "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\hybridspa.ts",
70
+ "RelativeDocumentMoniker": "hybridspa.ts",
71
+ "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\hybridspa.ts",
72
+ "RelativeToolTip": "hybridspa.ts",
73
+ "ViewState": "AQIAAOcAAAAAAAAAAAAwwBEBAAAHAAAA",
74
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|",
75
+ "WhenOpened": "2023-11-25T02:18:34.862Z",
80
76
  "EditorCaption": ""
81
77
  },
82
78
  {
83
79
  "$type": "Document",
84
- "DocumentIndex": 2,
80
+ "DocumentIndex": 1,
85
81
  "Title": "index.ts",
86
82
  "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.ts",
87
83
  "RelativeDocumentMoniker": "index.ts",
88
84
  "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.ts",
89
85
  "RelativeToolTip": "index.ts",
90
- "ViewState": "AQIAAMsGAAAAAAAAAAAvwDkHAAABAAAA",
86
+ "ViewState": "AQIAANgAAAAAAAAAAAAwwO4AAAANAAAA",
91
87
  "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|",
92
- "WhenOpened": "2023-12-05T02:10:16.04Z"
88
+ "WhenOpened": "2023-12-05T02:10:16.04Z",
89
+ "EditorCaption": ""
93
90
  },
94
91
  {
95
92
  "$type": "Document",
96
- "DocumentIndex": 1,
93
+ "DocumentIndex": 0,
97
94
  "Title": "index.d.ts",
98
95
  "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.d.ts",
99
96
  "RelativeDocumentMoniker": "index.d.ts",
100
97
  "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\index.d.ts",
101
98
  "RelativeToolTip": "index.d.ts",
102
- "ViewState": "AQIAAAMBAAAAAAAAAEAwwC8BAAAoAAAA",
99
+ "ViewState": "AQIAAGkAAAAAAAAAAAAwwH8AAAAIAAAA",
103
100
  "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|",
104
101
  "WhenOpened": "2024-01-01T16:19:43.325Z",
105
102
  "EditorCaption": ""
106
- },
107
- {
108
- "$type": "Document",
109
- "DocumentIndex": 3,
110
- "Title": "hybridspa.ts",
111
- "DocumentMoniker": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\hybridspa.ts",
112
- "RelativeDocumentMoniker": "hybridspa.ts",
113
- "ToolTip": "C:\\Users\\ArvindSuthar\\source\\repos\\front\\sync\\hybridspa.ts",
114
- "RelativeToolTip": "hybridspa.ts",
115
- "ViewState": "AQIAAB8AAAAAAAAAAAAGwFUAAAAEAAAA",
116
- "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003213|",
117
- "WhenOpened": "2023-11-25T02:18:34.862Z"
118
103
  }
119
104
  ]
120
105
  },
package/hybridspa.ts CHANGED
@@ -217,6 +217,88 @@ export async function adminPost(
217
217
  }
218
218
  return result;
219
219
  }
220
+ //configConsentReadPut
221
+ export async function configConsentReadPut(instance: IPublicClientApplication, authorizedUser: User, configId: string, tid: string, consent: boolean): Promise<APIResult> {
222
+ let result: APIResult = new APIResult();
223
+ // create parameterized config consent endpoint
224
+ let endpoint: string = mindlineConfig.configConsentEndpoint();
225
+ let url: URL = new URL(endpoint);
226
+ url.searchParams.append("configurationId", configId);
227
+ // create headers
228
+ const headers = await defineHeaders(instance, authorizedUser);
229
+ // create body
230
+ let configConsentReadBody: string = `
231
+ {
232
+ "tenantId": "${tid}",
233
+ "isReadPermissionConsented": ${consent ? "true" : "false"}
234
+ }`;
235
+ // make endpoint call
236
+ let options = { method: "PUT", headers: headers, body: configConsentReadBody };
237
+ try {
238
+ console.log("Attempting PUT read consent to /configuration/consent: " + url.href);
239
+ let response = await fetch(url.href, options);
240
+ if (response.status === 200 && response.statusText === "OK") {
241
+ console.log(`Successful PUT to ${url.href}`);
242
+ return result;
243
+ }
244
+ else {
245
+ result.error = await processErrors(response);
246
+ console.log(`Failed PUT to ${url.href}`);
247
+ console.log(result.error);
248
+ result.status = 500;
249
+ result.result = false;
250
+ return result;
251
+ }
252
+ }
253
+ catch (error: any) {
254
+ result.error = error.message;
255
+ result.status = 500;
256
+ result.result = false;
257
+ console.log(error.message);
258
+ }
259
+ return result;
260
+ }
261
+ //configConsentWritePut
262
+ export async function configConsentWritePut(instance: IPublicClientApplication, authorizedUser: User, configId: string, tid: string, consent: boolean): Promise<APIResult> {
263
+ let result: APIResult = new APIResult();
264
+ // create parameterized config consent endpoint
265
+ let endpoint: string = mindlineConfig.configConsentEndpoint();
266
+ let url: URL = new URL(endpoint);
267
+ url.searchParams.append("configurationId", configId);
268
+ // create headers
269
+ const headers = await defineHeaders(instance, authorizedUser);
270
+ // create body
271
+ let configConsentWriteBody: string = `
272
+ {
273
+ "tenantId": "${tid}",
274
+ "isWritePermissionConsented": ${consent ? "true" : "false"}
275
+ }`;
276
+ // make endpoint call
277
+ let options = { method: "PUT", headers: headers, body: configConsentWriteBody };
278
+ try {
279
+ console.log("Attempting PUT read consent to /configuration/consent: " + url.href);
280
+ let response = await fetch(url.href, options);
281
+ if (response.status === 200 && response.statusText === "OK") {
282
+ console.log(`Successful PUT to ${url.href}`);
283
+ return result;
284
+ }
285
+ else {
286
+ result.error = await processErrors(response);
287
+ console.log(`Failed PUT to ${url.href}`);
288
+ console.log(result.error);
289
+ result.status = 500;
290
+ result.result = false;
291
+ return result;
292
+ }
293
+ }
294
+ catch (error: any) {
295
+ result.error = error.message;
296
+ result.status = 500;
297
+ result.result = false;
298
+ console.log(error.message);
299
+ }
300
+ return result;
301
+ }
220
302
  //configDelete
221
303
  export async function configDelete(
222
304
  instance: IPublicClientApplication,
@@ -696,71 +778,6 @@ export async function tenantPost(
696
778
  }
697
779
  return result;
698
780
  }
699
- //tenantPut: write access token for onboarded tenant
700
- export async function tenantPut(
701
- instance: IPublicClientApplication,
702
- authorizedUser: User,
703
- tenant: Tenant,
704
- debug: boolean
705
- ): Promise<APIResult> {
706
- let result: APIResult = new APIResult();
707
- // we expect valid tid
708
- if (tenant.tid === "") {
709
- result.result = false;
710
- result.error = "tenantPut: invalid tid";
711
- result.status = 500;
712
- return result;
713
- }
714
- // create tenant endpoint
715
- let endpoint: string = mindlineConfig.tenantEndpoint();
716
- // create tenant headers
717
- const headers = await defineHeaders(instance, authorizedUser);
718
- // establish read and write service principals ("notassigned" is default")
719
- let readServicePrincipal: string = "null";
720
- let writeServicePrincipal: string = "null";
721
- if (tenant.permissionType == "write") {
722
- writeServicePrincipal = `"1"`;
723
- }
724
- else if (tenant.permissionType == "read") {
725
- readServicePrincipal = `"1"`;
726
- }
727
- // create tenant body
728
- let tenantBody: string = `{"tenantId": "${tenant.tid}",
729
- "name": "${tenant.name}",
730
- "domain": "${tenant.domain}",
731
- "type": "${tenant.tenantType}",
732
- "permissionType": "${tenant.permissionType}",
733
- "isOnboarded": ${tenant.onboarded == "true"},
734
- "authority": "${tenant.authority}",
735
- "readServicePrincipal": ${readServicePrincipal},
736
- "writeServicePrincipal": ${writeServicePrincipal}}`;
737
- let options = { method: "PUT", headers: headers, body: tenantBody };
738
- // make tenant endpoint call
739
- try {
740
- if (debug) debugger;
741
- console.log(`Attempting PUT to ${endpoint}`);
742
- let response = await fetch(endpoint, options);
743
- if (response.status === 200 && response.statusText === "OK") {
744
- console.log(`Successful PUT to ${endpoint}`);
745
- return result;
746
- }
747
- else {
748
- console.log(`Failed PUT to ${endpoint}: ${tenantBody}`);
749
- result.error = await processErrors(response);
750
- console.log(result.error);
751
- result.status = 500;
752
- result.result = false;
753
- return result;
754
- }
755
- }
756
- catch (error: any) {
757
- result.error = error.message;
758
- result.status = 500;
759
- result.result = false;
760
- console.log(result.error);
761
- }
762
- return result;
763
- }
764
781
  //workspacePut
765
782
  export async function workspacePut(instance: IPublicClientApplication, authorizedUser: User, workspaceId: string, workspaceName: string): Promise<APIResult> {
766
783
  let result: APIResult = new APIResult();
package/index.d.ts CHANGED
@@ -99,8 +99,6 @@ declare module "@mindline/sync" {
99
99
  name: string; // findTenantInformationByTenantId
100
100
  domain: string; // findTenantInformationByTenantId
101
101
  tenantType: TenantTypeStrings; // always "aad" for now
102
- permissionType: TenantPermissionTypeStrings; // read/write/notassigned
103
- onboarded: string; // have we onboarded this tenant? "true" or "false"
104
102
  authority: string; // from AAD ID auth response
105
103
  workspaceIDs: string;
106
104
  sel: boolean; // selection state
@@ -126,6 +124,8 @@ declare module "@mindline/sync" {
126
124
  usersWritten: number;
127
125
  configId: string;
128
126
  batchId: string;
127
+ isReadPermissionConsented: boolean;
128
+ isWritePermissionConsented: boolean;
129
129
  }
130
130
  export class Config {
131
131
  id: string;
@@ -156,6 +156,8 @@ declare module "@mindline/sync" {
156
156
  ts: Tenant[];
157
157
  cs: Config[];
158
158
  ws: Workspace[];
159
+ configlevelconsent_configid: string;
160
+ configlevelconsent_access: TenantConfigType;
159
161
  constructor(bClearLocalStorage: boolean);
160
162
  init(bClearLocalStorage: boolean): void;
161
163
  save(): void;
@@ -166,12 +168,7 @@ declare module "@mindline/sync" {
166
168
  "reload React" |
167
169
  "GET tenant details" |
168
170
  "POST config init" |
169
- "GET workspaces" |
170
- "onboard tenant" |
171
- "create 2nd tenant" |
172
- "invite 2nd admin" |
173
- "onboard 2nd tenant" |
174
- "create config";
171
+ "GET workspaces";
175
172
  export class TaskArray {
176
173
  tasks: Task[];
177
174
  constructor(bClearLocalStorage: boolean);
@@ -273,7 +270,7 @@ declare module "@mindline/sync" {
273
270
  //
274
271
  export function groupsGet(instance: IPublicClientApplication, user: User | undefined, groupSearchString: string): Promise<{ groups: Group[], error: string }>;
275
272
  export function oauth2PermissionGrantsGet(options: RequestInit, user: User, spid: string, oid: string): Promise<{grants: string, error: string}>;
276
- export function requestAdminConsent(user: User, scope: string): void;
273
+ export function requestAdminConsent(admin: User, tct: TenantConfigType): void;
277
274
  export function servicePrincipalGet(options: RequestInit, user: User, appid: string): Promise<{ spid: string, error: string }>;
278
275
  export function signIn(user: User, tasks: TaskArray): boolean;
279
276
  export function signInIncrementally(user: User, scope: string): void;
@@ -287,6 +284,8 @@ declare module "@mindline/sync" {
287
284
  //
288
285
  // Mindline Config API
289
286
  //
287
+ export function configConsentForRead(instance: IPublicClientApplication, authorizedUser: User, configId: string, tid: string, consent: boolean): Promise<APIResult>;
288
+ export function configConsentForWrite(instance: IPublicClientApplication, authorizedUser: User, configId: string, tid: string, consent: boolean): Promise<APIResult>;
290
289
  export function configEdit(
291
290
  instance: IPublicClientApplication,
292
291
  authorizedUser: User,
@@ -300,7 +299,6 @@ declare module "@mindline/sync" {
300
299
  export function configsRefresh(instance: IPublicClientApplication, authorizedUser: User, workspaceId: string, ii: InitInfo, debug: boolean): APIResult;
301
300
  export function initGet(instance: IPublicClientApplication, authorizedUser: User, user: User, ii: InitInfo, tasks: TaskArray, debug: boolean): APIResult;
302
301
  export function tenantAdd(instance: IPublicClientApplication, authorizedUser: User, tenant: Tenant, workspaceId: string): APIResult;
303
- export function tenantComplete(instance: IPublicClientApplication, authorizedUser: User, tenant: Tenant, debug: boolean): APIResult;
304
302
  export function tenantRemove(instance: IPublicClientApplication, authorizedUser: User, tenant: Tenant, workspaceId: string, debug: boolean): APIResult;
305
303
  export function userAdd(instance: IPublicClientApplication, authorizedUser: User, user: User, workspaceId: string): APIResult;
306
304
  export function userRemove(instance: IPublicClientApplication, authorizedUser: User, user: User, workspaceId: string): APIResult;
package/index.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  import * as signalR from "@microsoft/signalr"
3
3
  import { IPublicClientApplication, AuthenticationResult } from "@azure/msal-browser"
4
4
  import { deserializeArray } from 'class-transformer';
5
- import { defineHeaders, adminDelete, adminPost, adminsGet, configDelete, configsGet, configPost, configPut, initPost, readerPost, tenantPut, tenantPost, tenantDelete, tenantsGet, workspacePut, workspacesGet } from './hybridspa';
5
+ import { defineHeaders, adminDelete, adminPost, adminsGet, configConsentReadPut, configConsentWritePut, configDelete, configsGet, configPost, configPut, initPost, readerPost, tenantPost, tenantDelete, tenantsGet, workspacePut, workspacesGet } from './hybridspa';
6
6
  import { version } from './package.json';
7
7
  import users from "./users.json";
8
8
  import tenants from "./tenants.json";
@@ -40,6 +40,9 @@ export class mindlineConfig {
40
40
  static adminsEndpoint(): string {
41
41
  return `https://${mindlineConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/admins`;
42
42
  };
43
+ static configConsentEndpoint(): string {
44
+ return `https://${mindlineConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/configuration/consent`;
45
+ };
43
46
  static configEndpoint(): string {
44
47
  return `https://${mindlineConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/configuration`;
45
48
  };
@@ -159,8 +162,6 @@ export class Tenant {
159
162
  name: string;
160
163
  domain: string;
161
164
  tenantType: TenantTypeStrings;
162
- permissionType: TenantPermissionTypeStrings;
163
- onboarded: string;
164
165
  authority: string;
165
166
  workspaceIDs: string;
166
167
  sel: boolean; // selection state
@@ -171,8 +172,6 @@ export class Tenant {
171
172
  this.name = "";
172
173
  this.domain = "";
173
174
  this.tenantType = "aad";
174
- this.permissionType = "notassigned";
175
- this.onboarded = "false";
176
175
  this.authority = "";
177
176
  this.workspaceIDs = "";
178
177
  this.sel = false;
@@ -180,14 +179,15 @@ export class Tenant {
180
179
  this.lookupfield = "Domain";
181
180
  }
182
181
  }
183
- function getAppId(authority: string): string {
182
+ function getAppId(authority: string, tct: TenantConfigType): string {
184
183
  switch (authority) {
185
- case graphConfig.authorityWW: return "63100afe-506e-4bb2-8ff7-d8d5ab373129";
186
- case graphConfig.authorityUS: return "762d313c-dcfd-4582-8cc5-53cc9844f62e";
187
- case graphConfig.authorityCN: return "814e0ebd-ada6-42b4-b8ae-e26f3861a0aa";
184
+ case graphConfig.authorityWW: return tct === TenantConfigType.source ? "85d35da2-4118-4b03-aa05-605cedd7f2f8" : "63100afe-506e-4bb2-8ff7-d8d5ab373129";
185
+ case graphConfig.authorityUS: return tct === TenantConfigType.source ? "b08630c7-e227-4215-9746-afc9286fb864" : "17aa5d5a-f09f-4cec-87a6-28596f9fa513";
186
+ case graphConfig.authorityCN: return tct === TenantConfigType.source ? "7db7293b-add9-4a3f-8562-1a20bfe27d5e" : "debd015b-1154-4111-a4cb-fc220a537697";
188
187
  default: debugger; return "";
189
188
  }
190
189
  }
190
+ // https://learn.microsoft.com/en-us/graph/deployments
191
191
  function getGraphEndpoint(authority: string): string {
192
192
  switch (authority) {
193
193
  case graphConfig.authorityWW: return "https://graph.microsoft.com/";
@@ -196,10 +196,11 @@ function getGraphEndpoint(authority: string): string {
196
196
  default: debugger; return "";
197
197
  }
198
198
  }
199
+ // https://learn.microsoft.com/en-us/entra/identity-platform/authentication-national-cloud
199
200
  function getLoginEndpoint(authority: string): string {
200
201
  switch (authority) {
201
- case graphConfig.authorityWW: return "https://login.microsoft.com/";
202
- case graphConfig.authorityUS: return "https://login.microsoft.us/";
202
+ case graphConfig.authorityWW: return "https://login.microsoftonline.com/";
203
+ case graphConfig.authorityUS: return "https://login.microsoftonline.us/";
203
204
  case graphConfig.authorityCN: return "https://login.partner.microsoftonline.cn/";
204
205
  default: debugger; return "";
205
206
  }
@@ -221,6 +222,8 @@ export class TenantConfigInfo {
221
222
  usersWritten: number;
222
223
  configId: string;
223
224
  batchId: string;
225
+ isReadPermissionConsented: boolean;
226
+ isWritePermissionConsented: boolean;
224
227
  constructor() {
225
228
  this.tid = "";
226
229
  this.sourceGroupId = "";
@@ -232,6 +235,8 @@ export class TenantConfigInfo {
232
235
  this.usersWritten = 0;
233
236
  this.configId = "";
234
237
  this.batchId = "";
238
+ this.isReadPermissionConsented = false;
239
+ this.isWritePermissionConsented = false;
235
240
  }
236
241
  }
237
242
  export class Config {
@@ -304,6 +309,8 @@ export class InitInfo {
304
309
  ts: Tenant[];
305
310
  cs: Config[];
306
311
  ws: Workspace[];
312
+ configlevelconsent_configid: string;
313
+ configlevelconsent_access: TenantConfigType;
307
314
  constructor(bClearLocalStorage: boolean) {
308
315
  this.init(bClearLocalStorage);
309
316
  }
@@ -328,13 +335,15 @@ export class InitInfo {
328
335
  }
329
336
  }
330
337
  // if storage unavailable or we were just asked to clear, read defaults to enable usable UI
338
+ this.tab = 0;
339
+ this.version = version;
340
+ this.configlevelconsent_configid = "";
341
+ this.configlevelconsent_access = TenantConfigType.sourcetarget;
331
342
  var usersString = JSON.stringify(users);
332
343
  var tenantsString = JSON.stringify(tenants);
333
344
  var configsString = JSON.stringify(configs);
334
345
  var workspacesString = JSON.stringify(workspaces);
335
346
  try {
336
- this.tab = 0;
337
- this.version = version;
338
347
  this.us = deserializeArray(User, usersString);
339
348
  this.ts = deserializeArray(Tenant, tenantsString);
340
349
  this.cs = deserializeArray(Config, configsString);
@@ -396,6 +405,9 @@ export class InitInfo {
396
405
  }
397
406
  #initFromObjects(ii: InitInfo): void {
398
407
  this.tab = ii.tab;
408
+ this.version = version;
409
+ this.configlevelconsent_configid = ii.configlevelconsent_configid;
410
+ this.configlevelconsent_access = ii.configlevelconsent_access;
399
411
  if (typeof ii.us === "undefined") {
400
412
  this.us = new Array<User>();
401
413
  }
@@ -431,8 +443,6 @@ export class InitInfo {
431
443
  newtenant.name = tenant.name;
432
444
  newtenant.domain = tenant.domain;
433
445
  newtenant.tenantType = tenant.tenantType;
434
- newtenant.permissionType = tenant.permissionType;
435
- newtenant.onboarded = tenant.onboarded;
436
446
  newtenant.authority = tenant.authority;
437
447
  newtenant.workspaceIDs = tenant.workspaceIDs;
438
448
  newtenant.sel = tenant.sel;
@@ -480,12 +490,7 @@ export type TaskType = "initialization" |
480
490
  "reload React" |
481
491
  "GET tenant details" |
482
492
  "POST config init" |
483
- "GET workspaces" |
484
- "onboard tenant" |
485
- "create 2nd tenant" |
486
- "invite 2nd admin" |
487
- "onboard 2nd tenant" |
488
- "create config";
493
+ "GET workspaces";
489
494
  export class TaskArray {
490
495
  tasks: Task[];
491
496
  constructor(bClearLocalStorage: boolean) {
@@ -1299,31 +1304,33 @@ export async function oauth2PermissionGrantsSet(instance: IPublicClientApplicati
1299
1304
  return false;
1300
1305
  }
1301
1306
  }
1302
- export function requestAdminConsent(user: User, scope: string): void {
1303
- if (user.oid == "1") return;
1307
+ export function requestAdminConsent(admin: User, tct: TenantConfigType): void {
1304
1308
  //
1305
1309
  // for app permissions (app roles) we must use the /.default scope for admin consent
1306
1310
  // https://learn.microsoft.com/EN-US/azure/active-directory/develop/v2-admin-consent#:~:text=In%20order%20to%20request%20app%20permissions%2C%20you%20must%20use%20the%20/.default%20value.
1307
1311
  // https://learn.microsoft.com/en-us/answers/questions/431784/how-to-grant-application-permissions-with-dynamic
1308
1312
  //
1309
1313
  // this means that, if we want to be granular about SyncReader vs. SyncWriter permissions, we must have separate Applications
1310
- // for now, we request the /.default scope whenever any of the SyncReader or SyncWriter permissions are requested
1314
+ // in addition, if there are permissions like Group.Read.All, that also must be admin consented
1315
+ // for now, this function will perform admin consent for the Reader and Writer applications, we will come back to Group.Read.All and User.Read.All
1311
1316
  //
1312
- // trying to use the Challenge endpoint for app permissions, setting this scope caused the call to quietly fail without error
1313
- // scope = "63100afe-506e-4bb2-8ff7-d8d5ab373129/.default";
1317
+ // FYI - using the Challenge endpoint for app permissions caused the call to quietly fail without error
1318
+ // FYI - using the Challenge endpoint for this scope also caused the call to quietly fail withour error
1319
+ // scope = "63100afe-506e-4bb2-8ff7-d8d5ab373129/.default";
1314
1320
  //
1315
- // thereforce, we are assuming that, for app permissions (app roles), the Microsoft Identity Web Challenge endpoint does not work and we need to perform our own redirect to the admin consent endpoint
1321
+ // thereforce, we are assuming that, for app permissions (app roles) or delegated permissions requiring admin consent, the Microsoft Identity Web Challenge endpoint does not work and we need to perform our own redirect to the admin consent endpoint
1316
1322
  // https://learn.microsoft.com/EN-US/azure/active-directory/develop/scopes-oidc#client-credentials-grant-flow-and-default
1317
1323
  // https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#request-the-permissions-from-a-directory-admin
1318
1324
  //
1319
- let adminConsentURL: string = getLoginEndpoint(user.authority);
1320
- adminConsentURL += user.tid;
1325
+ let adminConsentURL: string = getLoginEndpoint(admin.authority);
1326
+ adminConsentURL += admin.tid;
1321
1327
  adminConsentURL += "/adminconsent";
1322
1328
  let url: URL = new URL(adminConsentURL);
1323
- let clientId: string = getAppId(user.authority);
1329
+ let clientId: string = getAppId(admin.authority, tct);
1324
1330
  url.searchParams.append("client_id", clientId);
1325
1331
  url.searchParams.append("redirect_uri", window.location.origin);
1326
- url.searchParams.append("domain_hint", user.companyDomain);
1332
+ url.searchParams.append("domain_hint", admin.companyDomain);
1333
+ url.searchParams.append("login_hint", admin.mail);
1327
1334
  window.location.assign(url.href);
1328
1335
  }
1329
1336
  export async function servicePrincipalGet(options: RequestInit, user: User, appid: string): Promise<{ spid: string, error: string }> {
@@ -1736,7 +1743,9 @@ export async function configEdit(
1736
1743
  result = await configPost(instance, authorizedUser, config, workspace.id, debug);
1737
1744
  if (result.result) {
1738
1745
  // config id was updated from "1"
1739
- setConfigId(config.id);
1746
+ if (setConfigId) {
1747
+ setConfigId(config.id);
1748
+ }
1740
1749
  // set this config as the selected config
1741
1750
  const newSelection = {};
1742
1751
  Object.defineProperty(newSelection, config.id, { value: true, writable: true, enumerable: true });
@@ -1758,6 +1767,12 @@ export async function configEdit(
1758
1767
  export async function configRemove(instance: IPublicClientApplication, authorizedUser: User, config: Config, workspaceId: string, debug: boolean): Promise<APIResult> {
1759
1768
  return configDelete(instance, authorizedUser, config, workspaceId, debug);
1760
1769
  }
1770
+ export async function configConsentForRead(instance: IPublicClientApplication, authorizedUser: User, configId: string, tid: string, consent: boolean): Promise<APIResult> {
1771
+ return configConsentReadPut(instance, authorizedUser, configId, tid, consent);
1772
+ }
1773
+ export async function configConsentForWrite(instance: IPublicClientApplication, authorizedUser: User, configId: string, tid: string, consent: boolean): Promise<APIResult> {
1774
+ return configConsentWritePut(instance, authorizedUser, configId, tid, consent);
1775
+ }
1761
1776
  export async function configsRefresh(instance: IPublicClientApplication, authorizedUser: User, workspaceId: string, ii: InitInfo, debug: boolean): Promise<APIResult> {
1762
1777
  let result: APIResult = new APIResult();
1763
1778
  if (debug) debugger;
@@ -1802,7 +1817,7 @@ export async function initGet(instance: IPublicClientApplication, authorizedUser
1802
1817
  tasks.setTaskStart("GET tenant details", new Date());
1803
1818
  result.result = await tenantRelationshipsGetById(user, ii, instance, debug);
1804
1819
  tasks.setTaskEnd("GET tenant details", new Date(), "complete");
1805
- // if this is the first time, we have just gotten tenant info, then we must POST user and not-yet-onboarded tenant to back end
1820
+ // if this is the first time, we have just gotten tenant info, then we must POST user and tenant to back end
1806
1821
  if (result.result) {
1807
1822
  tasks.setTaskStart("POST config init", new Date());
1808
1823
  result = await initPost(instance, authorizedUser, user, debug);
@@ -1832,9 +1847,6 @@ export async function initGet(instance: IPublicClientApplication, authorizedUser
1832
1847
  export async function tenantAdd(instance: IPublicClientApplication, authorizedUser: User, tenant: Tenant, workspaceId: string): Promise<APIResult> {
1833
1848
  return tenantPost(instance, authorizedUser, tenant, workspaceId);
1834
1849
  }
1835
- export async function tenantComplete(instance: IPublicClientApplication, authorizedUser: User, tenant: Tenant, debug: boolean): Promise<APIResult> {
1836
- return tenantPut(instance, authorizedUser, tenant, debug);
1837
- }
1838
1850
  export async function tenantRemove(instance: IPublicClientApplication, authorizedUser: User, tenant: Tenant, workspaceId: string, debug: boolean): Promise<APIResult> {
1839
1851
  return tenantDelete(instance, authorizedUser, tenant, workspaceId, debug);
1840
1852
  }
@@ -1849,7 +1861,7 @@ export async function workspaceEdit(instance: IPublicClientApplication, authoriz
1849
1861
  return await workspacePut(instance, authorizedUser, workspaceId, workspaceName);
1850
1862
  }
1851
1863
  // retrieve Workspace(s), User(s), Tenant(s), Config(s) given newly logged in user
1852
- function processReturnedAdmins(workspace: Workspace, ii: InitInfo, returnedAdmins: Array<Object>) {
1864
+ function processReturnedAdmins(workspace: Workspace, ii: InitInfo, returnedAdmins: Array<Object>, adminSelectedId: string) {
1853
1865
  returnedAdmins.map((item) => {
1854
1866
  // are we already tracking this user?
1855
1867
  let user: User | null = null;
@@ -1876,6 +1888,8 @@ function processReturnedAdmins(workspace: Workspace, ii: InitInfo, returnedAdmin
1876
1888
  // already tracking this user
1877
1889
  user = ii.us.at(usIndex);
1878
1890
  }
1891
+ // restore selection
1892
+ user.sel = (adminSelectedId === item.userId);
1879
1893
  // refresh all the data available from the server
1880
1894
  user.oid = item.userId ? item.userId : item.email;
1881
1895
  user.name = item.firstName ?? user.name;
@@ -1900,7 +1914,7 @@ function processReturnedAdmins(workspace: Workspace, ii: InitInfo, returnedAdmin
1900
1914
  });
1901
1915
  ii.save();
1902
1916
  }
1903
- function processReturnedTenants(workspace: Workspace, ii: InitInfo, returnedTenants: Array<Object>) {
1917
+ function processReturnedTenants(workspace: Workspace, ii: InitInfo, returnedTenants: Array<Object>, tenantSelectedId: string) {
1904
1918
  returnedTenants.map((item) => {
1905
1919
  // are we already tracking this tenant?
1906
1920
  let tenant: Tenant | null = null;
@@ -1926,12 +1940,13 @@ function processReturnedTenants(workspace: Workspace, ii: InitInfo, returnedTena
1926
1940
  // already tracking this tenant
1927
1941
  tenant = ii.ts.at(tsIndex);
1928
1942
  }
1943
+ // restore selection
1944
+ tenant.sel = (tenantSelectedId === item.tenantId);
1945
+ // refresh all the data available from the server
1929
1946
  tenant.tid = item.tenantId;
1930
1947
  tenant.name = item.name;
1931
1948
  tenant.domain = item.domain;
1932
1949
  tenant.tenantType = item.type.toLowerCase(); // should now be strings
1933
- tenant.permissionType = item.permissionType.toLowerCase(); // should now be strings
1934
- tenant.onboarded = item.isOnboarded ? "true" : "false";
1935
1950
 
1936
1951
  // canonicalize authority when getting it from config backend
1937
1952
  const regex = /^(https:\/\/login.microsoftonline.(?:us|com)\/)organizations\/v2.0$/;
@@ -1944,7 +1959,7 @@ function processReturnedTenants(workspace: Workspace, ii: InitInfo, returnedTena
1944
1959
  });
1945
1960
  ii.save();
1946
1961
  }
1947
- function processReturnedConfigs(workspace: Workspace, ii: InitInfo, returnedConfigs: Array<Object>) {
1962
+ function processReturnedConfigs(workspace: Workspace, ii: InitInfo, returnedConfigs: Array<Object>, configSelectedId: string) {
1948
1963
  // process returned configs
1949
1964
  returnedConfigs.map((item) => {
1950
1965
  // are we already tracking this config?
@@ -1971,6 +1986,9 @@ function processReturnedConfigs(workspace: Workspace, ii: InitInfo, returnedConf
1971
1986
  // already tracking this config
1972
1987
  config = ii.cs.at(csIndex);
1973
1988
  }
1989
+ // restore selection
1990
+ config.sel = (configSelectedId === item.id);
1991
+ // refresh all the data available from the server
1974
1992
  config!.id = item.id;
1975
1993
  config!.workspaceId = item.workspaceId;
1976
1994
  config!.name = item.name;
@@ -1989,6 +2007,8 @@ function processReturnedConfigs(workspace: Workspace, ii: InitInfo, returnedConf
1989
2007
  tenantConfigInfo.deltaToken = tci.deltaToken ?? "";
1990
2008
  tenantConfigInfo.configId = config!.id;
1991
2009
  tenantConfigInfo.batchId = tci.batchId ?? "";
2010
+ tenantConfigInfo.isReadPermissionConsented = tci.isReadPermissionConsented;
2011
+ tenantConfigInfo.isWritePermissionConsented = tci.isWritePermissionConsented;
1992
2012
  config!.tenants.push(tenantConfigInfo);
1993
2013
  });
1994
2014
  // ensure this workspace tracks this config
@@ -2023,11 +2043,32 @@ async function workspaceInfoGet(instance: IPublicClientApplication, authorizedUs
2023
2043
  // already tracking this workspace
2024
2044
  workspace = ii.ws.at(wsIndex);
2025
2045
  }
2046
+ // preserve selected admin, tenant, and config
2047
+ let adminSelectedId: string = "";
2048
+ for (let oid of workspace.associatedUsers) {
2049
+ let user = ii.us.find((u: User) => u.oid === oid);
2050
+ if (user != null && user.sel) {
2051
+ adminSelectedId = user.oid;
2052
+ }
2053
+ }
2054
+ let tenantSelectedId: string = "";
2055
+ for (let tid of workspace.associatedTenants) {
2056
+ let tenant = ii.ts.find((t: Tenant) => t.tid === tid);
2057
+ if (tenant != null && tenant.sel) {
2058
+ tenantSelectedId = tenant.tid;
2059
+ }
2060
+ }
2061
+ let configSelectedId: string = "";
2062
+ for (let cid of workspace.associatedConfigs) {
2063
+ let config = ii.cs.find((c: Config) => c.id === cid);
2064
+ if (config != null && config.sel) {
2065
+ configSelectedId = config.id;
2066
+ }
2067
+ }
2026
2068
  // clear associations as we are about to reset
2027
- // IN PROGRESS: do not clear so we can retain selection state
2028
- //workspace.associatedUsers.length = 0;
2029
- //workspace.associatedTenants.length = 0;
2030
- //workspace.associatedConfigs.length = 0;
2069
+ workspace.associatedUsers.length = 0;
2070
+ workspace.associatedTenants.length = 0;
2071
+ workspace.associatedConfigs.length = 0;
2031
2072
  // set id and name based on returned data
2032
2073
  workspace.id = o.id;
2033
2074
  workspace.name = o.name;
@@ -2042,9 +2083,9 @@ async function workspaceInfoGet(instance: IPublicClientApplication, authorizedUs
2042
2083
  if (!tenantsResult.result) return tenantsResult;
2043
2084
  if (!configsResult.result) return configsResult;
2044
2085
  // process returned workspace components (tenants first as they have the authorities that admins depend on)
2045
- processReturnedTenants(workspace, ii, tenantsResult.array!);
2046
- processReturnedAdmins(workspace, ii, adminsResult.array!);
2047
- processReturnedConfigs(workspace, ii, configsResult.array!);
2086
+ processReturnedTenants(workspace, ii, tenantsResult.array!, tenantSelectedId);
2087
+ processReturnedAdmins(workspace, ii, adminsResult.array!, adminSelectedId);
2088
+ processReturnedConfigs(workspace, ii, configsResult.array!, configSelectedId);
2048
2089
  // tag components with workspaceIDs
2049
2090
  ii.tagWithWorkspaces();
2050
2091
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mindline/sync",
3
3
  "type": "module",
4
- "version": "1.0.68",
4
+ "version": "1.0.70",
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.",
package/tasks.ts CHANGED
@@ -49,46 +49,6 @@ const data: any[] = [
49
49
  status: "not started"
50
50
  }
51
51
  ]
52
- },
53
- {
54
- id: 8,
55
- task: "onboard tenant",
56
- start: "1970-01-01T00:00:00",
57
- end: "1970-01-01T00:00:00",
58
- expected: "0:05",
59
- status: "not started"
60
- },
61
- {
62
- id: 9,
63
- task: "create 2nd tenant",
64
- start: "1970-01-01T00:00:00",
65
- end: "1970-01-01T00:00:00",
66
- expected: "0:01",
67
- status: "not started"
68
- },
69
- {
70
- id: 10,
71
- task: "invite 2nd admin",
72
- start: "1970-01-01T00:00:00",
73
- end: "1970-01-01T00:00:00",
74
- expected: "0:01",
75
- status: "not started"
76
- },
77
- {
78
- id: 11,
79
- task: "onboard 2nd tenant",
80
- start: "1970-01-01T00:00:00",
81
- end: "1970-01-01T00:00:00",
82
- expected: "0:05",
83
- status: "not started"
84
- },
85
- {
86
- id: 12,
87
- task: "create config",
88
- start: "1970-01-01T00:00:00",
89
- end: "1970-01-01T00:00:00",
90
- expected: "0:01",
91
- status: "not started"
92
52
  }
93
53
  ];
94
54
 
package/tenants.json CHANGED
@@ -4,8 +4,6 @@
4
4
  "name": "",
5
5
  "domain": "",
6
6
  "tenantType": "",
7
- "permissionType": "",
8
- "onboarded": false,
9
7
  "authority": "",
10
8
  "sel": true,
11
9
  "graphSP": ""