@mindline/sync 1.0.48 → 1.0.50
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/.vs/VSWorkspaceState.json +1 -1
- package/.vs/slnx.sqlite +0 -0
- package/.vs/sync/FileContentIndex/9c1a97af-ffa0-4355-ac1b-48c0cc3d9397.vsidx +0 -0
- package/.vs/sync/v17/.wsuo +0 -0
- package/hybridspa.ts +43 -73
- package/index.d.ts +32 -1
- package/index.ts +99 -13
- package/package.json +1 -1
- package/tasks.ts +2 -10
- package/.vs/sync/FileContentIndex/3b6f730a-80e1-4630-9c4f-343c5427a3ed.vsidx +0 -0
package/.vs/slnx.sqlite
CHANGED
|
Binary file
|
package/.vs/sync/v17/.wsuo
CHANGED
|
Binary file
|
package/hybridspa.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
//hybridspa.ts - calls to Mindline Config API
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
Tenant,
|
|
3
|
+
APIResult,
|
|
5
4
|
Config,
|
|
5
|
+
graphConfig,
|
|
6
|
+
Tenant,
|
|
6
7
|
TenantConfigInfo,
|
|
7
|
-
|
|
8
|
+
User
|
|
8
9
|
} from "./index";
|
|
9
10
|
import {
|
|
10
11
|
IPublicClientApplication,
|
|
@@ -12,44 +13,6 @@ import {
|
|
|
12
13
|
} from "@azure/msal-browser";
|
|
13
14
|
import { deserializeArray } from "class-transformer";
|
|
14
15
|
|
|
15
|
-
// add here endpoints for API services you would like to use.
|
|
16
|
-
export const graphConfig = {
|
|
17
|
-
// config API endpoints
|
|
18
|
-
adminEndpoint:
|
|
19
|
-
"https://dev-configurationapi-westus.azurewebsites.net/api/v1/admin",
|
|
20
|
-
adminIncompleteEndpoint:
|
|
21
|
-
"https://dev-configurationapi-westus.azurewebsites.net/api/v1/incomplete-admin",
|
|
22
|
-
adminsEndpoint:
|
|
23
|
-
"https://dev-configurationapi-westus.azurewebsites.net/api/v1/admins",
|
|
24
|
-
configEndpoint:
|
|
25
|
-
"https://dev-configurationapi-westus.azurewebsites.net/api/v1/configuration",
|
|
26
|
-
configsEndpoint:
|
|
27
|
-
"https://dev-configurationapi-westus.azurewebsites.net/api/v1/configurations",
|
|
28
|
-
initEndpoint:
|
|
29
|
-
"https://dev-configurationapi-westus.azurewebsites.net/api/v1/configuration/init",
|
|
30
|
-
readerStartSyncEndpoint: "https://dev-configurationapi-westus.azurewebsites.net/api/v1/startSync",
|
|
31
|
-
tenantEndpoint:
|
|
32
|
-
"https://dev-configurationapi-westus.azurewebsites.net/api/v1/tenant",
|
|
33
|
-
tenantsEndpoint:
|
|
34
|
-
"https://dev-configurationapi-westus.azurewebsites.net/api/v1/tenants",
|
|
35
|
-
workspaceEndpoint:
|
|
36
|
-
"https://dev-configurationapi-westus.azurewebsites.net/api/v1/workspaces",
|
|
37
|
-
// graph API endpoints
|
|
38
|
-
graphGroupsEndpoint: "https://graph.microsoft.com/v1.0/groups",
|
|
39
|
-
graphMailEndpoint: "https://graph.microsoft.com/v1.0/me/messages",
|
|
40
|
-
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me",
|
|
41
|
-
graphUsersEndpoint: "https://graph.microsoft.com/v1.0/users",
|
|
42
|
-
// sovereign cloud tenant info endpoints
|
|
43
|
-
graphTenantByDomainPredicate: "beta/tenantRelationships/findTenantInformationByDomainName",
|
|
44
|
-
graphTenantByIdPredicate: "beta/tenantRelationships/findTenantInformationByTenantId",
|
|
45
|
-
// authority values are based on the well-known OIDC auth endpoints
|
|
46
|
-
authorityWW: "https://login.microsoftonline.com/",
|
|
47
|
-
authorityWWRegex: /^(https:\/\/login\.microsoftonline\.(?:us|com)\/)([\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{12})\/oauth2\/authorize$/,
|
|
48
|
-
authorityUS: "https://login.microsoftonline.us/",
|
|
49
|
-
authorityUSRegex: /^(https:\/\/login\.microsoftonline\.(?:us|com)\/)([\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{12})\/oauth2\/authorize$/,
|
|
50
|
-
authorityCN: "https://login.partner.microsoftonline.cn/",
|
|
51
|
-
authorityCNRegex: /^(https:\/\/login\.partner\.microsoftonline\.cn\/)([\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{12})\/oauth2\/authorize$/,
|
|
52
|
-
};
|
|
53
16
|
// helper functions
|
|
54
17
|
// TODO: this is where you want to trigger a re-authentication if token expires
|
|
55
18
|
export async function defineHeaders(
|
|
@@ -117,12 +80,12 @@ export async function adminDelete(
|
|
|
117
80
|
// are we performing deletion of a full admin?
|
|
118
81
|
let url: URL | null = null;
|
|
119
82
|
if (user.oid !== user.mail) {
|
|
120
|
-
url = new URL(graphConfig.adminEndpoint);
|
|
83
|
+
url = new URL(graphConfig.adminEndpoint());
|
|
121
84
|
url.searchParams.append("workspaceId", workspaceId);
|
|
122
85
|
}
|
|
123
86
|
// or of an incomplete admin?
|
|
124
87
|
else if (user.mail !== "") {
|
|
125
|
-
url = new URL(graphConfig.adminIncompleteEndpoint);
|
|
88
|
+
url = new URL(graphConfig.adminIncompleteEndpoint());
|
|
126
89
|
url.searchParams.append("email", user.mail);
|
|
127
90
|
url.searchParams.append("workspaceId", workspaceId);
|
|
128
91
|
}
|
|
@@ -168,9 +131,9 @@ export async function adminsGet(
|
|
|
168
131
|
return result;
|
|
169
132
|
}
|
|
170
133
|
// create endpoint
|
|
171
|
-
let
|
|
134
|
+
let endpoint: string = graphConfig.adminsEndpoint();
|
|
172
135
|
// add parameter to endpoint
|
|
173
|
-
let url: URL = new URL(
|
|
136
|
+
let url: URL = new URL(endpoint);
|
|
174
137
|
url.searchParams.append("workspaceId", workspaceID);
|
|
175
138
|
// create headers
|
|
176
139
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
@@ -228,7 +191,7 @@ export async function adminPost(
|
|
|
228
191
|
return result;
|
|
229
192
|
}
|
|
230
193
|
// create admin endpoint
|
|
231
|
-
let
|
|
194
|
+
let endpoint: string = graphConfig.adminEndpoint();
|
|
232
195
|
// create headers
|
|
233
196
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
234
197
|
// create admin body
|
|
@@ -240,8 +203,8 @@ export async function adminPost(
|
|
|
240
203
|
let options = { method: "POST", headers: headers, body: adminBody };
|
|
241
204
|
// make admin endpoint call
|
|
242
205
|
try {
|
|
243
|
-
console.log("Attempting POST to /admin: " +
|
|
244
|
-
let response = await fetch(
|
|
206
|
+
console.log("Attempting POST to /admin: " + endpoint);
|
|
207
|
+
let response = await fetch(endpoint, options);
|
|
245
208
|
if (response.status === 200 && response.statusText === "OK") {
|
|
246
209
|
console.log(`Successful POST to /admin: ${adminBody}`);
|
|
247
210
|
return result;
|
|
@@ -277,7 +240,7 @@ export async function configDelete(
|
|
|
277
240
|
return result;
|
|
278
241
|
}
|
|
279
242
|
let url: URL | null = null;
|
|
280
|
-
url = new URL(graphConfig.configEndpoint);
|
|
243
|
+
url = new URL(graphConfig.configEndpoint());
|
|
281
244
|
url.searchParams.append("configurationId", config.id);
|
|
282
245
|
// create headers
|
|
283
246
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
@@ -323,7 +286,7 @@ export async function configPost(
|
|
|
323
286
|
return result;
|
|
324
287
|
}
|
|
325
288
|
// create no parameter config endpoint
|
|
326
|
-
let
|
|
289
|
+
let endpoint: string = graphConfig.configEndpoint();
|
|
327
290
|
// create config headers
|
|
328
291
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
329
292
|
// create config body
|
|
@@ -357,8 +320,8 @@ export async function configPost(
|
|
|
357
320
|
// make config endpoint call
|
|
358
321
|
try {
|
|
359
322
|
if (debug) debugger;
|
|
360
|
-
console.log("Attempting POST to /config: " +
|
|
361
|
-
let response = await fetch(
|
|
323
|
+
console.log("Attempting POST to /config: " + endpoint);
|
|
324
|
+
let response = await fetch(endpoint, options);
|
|
362
325
|
if (response.status === 200 && response.statusText === "OK") {
|
|
363
326
|
let data = await response.json();
|
|
364
327
|
config.id = data;
|
|
@@ -399,8 +362,8 @@ export async function configPut(
|
|
|
399
362
|
return result;
|
|
400
363
|
}
|
|
401
364
|
// create parametrized config endpoint
|
|
402
|
-
let
|
|
403
|
-
let url: URL = new URL(
|
|
365
|
+
let endpoint: string = graphConfig.configEndpoint();
|
|
366
|
+
let url: URL = new URL(endpoint);
|
|
404
367
|
url.searchParams.append("configurationId", config.id);
|
|
405
368
|
// create config headers
|
|
406
369
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
@@ -474,9 +437,9 @@ export async function configsGet(
|
|
|
474
437
|
return result;
|
|
475
438
|
}
|
|
476
439
|
// create endpoint
|
|
477
|
-
let
|
|
440
|
+
let endpoint: string = graphConfig.configsEndpoint();
|
|
478
441
|
// add parameter to endpoint
|
|
479
|
-
let url: URL = new URL(
|
|
442
|
+
let url: URL = new URL(endpoint);
|
|
480
443
|
url.searchParams.append("workspaceId", workspaceID);
|
|
481
444
|
// create headers
|
|
482
445
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
@@ -535,7 +498,7 @@ export async function initPost(
|
|
|
535
498
|
return result;
|
|
536
499
|
}
|
|
537
500
|
// create init endpoint
|
|
538
|
-
let
|
|
501
|
+
let endpoint: string = graphConfig.initEndpoint();
|
|
539
502
|
// create init headers
|
|
540
503
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
541
504
|
// create init body
|
|
@@ -553,8 +516,8 @@ export async function initPost(
|
|
|
553
516
|
// make init endpoint call
|
|
554
517
|
try {
|
|
555
518
|
if (debug) debugger;
|
|
556
|
-
console.log("Attempting POST to /configuration/init: " +
|
|
557
|
-
let response = await fetch(
|
|
519
|
+
console.log("Attempting POST to /configuration/init: " + endpoint);
|
|
520
|
+
let response = await fetch(endpoint, options);
|
|
558
521
|
if (response.status === 200 && response.statusText === "OK") {
|
|
559
522
|
console.log(`Successful POST to /configuration/init: ${initBody}`);
|
|
560
523
|
return result;
|
|
@@ -593,7 +556,7 @@ export async function tenantDelete(
|
|
|
593
556
|
return result;
|
|
594
557
|
}
|
|
595
558
|
// create parametrized tenant endpoint
|
|
596
|
-
let url: URL = new URL(graphConfig.tenantEndpoint);
|
|
559
|
+
let url: URL = new URL(graphConfig.tenantEndpoint());
|
|
597
560
|
url.searchParams.append("tenantId", tenant.tid);
|
|
598
561
|
url.searchParams.append("workspaceId", workspaceId);
|
|
599
562
|
// create headers
|
|
@@ -640,9 +603,9 @@ export async function tenantsGet(
|
|
|
640
603
|
return result;
|
|
641
604
|
}
|
|
642
605
|
// create endpoint
|
|
643
|
-
let
|
|
606
|
+
let endpoint: string = graphConfig.tenantsEndpoint();
|
|
644
607
|
// add parameter to endpoint
|
|
645
|
-
let url: URL = new URL(
|
|
608
|
+
let url: URL = new URL(endpoint);
|
|
646
609
|
url.searchParams.append("workspaceId", workspaceID);
|
|
647
610
|
// create headers
|
|
648
611
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
@@ -701,8 +664,8 @@ export async function tenantPost(
|
|
|
701
664
|
return result;
|
|
702
665
|
}
|
|
703
666
|
// create parametrized tenant endpoint
|
|
704
|
-
let
|
|
705
|
-
let url: URL = new URL(
|
|
667
|
+
let endpoint: string = graphConfig.tenantEndpoint();
|
|
668
|
+
let url: URL = new URL(endpoint);
|
|
706
669
|
url.searchParams.append("workspaceId", workspaceId);
|
|
707
670
|
// create tenant headers
|
|
708
671
|
const headers = await defineHeaders(instance, addingUser);
|
|
@@ -756,7 +719,7 @@ export async function tenantPut(
|
|
|
756
719
|
return result;
|
|
757
720
|
}
|
|
758
721
|
// create tenant endpoint
|
|
759
|
-
let
|
|
722
|
+
let endpoint: string = graphConfig.tenantEndpoint();
|
|
760
723
|
// create tenant headers
|
|
761
724
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
762
725
|
// establish read and write service principals ("notassigned" is default")
|
|
@@ -782,14 +745,14 @@ export async function tenantPut(
|
|
|
782
745
|
// make tenant endpoint call
|
|
783
746
|
try {
|
|
784
747
|
if (debug) debugger;
|
|
785
|
-
console.log(`Attempting PUT to ${
|
|
786
|
-
let response = await fetch(
|
|
748
|
+
console.log(`Attempting PUT to ${endpoint}`);
|
|
749
|
+
let response = await fetch(endpoint, options);
|
|
787
750
|
if (response.status === 200 && response.statusText === "OK") {
|
|
788
|
-
console.log(`Successful PUT to ${
|
|
751
|
+
console.log(`Successful PUT to ${endpoint}`);
|
|
789
752
|
return result;
|
|
790
753
|
}
|
|
791
754
|
else {
|
|
792
|
-
console.log(`Failed PUT to ${
|
|
755
|
+
console.log(`Failed PUT to ${endpoint}: ${tenantBody}`);
|
|
793
756
|
result.error = await processErrors(response);
|
|
794
757
|
console.log(result.error);
|
|
795
758
|
result.status = 500;
|
|
@@ -821,9 +784,9 @@ export async function workspacesGet(
|
|
|
821
784
|
return result;
|
|
822
785
|
}
|
|
823
786
|
// create workspaces endpoint
|
|
824
|
-
let
|
|
787
|
+
let endpoint: string = graphConfig.workspaceEndpoint();
|
|
825
788
|
// create workspace endpoint
|
|
826
|
-
let url: URL = new URL(
|
|
789
|
+
let url: URL = new URL(endpoint);
|
|
827
790
|
// create workspace headers
|
|
828
791
|
const headers = await defineHeaders(instance, authorizedUser);
|
|
829
792
|
// make workspace endpoint call
|
|
@@ -880,7 +843,7 @@ export async function readerPost(
|
|
|
880
843
|
return result;
|
|
881
844
|
}
|
|
882
845
|
// create reader endpoint
|
|
883
|
-
let readerEndpoint: string = graphConfig.readerStartSyncEndpoint;
|
|
846
|
+
let readerEndpoint: string = graphConfig.readerStartSyncEndpoint();
|
|
884
847
|
let url: URL = new URL(readerEndpoint);
|
|
885
848
|
url.searchParams.append("configurationId", config.id);
|
|
886
849
|
// create headers
|
|
@@ -893,7 +856,14 @@ export async function readerPost(
|
|
|
893
856
|
if (response.status === 200 && response.statusText === "OK") {
|
|
894
857
|
console.log(`Successful POST to /startSync: ${readerEndpoint}`);
|
|
895
858
|
let jsonResponse = await response.json();
|
|
896
|
-
|
|
859
|
+
if (jsonResponse.PayloadStr != "") {
|
|
860
|
+
result.array = JSON.parse(jsonResponse.PayloadStr);
|
|
861
|
+
}
|
|
862
|
+
else {
|
|
863
|
+
result.result = false;
|
|
864
|
+
result.error = "readerPost: blank payload returned, sync may be disabled on back end";
|
|
865
|
+
result.status = 500;
|
|
866
|
+
}
|
|
897
867
|
return result;
|
|
898
868
|
} else {
|
|
899
869
|
result.error = await processErrors(response);
|
package/index.d.ts
CHANGED
|
@@ -3,6 +3,37 @@ declare module "@mindline/sync" {
|
|
|
3
3
|
export function sum(a: number, b: number): number;
|
|
4
4
|
export function helloNpm(): string;
|
|
5
5
|
|
|
6
|
+
export class graphConfig {
|
|
7
|
+
static environmentTag: string = "dev";
|
|
8
|
+
// config API endpoints
|
|
9
|
+
static adminEndpoint(): string;
|
|
10
|
+
static adminIncompleteEndpoint(): string;
|
|
11
|
+
static adminsEndpoint(): string;
|
|
12
|
+
static configEndpoint(): string;
|
|
13
|
+
static configsEndpoint(): string;
|
|
14
|
+
static initEndpoint(): string;
|
|
15
|
+
static readerStartSyncEndpoint(): string;
|
|
16
|
+
static tenantEndpoint(): string;
|
|
17
|
+
static tenantsEndpoint(): string;
|
|
18
|
+
static workspaceEndpoint(): string;
|
|
19
|
+
// SignalR endpoint
|
|
20
|
+
static signalREndpoint(): string;
|
|
21
|
+
// graph API endpoints
|
|
22
|
+
static graphGroupsEndpoint: string = "https://graph.microsoft.com/v1.0/groups";
|
|
23
|
+
static graphMailEndpoint: string = "https://graph.microsoft.com/v1.0/me/messages";
|
|
24
|
+
static graphMeEndpoint: string = "https://graph.microsoft.com/v1.0/me";
|
|
25
|
+
static graphUsersEndpoint: string = "https://graph.microsoft.com/v1.0/users";
|
|
26
|
+
// sovereign cloud tenant info endpoints
|
|
27
|
+
static graphTenantByDomainPredicate: string = "beta/tenantRelationships/findTenantInformationByDomainName";
|
|
28
|
+
static graphTenantByIdPredicate: string = "beta/tenantRelationships/findTenantInformationByTenantId";
|
|
29
|
+
// authority values are based on the well-known OIDC auth endpoints
|
|
30
|
+
static authorityWW: string = "https://login.microsoftonline.com/";
|
|
31
|
+
static authorityWWRegex: RegExp = /^(https:\/\/login\.microsoftonline\.(?:us|com)\/)([\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{12})\/oauth2\/authorize$/;
|
|
32
|
+
static authorityUS: string = "https://login.microsoftonline.us/";
|
|
33
|
+
static authorityUSRegex: RegExp = /^(https:\/\/login\.microsoftonline\.(?:us|com)\/)([\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{12})\/oauth2\/authorize$/;
|
|
34
|
+
static authorityCN: string = "https://login.partner.microsoftonline.cn/";
|
|
35
|
+
static authorityCNRegex: RegExp = /^(https:\/\/login\.partner\.microsoftonline\.cn\/)([\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{12})\/oauth2\/authorize$/;
|
|
36
|
+
};
|
|
6
37
|
export class Group {
|
|
7
38
|
id: string;
|
|
8
39
|
displayName: string;
|
|
@@ -116,7 +147,6 @@ declare module "@mindline/sync" {
|
|
|
116
147
|
export type TaskType = "initialization" |
|
|
117
148
|
"authenticate user" |
|
|
118
149
|
"reload React" |
|
|
119
|
-
"PUT tenant" |
|
|
120
150
|
"GET tenant details" |
|
|
121
151
|
"POST config init" |
|
|
122
152
|
"GET workspaces" |
|
|
@@ -233,6 +263,7 @@ declare module "@mindline/sync" {
|
|
|
233
263
|
export function groupsGet(instance: IPublicClientApplication, user: User | undefined, groupSearchString: string): Promise<{groups: Group[], error: string}>;
|
|
234
264
|
export function signIn(user: User, tasks: TaskArray): void;
|
|
235
265
|
export function signInIncrementally(user: User, scope: string): void;
|
|
266
|
+
export function requestAdminConsent(user: User, scope: string): void;
|
|
236
267
|
export function signOut(user: User): boolean;
|
|
237
268
|
export function tenantRelationshipsGetByDomain(loggedInuser: User, tenant: Tenant, instance: IPublicClientApplication, debug: boolean): boolean;
|
|
238
269
|
export function tenantRelationshipsGetById(user: User, ii: InitInfo, instance: IPublicClientApplication, tasks: TaskArray, debug: boolean): boolean;
|
package/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
//index.ts - published interface - AAD implementations, facade to Mindline Config API
|
|
2
2
|
import * as signalR from "@microsoft/signalr"
|
|
3
3
|
import { IPublicClientApplication, AuthenticationResult } from "@azure/msal-browser"
|
|
4
|
-
import { deserializeArray
|
|
5
|
-
import { defineHeaders, adminDelete, adminPost, adminsGet, configDelete, configsGet, configPost, configPut,
|
|
4
|
+
import { deserializeArray } from 'class-transformer';
|
|
5
|
+
import { defineHeaders, adminDelete, adminPost, adminsGet, configDelete, configsGet, configPost, configPut, initPost, readerPost, tenantPut, tenantPost, tenantDelete, tenantsGet, workspacesGet } from './hybridspa';
|
|
6
6
|
import { version } from './package.json';
|
|
7
7
|
import users from "./users.json";
|
|
8
8
|
import tenants from "./tenants.json";
|
|
@@ -19,6 +19,60 @@ export function sum(a: number, b: number): number {
|
|
|
19
19
|
export function helloNpm(): string {
|
|
20
20
|
return "hello NPM";
|
|
21
21
|
}
|
|
22
|
+
// main application exports
|
|
23
|
+
export class graphConfig {
|
|
24
|
+
static environmentTag: string = "dev";
|
|
25
|
+
// config API endpoints
|
|
26
|
+
static adminEndpoint(): string {
|
|
27
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/admin`
|
|
28
|
+
};
|
|
29
|
+
static adminIncompleteEndpoint(): string {
|
|
30
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/incomplete-admin`;
|
|
31
|
+
};
|
|
32
|
+
static adminsEndpoint(): string {
|
|
33
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/admins`;
|
|
34
|
+
};
|
|
35
|
+
static configEndpoint(): string {
|
|
36
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/configuration`;
|
|
37
|
+
};
|
|
38
|
+
static configsEndpoint(): string {
|
|
39
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/configurations`;
|
|
40
|
+
};
|
|
41
|
+
static initEndpoint(): string {
|
|
42
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/configuration/init`;
|
|
43
|
+
};
|
|
44
|
+
static readerStartSyncEndpoint(): string {
|
|
45
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/startSync`;
|
|
46
|
+
};
|
|
47
|
+
static tenantEndpoint(): string {
|
|
48
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/tenant`;
|
|
49
|
+
};
|
|
50
|
+
static tenantsEndpoint(): string {
|
|
51
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/tenants`;
|
|
52
|
+
};
|
|
53
|
+
static workspaceEndpoint(): string {
|
|
54
|
+
return `https://${graphConfig.environmentTag}-configurationapi-westus.azurewebsites.net/api/v1/workspaces`;
|
|
55
|
+
};
|
|
56
|
+
// SignalR endpoint
|
|
57
|
+
static signalREndpoint(): string {
|
|
58
|
+
return `https://${graphConfig.environmentTag}-signalrdispatcher-westus.azurewebsites.net/statsHub`;
|
|
59
|
+
};
|
|
60
|
+
// graph API endpoints
|
|
61
|
+
static graphGroupsEndpoint: string = "https://graph.microsoft.com/v1.0/groups";
|
|
62
|
+
static graphMailEndpoint: string = "https://graph.microsoft.com/v1.0/me/messages";
|
|
63
|
+
static graphMeEndpoint: string = "https://graph.microsoft.com/v1.0/me";
|
|
64
|
+
static graphUsersEndpoint: string = "https://graph.microsoft.com/v1.0/users";
|
|
65
|
+
// sovereign cloud tenant info endpoints
|
|
66
|
+
static graphTenantByDomainPredicate: string = "beta/tenantRelationships/findTenantInformationByDomainName";
|
|
67
|
+
static graphTenantByIdPredicate: string = "beta/tenantRelationships/findTenantInformationByTenantId";
|
|
68
|
+
// authority values are based on the well-known OIDC auth endpoints
|
|
69
|
+
static authorityWW: string = "https://login.microsoftonline.com/";
|
|
70
|
+
static authorityWWRegex: RegExp = /^(https:\/\/login\.microsoftonline\.(?:us|com)\/)([\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{12})\/oauth2\/authorize$/;
|
|
71
|
+
static authorityUS: string = "https://login.microsoftonline.us/";
|
|
72
|
+
static authorityUSRegex: RegExp = /^(https:\/\/login\.microsoftonline\.(?:us|com)\/)([\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{12})\/oauth2\/authorize$/;
|
|
73
|
+
static authorityCN: string = "https://login.partner.microsoftonline.cn/";
|
|
74
|
+
static authorityCNRegex: RegExp = /^(https:\/\/login\.partner\.microsoftonline\.cn\/)([\dA-Fa-f]{8}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{4}-[\dA-Fa-f]{12})\/oauth2\/authorize$/;
|
|
75
|
+
};
|
|
22
76
|
export class Group {
|
|
23
77
|
id: string;
|
|
24
78
|
displayName: string;
|
|
@@ -391,7 +445,6 @@ export class InitInfo {
|
|
|
391
445
|
export type TaskType = "initialization" |
|
|
392
446
|
"authenticate user" |
|
|
393
447
|
"reload React" |
|
|
394
|
-
"PUT tenant" |
|
|
395
448
|
"GET tenant details" |
|
|
396
449
|
"POST config init" |
|
|
397
450
|
"GET workspaces" |
|
|
@@ -819,10 +872,10 @@ export class BatchArray {
|
|
|
819
872
|
initializeProgressBar(setSyncProgress: (progress: number) => void, setConfigSyncResult: (result: string) => void, setIdleText: (idleText: string) => void, setMilestones: (milestones: Milestone[]) => void): void {
|
|
820
873
|
this.pb_startTS = Date.now();
|
|
821
874
|
this.pb_progress = 0;
|
|
822
|
-
this.pb_increment =
|
|
875
|
+
this.pb_increment = .25;
|
|
823
876
|
this.pb_idle = 0;
|
|
824
877
|
this.pb_idleMax = 0;
|
|
825
|
-
setIdleText(`No updates seen for ${this.pb_idle} seconds. [max idle: ${this.pb_idleMax}]`);
|
|
878
|
+
setIdleText(`No updates seen for ${this.pb_idle} seconds. [max idle: ${this.pb_idleMax}/60]`);
|
|
826
879
|
this.pb_timer = setInterval(() => {
|
|
827
880
|
// if signalR has finished the sync, stop the timer
|
|
828
881
|
if (this.milestoneArray.milestones[0].Write != null) {
|
|
@@ -833,10 +886,10 @@ export class BatchArray {
|
|
|
833
886
|
setIdleText(`Complete. [max idle: ${this.pb_idleMax}]`);
|
|
834
887
|
}
|
|
835
888
|
else {
|
|
836
|
-
// if we've gone
|
|
889
|
+
// if we've gone 60 seconds without a signalR message, finish the sync
|
|
837
890
|
this.pb_idle = this.pb_idle + 1;
|
|
838
891
|
this.pb_idleMax = Math.max(this.pb_idle, this.pb_idleMax);
|
|
839
|
-
setIdleText(`No updates seen for ${this.pb_idle} seconds. [max idle: ${this.pb_idleMax}]`);
|
|
892
|
+
setIdleText(`No updates seen for ${this.pb_idle} seconds. [max idle: ${this.pb_idleMax}/60]`);
|
|
840
893
|
if (this.pb_idle >= 60) {
|
|
841
894
|
clearInterval(this.pb_timer);
|
|
842
895
|
this.pb_timer = null;
|
|
@@ -916,11 +969,13 @@ export class BatchArray {
|
|
|
916
969
|
let bTotalCount = statskeys[j].endsWith("TotalCount");
|
|
917
970
|
let bCurrentCount = statskeys[j].endsWith("CurrentCount");
|
|
918
971
|
let bDeferredCount = statskeys[j].endsWith("DeferredCount");
|
|
972
|
+
let bRescheduledCount = statskeys[j].endsWith("RescheduledCount");
|
|
919
973
|
if (statskeys[j].startsWith("Reader")) {
|
|
920
974
|
// parse tid from Reader key
|
|
921
975
|
let tidRegexp = /Reader\/TID:(.+)\/TotalCount/;
|
|
922
976
|
if (bCurrentCount) tidRegexp = /Reader\/TID:(.+)\/CurrentCount/;
|
|
923
977
|
if (bDeferredCount) tidRegexp = /Reader\/TID:(.+)\/DeferredCount/;
|
|
978
|
+
if (bRescheduledCount) tidRegexp = /Reader\/TID:(.+)\/RescheduledCount/;
|
|
924
979
|
let matchTID = statskeys[j].match(tidRegexp);
|
|
925
980
|
if (matchTID == null) {
|
|
926
981
|
console.log(`tid not found in ${statskeys[j]}.`);
|
|
@@ -945,6 +1000,7 @@ export class BatchArray {
|
|
|
945
1000
|
let tidRegexp = /Writer\/TID:(.+)\/TotalCount/;
|
|
946
1001
|
if (bCurrentCount) tidRegexp = /Writer\/TID:(.+)\/CurrentCount/;
|
|
947
1002
|
if (bDeferredCount) tidRegexp = /Writer\/TID:(.+)\/DeferredCount/;
|
|
1003
|
+
if (bRescheduledCount) tidRegexp = /Writer\/TID:(.+)\/RescheduledCount/;
|
|
948
1004
|
let matchTID = statskeys[j].match(tidRegexp);
|
|
949
1005
|
if (matchTID == null) {
|
|
950
1006
|
console.log(`tid not found in ${statskeys[j]}.`);
|
|
@@ -968,7 +1024,7 @@ export class BatchArray {
|
|
|
968
1024
|
writerNode.written = Math.max(Number(statsvalues[j]), writerNode.written);
|
|
969
1025
|
console.log(`----- ${writerNode.name} Total Written: ${writerNode.written}`);
|
|
970
1026
|
}
|
|
971
|
-
else if (bDeferredCount) {
|
|
1027
|
+
else if (bDeferredCount || bRescheduledCount) {
|
|
972
1028
|
writerNode.deferred = Math.max(Number(statsvalues[j]), writerNode.deferred);
|
|
973
1029
|
console.log(`----- ${writerNode.name} Total Deferred: ${writerNode.deferred}`);
|
|
974
1030
|
}
|
|
@@ -1022,10 +1078,10 @@ export class BatchArray {
|
|
|
1022
1078
|
console.log(`Setting config sync result: "reading complete"`);
|
|
1023
1079
|
// trigger refresh delta tokens
|
|
1024
1080
|
setRefreshDeltaTrigger(true);
|
|
1025
|
-
// change to % per second to complete in
|
|
1081
|
+
// change to % per second to complete in 12x as long as it took to get here
|
|
1026
1082
|
let readTS = Date.now();
|
|
1027
1083
|
let secsElapsed = (readTS - this.pb_startTS) / 1000;
|
|
1028
|
-
let expectedPercentDone =
|
|
1084
|
+
let expectedPercentDone = 8.5;
|
|
1029
1085
|
let expectedPercentPerSecond = secsElapsed / expectedPercentDone;
|
|
1030
1086
|
this.pb_increment = expectedPercentPerSecond;
|
|
1031
1087
|
console.log(`Setting increment: ${this.pb_increment}% per second`);
|
|
@@ -1050,10 +1106,12 @@ export class BatchArray {
|
|
|
1050
1106
|
}
|
|
1051
1107
|
// start SignalR connection based on each batchId
|
|
1052
1108
|
batchIdArray.map((batchPair: Object) => {
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1109
|
+
const endpoint: string = graphConfig.signalREndpoint();
|
|
1110
|
+
let endpointUrl: URL = new URL(endpoint);
|
|
1111
|
+
endpointUrl.searchParams.append("statsId", batchPair.BatchId);
|
|
1112
|
+
console.log(`Creating SignalR Hub for TID: ${batchPair.SourceId} ${endpointUrl.href}`);
|
|
1055
1113
|
const connection: signalR.HubConnection = new signalR.HubConnectionBuilder()
|
|
1056
|
-
.withUrl(endpointUrl)
|
|
1114
|
+
.withUrl(endpointUrl.href)
|
|
1057
1115
|
.withAutomaticReconnect()
|
|
1058
1116
|
.configureLogging(signalR.LogLevel.Information)
|
|
1059
1117
|
.build();
|
|
@@ -1181,6 +1239,9 @@ export function signIn(user: User, tasks: TaskArray): void {
|
|
|
1181
1239
|
}
|
|
1182
1240
|
export function signInIncrementally(user: User, scope: string): void {
|
|
1183
1241
|
if (user.oid == "1") return;
|
|
1242
|
+
//
|
|
1243
|
+
// for delegated permissions, we can use the Microsoft Identity Web Account Controller Challenge method
|
|
1244
|
+
//
|
|
1184
1245
|
let tenantURL: string = window.location.href;
|
|
1185
1246
|
tenantURL += "MicrosoftIdentity/Account/Challenge";
|
|
1186
1247
|
let url: URL = new URL(tenantURL);
|
|
@@ -1190,6 +1251,31 @@ export function signInIncrementally(user: User, scope: string): void {
|
|
|
1190
1251
|
url.searchParams.append("loginHint", user.mail);
|
|
1191
1252
|
window.location.assign(url.href);
|
|
1192
1253
|
}
|
|
1254
|
+
export function requestAdminConsent(user: User, scope: string): void {
|
|
1255
|
+
if (user.oid == "1") return;
|
|
1256
|
+
//
|
|
1257
|
+
// for app permissions (app roles) we must use the /.default scope for admin consent
|
|
1258
|
+
// 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.
|
|
1259
|
+
// https://learn.microsoft.com/en-us/answers/questions/431784/how-to-grant-application-permissions-with-dynamic
|
|
1260
|
+
//
|
|
1261
|
+
// this means that, if we want to be granular about SyncReader vs. SyncWriter permissions, we must have separate Applications
|
|
1262
|
+
// for now, we request the /.default scope whenever any of the SyncReader or SyncWriter permissions are requested
|
|
1263
|
+
//
|
|
1264
|
+
// trying to use the Challenge endpoint for app permissions, setting this scope caused the call to quietly fail without error
|
|
1265
|
+
// scope = "63100afe-506e-4bb2-8ff7-d8d5ab373129/.default";
|
|
1266
|
+
//
|
|
1267
|
+
// 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
|
|
1268
|
+
// https://learn.microsoft.com/EN-US/azure/active-directory/develop/scopes-oidc#client-credentials-grant-flow-and-default
|
|
1269
|
+
// https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#request-the-permissions-from-a-directory-admin
|
|
1270
|
+
//
|
|
1271
|
+
let adminConsentURL: string = "https://login.microsoftonline.com/";
|
|
1272
|
+
adminConsentURL += user.tid;
|
|
1273
|
+
adminConsentURL += "/adminconsent";
|
|
1274
|
+
let url: URL = new URL(adminConsentURL);
|
|
1275
|
+
url.searchParams.append("client_id", "63100afe-506e-4bb2-8ff7-d8d5ab373129");
|
|
1276
|
+
url.searchParams.append("redirectUri", window.location.origin);
|
|
1277
|
+
window.location.assign(url.href);
|
|
1278
|
+
}
|
|
1193
1279
|
export async function signOut(user: User): Promise<boolean>{
|
|
1194
1280
|
if (user.oid == "1") return;
|
|
1195
1281
|
// set logout_hint in the .NET session for streamlined logout
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindline/sync",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.50",
|
|
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
|
@@ -26,14 +26,6 @@ const data: any[] = [
|
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
id: 4,
|
|
29
|
-
task: "PUT tenant",
|
|
30
|
-
start: "1970-01-01T00:00:00",
|
|
31
|
-
end: "1970-01-01T00:00:00",
|
|
32
|
-
expected: "0:01",
|
|
33
|
-
status: "not started"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
id: 5,
|
|
37
29
|
task: "GET tenant details",
|
|
38
30
|
start: "1970-01-01T00:00:00",
|
|
39
31
|
end: "1970-01-01T00:00:00",
|
|
@@ -41,7 +33,7 @@ const data: any[] = [
|
|
|
41
33
|
status: "not started"
|
|
42
34
|
},
|
|
43
35
|
{
|
|
44
|
-
id:
|
|
36
|
+
id: 5,
|
|
45
37
|
task: "POST config init",
|
|
46
38
|
start: "1970-01-01T00:00:00",
|
|
47
39
|
end: "1970-01-01T00:00:00",
|
|
@@ -49,7 +41,7 @@ const data: any[] = [
|
|
|
49
41
|
status: "not started"
|
|
50
42
|
},
|
|
51
43
|
{
|
|
52
|
-
id:
|
|
44
|
+
id: 6,
|
|
53
45
|
task: "GET workspaces",
|
|
54
46
|
start: "1970-01-01T00:00:00",
|
|
55
47
|
end: "1970-01-01T00:00:00",
|
|
Binary file
|