@mindline/sync 1.0.57 → 1.0.58
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 +0 -1
- package/.vs/slnx.sqlite +0 -0
- package/.vs/sync/FileContentIndex/e6f486c6-def7-4720-b195-014eeb787959.vsidx +0 -0
- package/.vs/sync/v17/.wsuo +0 -0
- package/index.d.ts +11 -4
- package/index.ts +182 -53
- package/package.json +1 -1
- package/tenants.json +2 -1
- package/users.json +0 -1
- package/users2.json +0 -6
- package/.vs/sync/FileContentIndex/edac37e3-1727-4501-92b8-aab08cc1220c.vsidx +0 -0
package/.vs/slnx.sqlite
CHANGED
|
Binary file
|
package/.vs/sync/v17/.wsuo
CHANGED
|
Binary file
|
package/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ declare module "@mindline/sync" {
|
|
|
30
30
|
static graphGroupsEndpoint: string;
|
|
31
31
|
static graphMailEndpoint: string;
|
|
32
32
|
static graphMeEndpoint: string;
|
|
33
|
+
static graphOauth2PermissionGrants: string;
|
|
34
|
+
static graphServicePrincipalsEndpoint: string;
|
|
33
35
|
static graphUsersEndpoint: string;
|
|
34
36
|
// sovereign cloud tenant info endpoints
|
|
35
37
|
static graphTenantByDomainPredicate: string;
|
|
@@ -52,6 +54,7 @@ declare module "@mindline/sync" {
|
|
|
52
54
|
group: string;
|
|
53
55
|
value: string;
|
|
54
56
|
consented: boolean;
|
|
57
|
+
removable: boolean;
|
|
55
58
|
expanded: string;
|
|
56
59
|
static compareByValue(a: UserScope, b: UserScope): number;
|
|
57
60
|
static compareByGroup(a: UserScope, b: UserScope): number;
|
|
@@ -64,7 +67,6 @@ declare module "@mindline/sync" {
|
|
|
64
67
|
tid: string; // from AAD ID token
|
|
65
68
|
companyName: string; // findTenantInformationByTenantId TODO: process changes to company name
|
|
66
69
|
companyDomain: string; // findTenantInformationByTenantId TODO: process changes to company name
|
|
67
|
-
associatedWorkspaces: string[];
|
|
68
70
|
workspaceIDs: string;
|
|
69
71
|
session: string;
|
|
70
72
|
spacode: string;
|
|
@@ -99,6 +101,7 @@ declare module "@mindline/sync" {
|
|
|
99
101
|
authority: string; // from AAD ID auth response
|
|
100
102
|
workspaceIDs: string;
|
|
101
103
|
sel: boolean; // selection state
|
|
104
|
+
graphSP: string; // graph resource ID (service principal) for this tenant
|
|
102
105
|
constructor();
|
|
103
106
|
}
|
|
104
107
|
// config
|
|
@@ -263,14 +266,18 @@ declare module "@mindline/sync" {
|
|
|
263
266
|
//
|
|
264
267
|
// Azure AD Graph API
|
|
265
268
|
//
|
|
266
|
-
export function groupsGet(instance: IPublicClientApplication, user: User | undefined, groupSearchString: string): Promise<{groups: Group[], error: string}>;
|
|
269
|
+
export function groupsGet(instance: IPublicClientApplication, user: User | undefined, groupSearchString: string): Promise<{ groups: Group[], error: string }>;
|
|
270
|
+
export function oauth2PermissionGrantsGet(options: RequestInit, spid: string, oid: string): Promise<{grants: string, error: string}>;
|
|
271
|
+
export function requestAdminConsent(user: User, scope: string): void;
|
|
272
|
+
export function servicePrincipalGet(options: RequestInit, appid: string): Promise<{ spid: string, error: string }>;
|
|
267
273
|
export function signIn(user: User, tasks: TaskArray): boolean;
|
|
268
274
|
export function signInIncrementally(user: User, scope: string): void;
|
|
269
|
-
export function requestAdminConsent(user: User, scope: string): void;
|
|
270
275
|
export function signOut(user: User): boolean;
|
|
271
276
|
export function tenantRelationshipsGetByDomain(loggedInuser: User, tenant: Tenant, instance: IPublicClientApplication, debug: boolean): boolean;
|
|
272
277
|
export function tenantRelationshipsGetById(user: User, ii: InitInfo, instance: IPublicClientApplication, tasks: TaskArray, debug: boolean): boolean;
|
|
273
|
-
export function tenantUnauthenticatedLookup(tenant: Tenant, debug: boolean):
|
|
278
|
+
export function tenantUnauthenticatedLookup(tenant: Tenant, debug: boolean): boolean;
|
|
279
|
+
export function userDelegatedScopesGet(instance: IPublicClientApplication, loggedInUser: User, tenant: Tenant): { scopes: string, id: string, error: string };
|
|
280
|
+
export function userDelegatedScopesRemove(instance: IPublicClientApplication, loggedInUser: User, tenant: Tenant, scope: string): boolean;
|
|
274
281
|
export function usersGet(instance: IPublicClientApplication, user: User | undefined): { users: string[], error: string };
|
|
275
282
|
//
|
|
276
283
|
// Mindline Config API
|
package/index.ts
CHANGED
|
@@ -69,6 +69,8 @@ export class graphConfig {
|
|
|
69
69
|
static graphGroupsEndpoint: string = "https://graph.microsoft.com/v1.0/groups";
|
|
70
70
|
static graphMailEndpoint: string = "https://graph.microsoft.com/v1.0/me/messages";
|
|
71
71
|
static graphMeEndpoint: string = "https://graph.microsoft.com/v1.0/me";
|
|
72
|
+
static graphOauth2PermissionGrants: string = "https://graph.microsoft.com/v1.0/oauth2PermissionGrants";
|
|
73
|
+
static graphServicePrincipalsEndpoint: string = "https://graph.microsoft.com/v1.0/servicePrincipals";
|
|
72
74
|
static graphUsersEndpoint: string = "https://graph.microsoft.com/v1.0/users";
|
|
73
75
|
// sovereign cloud tenant info endpoints
|
|
74
76
|
static graphTenantByDomainPredicate: string = "beta/tenantRelationships/findTenantInformationByDomainName";
|
|
@@ -90,6 +92,7 @@ export class UserScope {
|
|
|
90
92
|
group: string;
|
|
91
93
|
value: string;
|
|
92
94
|
consented: boolean;
|
|
95
|
+
removable: boolean;
|
|
93
96
|
expanded: string;
|
|
94
97
|
static compareByValue(a: UserScope, b: UserScope): number {
|
|
95
98
|
return a.value.localeCompare(b.value);
|
|
@@ -106,7 +109,6 @@ export class User {
|
|
|
106
109
|
tid: string;
|
|
107
110
|
companyName: string;
|
|
108
111
|
companyDomain: string;
|
|
109
|
-
associatedWorkspaces: string[];
|
|
110
112
|
workspaceIDs: string;
|
|
111
113
|
session: string; // button text
|
|
112
114
|
spacode: string; // to get front end access token
|
|
@@ -119,11 +121,10 @@ export class User {
|
|
|
119
121
|
this.oid = "";
|
|
120
122
|
this.name = "";
|
|
121
123
|
this.mail = "";
|
|
122
|
-
this.authority = "";
|
|
124
|
+
this.authority = "https://login.microsoftonline.com/";
|
|
123
125
|
this.tid = "";
|
|
124
126
|
this.companyName = "";
|
|
125
127
|
this.companyDomain = "";
|
|
126
|
-
this.associatedWorkspaces = new Array();
|
|
127
128
|
this.workspaceIDs = "";
|
|
128
129
|
this.session = "Sign In";
|
|
129
130
|
this.spacode = "";
|
|
@@ -157,6 +158,7 @@ export class Tenant {
|
|
|
157
158
|
authority: string;
|
|
158
159
|
workspaceIDs: string;
|
|
159
160
|
sel: boolean; // selection state
|
|
161
|
+
graphSP: string; // graph resource ID (service principal) for this tenant
|
|
160
162
|
constructor() {
|
|
161
163
|
this.tid = "";
|
|
162
164
|
this.name = "";
|
|
@@ -167,6 +169,7 @@ export class Tenant {
|
|
|
167
169
|
this.authority = "";
|
|
168
170
|
this.workspaceIDs = "";
|
|
169
171
|
this.sel = false;
|
|
172
|
+
this.graphSP = "";
|
|
170
173
|
}
|
|
171
174
|
}
|
|
172
175
|
function getGraphEndpoint(authority: string): string {
|
|
@@ -381,7 +384,6 @@ export class InitInfo {
|
|
|
381
384
|
newuser.tid = user.tid;
|
|
382
385
|
newuser.companyName = user.companyName;
|
|
383
386
|
newuser.companyDomain = user.companyDomain;
|
|
384
|
-
newuser.associatedWorkspaces = user.associatedWorkspaces;
|
|
385
387
|
newuser.workspaceIDs = user.workspaceIDs;
|
|
386
388
|
newuser.session = user.session;
|
|
387
389
|
newuser.spacode = user.spacode;
|
|
@@ -897,11 +899,9 @@ export class BatchArray {
|
|
|
897
899
|
this.pb_idleMax = Math.max(this.pb_idle, this.pb_idleMax);
|
|
898
900
|
setIdleText(`${this.pb_total} seconds elapsed. Last update ${this.pb_idle} seconds ago. [max idle: ${this.pb_idleMax}/60]`);
|
|
899
901
|
if (this.pb_idle >= 60) {
|
|
900
|
-
clearInterval(this.pb_timer);
|
|
901
|
-
this.pb_timer = null;
|
|
902
902
|
if (this.milestoneArray.milestones[0].Write == null) {
|
|
903
903
|
this.milestoneArray.write(setMilestones);
|
|
904
|
-
setConfigSyncResult(`
|
|
904
|
+
setConfigSyncResult(`sync continuing, but no update for ${this.pb_idle} seconds`);
|
|
905
905
|
}
|
|
906
906
|
}
|
|
907
907
|
// if we get to 100, the progress bar stops but SignalR or countdown timer completes the sync
|
|
@@ -1198,7 +1198,6 @@ export class TenantNode {
|
|
|
1198
1198
|
//
|
|
1199
1199
|
// Azure AD Graph API
|
|
1200
1200
|
//
|
|
1201
|
-
//groupsGet - GET /groups
|
|
1202
1201
|
export async function groupsGet(instance: IPublicClientApplication, user: User | undefined, groupSearchString: string): Promise<{ groups: Group[], error: string }> {
|
|
1203
1202
|
// need a logged in user to get graph users
|
|
1204
1203
|
if (user == null || user.spacode == "") {
|
|
@@ -1222,6 +1221,103 @@ export async function groupsGet(instance: IPublicClientApplication, user: User |
|
|
|
1222
1221
|
return { groups: [], error: `Exception: ${error}` };
|
|
1223
1222
|
}
|
|
1224
1223
|
}
|
|
1224
|
+
export async function oauth2PermissionGrantsGet(options: RequestInit, spid: string, oid: string): Promise<{ grants: string, id: string, error: string }> {
|
|
1225
|
+
try {
|
|
1226
|
+
// make /oauth2PermissionGrants endpoint call
|
|
1227
|
+
let spurl: string = graphConfig.graphOauth2PermissionGrants;
|
|
1228
|
+
let url: URL = new URL(spurl);
|
|
1229
|
+
url.searchParams.append("$filter", `resourceId eq '${spid}' and consentType eq 'Principal' and principalId eq '${oid}'`);
|
|
1230
|
+
let response = await fetch(url.href, options);
|
|
1231
|
+
let data = await response.json();
|
|
1232
|
+
if (typeof data.error != "undefined") {
|
|
1233
|
+
return { grants: null, id: null, error: `${data.error.code}: ${data.error.message}` };
|
|
1234
|
+
}
|
|
1235
|
+
// we assume there is only one such grant
|
|
1236
|
+
if (data.value.length != 1) {
|
|
1237
|
+
debugger;
|
|
1238
|
+
return { grants: null, id: null, error: `oauth2PermissionGrantsGet: more than one matching delegated consent grant.` };
|
|
1239
|
+
}
|
|
1240
|
+
return { grants: data.value[0].scope, id: data.value[0].id, error: `` };
|
|
1241
|
+
}
|
|
1242
|
+
catch (error: any) {
|
|
1243
|
+
console.log(error);
|
|
1244
|
+
return { grants: null, id: null, error: `Exception: ${error}` };
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
export async function oauth2PermissionGrantsSet(instance: IPublicClientApplication, loggedInUser: User, id: string, scopes: string): Promise<boolean> {
|
|
1248
|
+
// need a logged in user to get graph users
|
|
1249
|
+
if (loggedInUser == null || loggedInUser.spacode == "") {
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
// make /oauth2PermissionGrants endpoint call
|
|
1253
|
+
try {
|
|
1254
|
+
let grantsurl: string = graphConfig.graphOauth2PermissionGrants + `/${id}`;
|
|
1255
|
+
let scopesBody: string = `{ "scope": "${scopes}" }`;
|
|
1256
|
+
const headers = await defineHeaders(instance, loggedInUser);
|
|
1257
|
+
let options: RequestInit = { method: "PATCH", headers: headers, body: scopesBody };
|
|
1258
|
+
let response = await fetch(grantsurl, options);
|
|
1259
|
+
if (response.status == 204 && response.statusText == "No Content") {
|
|
1260
|
+
return true;
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
debugger;
|
|
1264
|
+
console.log(`oauth2PermissionGrantsSet: PATCH failed ${data.error.code}: ${data.error.message}`);
|
|
1265
|
+
return false;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
catch (error: any) {
|
|
1269
|
+
debugger;
|
|
1270
|
+
console.log(error);
|
|
1271
|
+
return false;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
export function requestAdminConsent(user: User, scope: string): void {
|
|
1275
|
+
if (user.oid == "1") return;
|
|
1276
|
+
//
|
|
1277
|
+
// for app permissions (app roles) we must use the /.default scope for admin consent
|
|
1278
|
+
// 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.
|
|
1279
|
+
// https://learn.microsoft.com/en-us/answers/questions/431784/how-to-grant-application-permissions-with-dynamic
|
|
1280
|
+
//
|
|
1281
|
+
// this means that, if we want to be granular about SyncReader vs. SyncWriter permissions, we must have separate Applications
|
|
1282
|
+
// for now, we request the /.default scope whenever any of the SyncReader or SyncWriter permissions are requested
|
|
1283
|
+
//
|
|
1284
|
+
// trying to use the Challenge endpoint for app permissions, setting this scope caused the call to quietly fail without error
|
|
1285
|
+
// scope = "63100afe-506e-4bb2-8ff7-d8d5ab373129/.default";
|
|
1286
|
+
//
|
|
1287
|
+
// 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
|
|
1288
|
+
// https://learn.microsoft.com/EN-US/azure/active-directory/develop/scopes-oidc#client-credentials-grant-flow-and-default
|
|
1289
|
+
// https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#request-the-permissions-from-a-directory-admin
|
|
1290
|
+
//
|
|
1291
|
+
let adminConsentURL: string = "https://login.microsoftonline.com/";
|
|
1292
|
+
adminConsentURL += user.tid;
|
|
1293
|
+
adminConsentURL += "/adminconsent";
|
|
1294
|
+
let url: URL = new URL(adminConsentURL);
|
|
1295
|
+
url.searchParams.append("client_id", "63100afe-506e-4bb2-8ff7-d8d5ab373129");
|
|
1296
|
+
url.searchParams.append("redirect_uri", window.location.origin);
|
|
1297
|
+
url.searchParams.append("domain_hint", user.companyDomain);
|
|
1298
|
+
window.location.assign(url.href);
|
|
1299
|
+
}
|
|
1300
|
+
export async function servicePrincipalGet(options: RequestInit, appid: string): Promise<{ spid: string, error: string }> {
|
|
1301
|
+
try {
|
|
1302
|
+
// make /servicePrincipals endpoint call to get the Service Principal ID
|
|
1303
|
+
let spurl: string = graphConfig.graphServicePrincipalsEndpoint;
|
|
1304
|
+
spurl += `(appId='${appid}')`;
|
|
1305
|
+
let url: URL = new URL(spurl);
|
|
1306
|
+
url.searchParams.append("$select", "id,appId,displayName");
|
|
1307
|
+
let response = await fetch(url.href, options);
|
|
1308
|
+
let data = await response.json();
|
|
1309
|
+
if (typeof data.error !== "undefined") {
|
|
1310
|
+
return { spid: "", error: `${data.error.code}: ${data.error.message}` };
|
|
1311
|
+
}
|
|
1312
|
+
else {
|
|
1313
|
+
return { spid: data.id, error: `` };
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
catch (error: any) {
|
|
1317
|
+
console.log(error);
|
|
1318
|
+
return { spid: "", error: `Exception: ${error}` };
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1225
1321
|
export async function signIn(user: User, tasks: TaskArray): Promise<boolean> {
|
|
1226
1322
|
// admin authority is blank at signIn, lookup authority real-time
|
|
1227
1323
|
if (user.authority == "") {
|
|
@@ -1291,32 +1387,6 @@ export function signInIncrementally(user: User, scope: string): void {
|
|
|
1291
1387
|
url.searchParams.append("loginHint", user.mail);
|
|
1292
1388
|
window.location.assign(url.href);
|
|
1293
1389
|
}
|
|
1294
|
-
export function requestAdminConsent(user: User, scope: string): void {
|
|
1295
|
-
if (user.oid == "1") return;
|
|
1296
|
-
//
|
|
1297
|
-
// for app permissions (app roles) we must use the /.default scope for admin consent
|
|
1298
|
-
// 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.
|
|
1299
|
-
// https://learn.microsoft.com/en-us/answers/questions/431784/how-to-grant-application-permissions-with-dynamic
|
|
1300
|
-
//
|
|
1301
|
-
// this means that, if we want to be granular about SyncReader vs. SyncWriter permissions, we must have separate Applications
|
|
1302
|
-
// for now, we request the /.default scope whenever any of the SyncReader or SyncWriter permissions are requested
|
|
1303
|
-
//
|
|
1304
|
-
// trying to use the Challenge endpoint for app permissions, setting this scope caused the call to quietly fail without error
|
|
1305
|
-
// scope = "63100afe-506e-4bb2-8ff7-d8d5ab373129/.default";
|
|
1306
|
-
//
|
|
1307
|
-
// 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
|
|
1308
|
-
// https://learn.microsoft.com/EN-US/azure/active-directory/develop/scopes-oidc#client-credentials-grant-flow-and-default
|
|
1309
|
-
// https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#request-the-permissions-from-a-directory-admin
|
|
1310
|
-
//
|
|
1311
|
-
let adminConsentURL: string = "https://login.microsoftonline.com/";
|
|
1312
|
-
adminConsentURL += user.tid;
|
|
1313
|
-
adminConsentURL += "/adminconsent";
|
|
1314
|
-
let url: URL = new URL(adminConsentURL);
|
|
1315
|
-
url.searchParams.append("client_id", "63100afe-506e-4bb2-8ff7-d8d5ab373129");
|
|
1316
|
-
url.searchParams.append("redirect_uri", window.location.origin);
|
|
1317
|
-
url.searchParams.append("domain_hint", user.companyDomain);
|
|
1318
|
-
window.location.assign(url.href);
|
|
1319
|
-
}
|
|
1320
1390
|
export async function signOut(user: User): Promise<boolean>{
|
|
1321
1391
|
if (user.oid == "1") return false;
|
|
1322
1392
|
// set logout_hint in the .NET session for streamlined logout
|
|
@@ -1355,10 +1425,10 @@ export async function tenantRelationshipsGetByDomain(loggedInUser: User, tenant:
|
|
|
1355
1425
|
try {
|
|
1356
1426
|
let response: AuthenticationResult = await instance.acquireTokenByCode({ code: loggedInUser.spacode });
|
|
1357
1427
|
loggedInUser.accessToken = response.accessToken; // cache access token on the user
|
|
1358
|
-
console.log("Front end token acquired: " + loggedInUser.accessToken.slice(0, 20));
|
|
1428
|
+
console.log("tenantRelationshipsGetByDomain: Front end token acquired: " + loggedInUser.accessToken.slice(0, 20));
|
|
1359
1429
|
}
|
|
1360
1430
|
catch (error: any) {
|
|
1361
|
-
console.log("Front end token failure: " + error);
|
|
1431
|
+
console.log("tenantRelationshipsGetByDomain: Front end token failure: " + error);
|
|
1362
1432
|
return false; // failed to get access token, no need to re-render
|
|
1363
1433
|
}
|
|
1364
1434
|
}
|
|
@@ -1374,37 +1444,38 @@ export async function tenantRelationshipsGetByDomain(loggedInUser: User, tenant:
|
|
|
1374
1444
|
tenantEndpoint += "(domainName='";
|
|
1375
1445
|
tenantEndpoint += tenant.domain;
|
|
1376
1446
|
tenantEndpoint += "')";
|
|
1377
|
-
console.log("Attempting GET from /findTenantInformationByDomainName:", tenantEndpoint);
|
|
1447
|
+
console.log("tenantRelationshipsGetByDomain: Attempting GET from /findTenantInformationByDomainName:", tenantEndpoint);
|
|
1378
1448
|
let response = await fetch(tenantEndpoint, options);
|
|
1379
1449
|
if (response.status == 200 && response.statusText == "OK") {
|
|
1380
1450
|
let data = await response.json();
|
|
1381
1451
|
if (data) {
|
|
1382
1452
|
if (data.error != null) {
|
|
1383
1453
|
debugger;
|
|
1384
|
-
console.log("Failed GET from /findTenantInformationByDomainName: ", data.error.message);
|
|
1454
|
+
console.log("tenantRelationshipsGetByDomain: Failed GET from /findTenantInformationByDomainName: ", data.error.message);
|
|
1385
1455
|
return false;
|
|
1386
1456
|
}
|
|
1387
1457
|
else if (data.displayName != null && data.displayName !== "") {
|
|
1388
1458
|
// set domain information on passed tenant
|
|
1389
1459
|
tenant.tid = data.tenantId;
|
|
1390
1460
|
tenant.name = data.displayName;
|
|
1391
|
-
console.log("Successful GET from /findTenantInformationByDomainName: ", data.displayName);
|
|
1461
|
+
console.log("tenantRelationshipsGetByDomain: Successful GET from /findTenantInformationByDomainName: ", data.displayName);
|
|
1392
1462
|
return true; // success, need UX to re-render
|
|
1393
1463
|
}
|
|
1394
1464
|
}
|
|
1395
1465
|
else {
|
|
1396
|
-
console.log("Failed to GET from /
|
|
1466
|
+
console.log("tenantRelationshipsGetByDomain: Failed to GET from /findTenantInformationByDomainName: ", tenantEndpoint);
|
|
1397
1467
|
}
|
|
1398
1468
|
}
|
|
1399
1469
|
}
|
|
1400
1470
|
catch (error: any) {
|
|
1401
|
-
console.log("Failed to GET from /
|
|
1471
|
+
console.log("Failed to GET from /findTenantInformationByDomainName: ", error);
|
|
1402
1472
|
return false; // failed, no need for UX to re-render
|
|
1403
1473
|
}
|
|
1404
1474
|
return false; // failed, no need for UX to re-render
|
|
1405
1475
|
}
|
|
1406
1476
|
//tenantRelationshipsGetById - query AAD for associated company name and domain
|
|
1407
1477
|
export async function tenantRelationshipsGetById(user: User, ii: InitInfo, instance: IPublicClientApplication, tasks: TaskArray, debug: boolean): Promise<boolean> {
|
|
1478
|
+
console.log("**** tenantRelationshipsGetById");
|
|
1408
1479
|
if (debug) debugger;
|
|
1409
1480
|
// since we should mainly be called when a user has newly logged in, we can afford the performance hit of looking up the tenant name and domain again
|
|
1410
1481
|
// if (user.companyName != "") return false;
|
|
@@ -1413,10 +1484,10 @@ export async function tenantRelationshipsGetById(user: User, ii: InitInfo, insta
|
|
|
1413
1484
|
try {
|
|
1414
1485
|
let response: AuthenticationResult = await instance.acquireTokenByCode({ code: user.spacode });
|
|
1415
1486
|
user.accessToken = response.accessToken; // cache access token
|
|
1416
|
-
console.log("Front end token acquired: " + user.accessToken.slice(0, 20));
|
|
1487
|
+
console.log("tenantRelationshipsGetById: Front end token acquired: " + user.accessToken.slice(0, 20));
|
|
1417
1488
|
}
|
|
1418
1489
|
catch (error: any) {
|
|
1419
|
-
console.log("Front end token failure: " + error);
|
|
1490
|
+
console.log("tenantRelationshipsGetById: Front end token failure: " + error);
|
|
1420
1491
|
return false; // failed to get access token, no need to re-render
|
|
1421
1492
|
}
|
|
1422
1493
|
}
|
|
@@ -1434,7 +1505,7 @@ export async function tenantRelationshipsGetById(user: User, ii: InitInfo, insta
|
|
|
1434
1505
|
tenantEndpoint += "')";
|
|
1435
1506
|
// track time of tenant details query
|
|
1436
1507
|
tasks.setTaskStart("GET tenant details", new Date());
|
|
1437
|
-
console.log("Attempting GET from /findTenantInformationByTenantId:", tenantEndpoint);
|
|
1508
|
+
console.log("tenantRelationshipsGetById: Attempting GET from /findTenantInformationByTenantId:", tenantEndpoint);
|
|
1438
1509
|
let response = await fetch(tenantEndpoint, options);
|
|
1439
1510
|
let data = await response.json();
|
|
1440
1511
|
if (data && typeof data.displayName !== undefined && data.displayName !== "") {
|
|
@@ -1451,16 +1522,16 @@ export async function tenantRelationshipsGetById(user: User, ii: InitInfo, insta
|
|
|
1451
1522
|
console.log("tenantRelationshipsGetById: missing associated tenant for logged in user.");
|
|
1452
1523
|
debugger;
|
|
1453
1524
|
}
|
|
1454
|
-
console.log("Successful GET from /findTenantInformationByTenantId: ", data.displayName);
|
|
1525
|
+
console.log("tenantRelationshipsGetById: Successful GET from /findTenantInformationByTenantId: ", data.displayName);
|
|
1455
1526
|
tasks.setTaskEnd("GET tenant details", new Date(), "complete");
|
|
1456
1527
|
return true; // success, need UX to re-render
|
|
1457
1528
|
}
|
|
1458
1529
|
else {
|
|
1459
|
-
console.log("Failed to GET from /findTenantInformationByTenantId: ", tenantEndpoint);
|
|
1530
|
+
console.log("tenantRelationshipsGetById: Failed to GET from /findTenantInformationByTenantId: ", tenantEndpoint);
|
|
1460
1531
|
}
|
|
1461
1532
|
}
|
|
1462
1533
|
catch (error: any) {
|
|
1463
|
-
console.log("Failed to GET from /findTenantInformationByTenantId: ", error);
|
|
1534
|
+
console.log("tenantRelationshipsGetById: Failed to GET from /findTenantInformationByTenantId: ", error);
|
|
1464
1535
|
tasks.setTaskEnd("GET tenant details", new Date(), "failed");
|
|
1465
1536
|
return false; // failed, no need for UX to re-render
|
|
1466
1537
|
}
|
|
@@ -1516,17 +1587,76 @@ export async function tenantUnauthenticatedLookup(tenant: Tenant, debug: boolean
|
|
|
1516
1587
|
}
|
|
1517
1588
|
return false; // failed, no need for UX to re-render
|
|
1518
1589
|
}
|
|
1590
|
+
export async function userDelegatedScopesGet(instance: IPublicClientApplication, loggedInUser: User, tenant: Tenant): Promise<{ scopes: string, id: string, error: string }> {
|
|
1591
|
+
// need a logged in user and valid tenant to query graph
|
|
1592
|
+
if (loggedInUser == null || loggedInUser.spacode == "" || tenant == null) {
|
|
1593
|
+
debugger;
|
|
1594
|
+
return { scopes: null, id: null, error: `500: invalid parameter(s) passed to getUserDelegatedScopes` };
|
|
1595
|
+
}
|
|
1596
|
+
// create headers
|
|
1597
|
+
const headers = await defineHeaders(instance, loggedInUser);
|
|
1598
|
+
let options: RequestInit = { method: "GET", headers: headers };
|
|
1599
|
+
try {
|
|
1600
|
+
// first, cache Graph resource ID (service principal) for this tenant if we don't have it already
|
|
1601
|
+
if (tenant.graphSP == "") {
|
|
1602
|
+
let { spid, error } = await servicePrincipalGet(options, "00000003-0000-0000-c000-000000000000");
|
|
1603
|
+
if (error != "") {
|
|
1604
|
+
debugger;
|
|
1605
|
+
return { scopes: null, id: null, error: `${error}` };
|
|
1606
|
+
}
|
|
1607
|
+
tenant.graphSP = spid;
|
|
1608
|
+
}
|
|
1609
|
+
// then, retrieve the delegated Graph permissions assigned to this user
|
|
1610
|
+
let { grants, id, error } = await oauth2PermissionGrantsGet(options, tenant.graphSP, loggedInUser.oid);
|
|
1611
|
+
if (error != "") {
|
|
1612
|
+
debugger;
|
|
1613
|
+
return { scopes: null, id: null, error: `${error}` };
|
|
1614
|
+
}
|
|
1615
|
+
return { scopes: grants, id: id, error: `` };
|
|
1616
|
+
}
|
|
1617
|
+
catch (error: any) {
|
|
1618
|
+
debugger;
|
|
1619
|
+
console.log(error);
|
|
1620
|
+
return { scopes: null, id: null, error: `Exception: ${error}` };
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
export async function userDelegatedScopesRemove(instance: IPublicClientApplication, loggedInUser: User, tenant: Tenant, scope: string): Promise<boolean> {
|
|
1624
|
+
// need a logged in user and valid tenant to query graph
|
|
1625
|
+
if (loggedInUser == null || loggedInUser.spacode == "" || tenant == null) {
|
|
1626
|
+
debugger;
|
|
1627
|
+
return false;
|
|
1628
|
+
}
|
|
1629
|
+
// get current set of delegated scopes in order to remove passed scope
|
|
1630
|
+
let { scopes, id, error } = await userDelegatedScopesGet(instance, loggedInUser, tenant);
|
|
1631
|
+
if (error != "") {
|
|
1632
|
+
debugger;
|
|
1633
|
+
console.log(`userDelegatedScopesRemove: cannot find userDelegatedScopes for ${loggedInUser.mail}: ${error}`);
|
|
1634
|
+
return false;
|
|
1635
|
+
}
|
|
1636
|
+
// remove passed scope (case sensitive)
|
|
1637
|
+
scopes = scopes.replace(scope, "");
|
|
1638
|
+
// set updated oauth2permissions
|
|
1639
|
+
let removed: boolean = await oauth2PermissionGrantsSet(instance, loggedInUser, id, scopes);
|
|
1640
|
+
if (!removed) {
|
|
1641
|
+
debugger;
|
|
1642
|
+
console.log(`userDelegatedScopesRemove: cannot set oauth2PermissionGrants for ${loggedInUser.mail}: ${error}`);
|
|
1643
|
+
return false;
|
|
1644
|
+
}
|
|
1645
|
+
// replace scope array on logged in user
|
|
1646
|
+
loggedInUser.scopes = scopes.split(" ");
|
|
1647
|
+
return removed;
|
|
1648
|
+
}
|
|
1519
1649
|
//usersGet - GET from AAD Users endpoint
|
|
1520
1650
|
export async function usersGet(instance: IPublicClientApplication, user: User | undefined): Promise<{ users: string[], error: string }> {
|
|
1521
1651
|
// need a logged in user to get graph users
|
|
1522
1652
|
if (user == null || user.spacode == "") {
|
|
1523
1653
|
return { users: [], error: `500: invalid user passed to groupsGet` };
|
|
1524
1654
|
}
|
|
1525
|
-
// create headers
|
|
1526
|
-
const headers = await defineHeaders(instance, user);
|
|
1527
|
-
let options = { method: "GET", headers: headers };
|
|
1528
1655
|
// make /users endpoint call
|
|
1529
1656
|
try {
|
|
1657
|
+
// create headers
|
|
1658
|
+
const headers = await defineHeaders(instance, user);
|
|
1659
|
+
let options = { method: "GET", headers: headers };
|
|
1530
1660
|
let response = await fetch(graphConfig.graphUsersEndpoint, options);
|
|
1531
1661
|
let data = await response.json();
|
|
1532
1662
|
if (typeof data.error !== "undefined") {
|
|
@@ -1543,9 +1673,7 @@ export async function usersGet(instance: IPublicClientApplication, user: User |
|
|
|
1543
1673
|
return { users: [], error: `Exception: ${error}` };
|
|
1544
1674
|
}
|
|
1545
1675
|
}
|
|
1546
|
-
//
|
|
1547
|
-
// Mindline Config API
|
|
1548
|
-
//
|
|
1676
|
+
// ======================= Mindline Config API ===============================
|
|
1549
1677
|
export async function configEdit(instance: IPublicClientApplication, authorizedUser: User, config: Config, workspaceId: string, debug: boolean): Promise<APIResult> {
|
|
1550
1678
|
let result: APIResult = new APIResult();
|
|
1551
1679
|
if (config.id === "1") {
|
|
@@ -1589,6 +1717,7 @@ export async function configsRefresh(instance: IPublicClientApplication, authori
|
|
|
1589
1717
|
}
|
|
1590
1718
|
// retrieve Workspace(s), User(s), Tenant(s), Config(s) given newly logged in user
|
|
1591
1719
|
export async function initGet(instance: IPublicClientApplication, authorizedUser: User, user: User, ii: InitInfo, tasks: TaskArray, debug: boolean): Promise<APIResult> {
|
|
1720
|
+
console.log(`>>>>>> initGet`);
|
|
1592
1721
|
let result: APIResult = new APIResult();
|
|
1593
1722
|
if (debug) debugger;
|
|
1594
1723
|
// lookup authority for this user (the lookup call does it based on domain, but TID works as well to find authority)
|
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.58",
|
|
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/tenants.json
CHANGED
package/users.json
CHANGED
package/users2.json
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
"tid": "7f4567b8-f9a9-4ad3-9cb5-ef16a80e5744",
|
|
8
8
|
"companyName": "Mindline1",
|
|
9
9
|
"companyDomain": "mindline1.onmicrosoft.com",
|
|
10
|
-
"associatedWorkspaces": [ "1", "2", "3" ],
|
|
11
10
|
"session": "Sign In"
|
|
12
11
|
},
|
|
13
12
|
{
|
|
@@ -18,7 +17,6 @@
|
|
|
18
17
|
"tid": "df9c2e0a-f6fe-43bb-a155-d51f66dffe0e",
|
|
19
18
|
"companyName": "Mindline2",
|
|
20
19
|
"companyDomain": "mindline2.onmicrosoft.com",
|
|
21
|
-
"associatedWorkspaces": [ "1" ],
|
|
22
20
|
"session": "Sign In"
|
|
23
21
|
},
|
|
24
22
|
{
|
|
@@ -29,7 +27,6 @@
|
|
|
29
27
|
"tid": "1",
|
|
30
28
|
"companyName": "WhoIAm",
|
|
31
29
|
"companyDomain": "whoiam.onmicrosoft.com",
|
|
32
|
-
"associatedWorkspaces": [ "2" ],
|
|
33
30
|
"session": "Sign In"
|
|
34
31
|
},
|
|
35
32
|
{
|
|
@@ -40,7 +37,6 @@
|
|
|
40
37
|
"tid": "2",
|
|
41
38
|
"companyName": "Grit Sofware",
|
|
42
39
|
"companyDomain": "gritsoftware.onmicrosoft.com",
|
|
43
|
-
"associatedWorkspaces": [ "2" ],
|
|
44
40
|
"session": "Sign In"
|
|
45
41
|
},
|
|
46
42
|
{
|
|
@@ -51,7 +47,6 @@
|
|
|
51
47
|
"tid": "3",
|
|
52
48
|
"companyName": "Google",
|
|
53
49
|
"companyDomain": "google.onmicrosoft.com",
|
|
54
|
-
"associatedWorkspaces": [ "3" ],
|
|
55
50
|
"session": "Sign In"
|
|
56
51
|
},
|
|
57
52
|
{
|
|
@@ -62,7 +57,6 @@
|
|
|
62
57
|
"tid": "4",
|
|
63
58
|
"companyName": "Trackman Golf",
|
|
64
59
|
"companyDomain": "trackman.onmicrosoft.com",
|
|
65
|
-
"associatedWorkspaces": [ "3" ],
|
|
66
60
|
"session": "Sign In"
|
|
67
61
|
}
|
|
68
62
|
]
|
|
Binary file
|