@mindline/sync 1.0.73 → 1.0.75
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 -0
- package/.vs/slnx.sqlite +0 -0
- package/.vs/sync/FileContentIndex/14dddfc3-43fc-4d99-b5ff-2695774c7325.vsidx +0 -0
- package/.vs/sync/FileContentIndex/5d07352f-b1a7-4c75-8bbb-e4a043235bc2.vsidx +0 -0
- package/.vs/sync/FileContentIndex/7e4ecd2c-67ad-427f-b332-f46a4844d20f.vsidx +0 -0
- package/.vs/sync/FileContentIndex/fb5d1ac5-8cdf-47e7-80da-383f4c5b9f68.vsidx +0 -0
- package/.vs/sync/config/applicationhost.config +1011 -0
- package/.vs/sync/v17/.wsuo +0 -0
- package/.vs/sync/v17/DocumentLayout.json +89 -22
- package/actors.json +20 -0
- package/hybridspa.ts +71 -42
- package/index.d.ts +26 -8
- package/index.ts +315 -21
- package/package.json +1 -1
- package/resources.json +58 -0
- package/.vs/sync/FileContentIndex/2df54e99-e721-4891-b1ee-16c3205a1c35.vsidx +0 -0
package/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
//index.ts - published interface - AAD implementations, facade to Mindline Config API
|
|
2
2
|
import * as signalR from "@microsoft/signalr"
|
|
3
|
+
import { AccountInfo } from "@azure/msal-common";
|
|
3
4
|
import { IPublicClientApplication, AuthenticationResult } from "@azure/msal-browser"
|
|
4
5
|
import { deserializeArray } from 'class-transformer';
|
|
5
|
-
import {
|
|
6
|
+
import { processErrors, adminDelete, adminPost, adminsGet, configConsentReadPut, configConsentWritePut, configDelete, configsGet, configPost, configPut, initPost, readerPost, tenantPost, tenantDelete, tenantsGet, workspacePut, workspacesGet } from './hybridspa';
|
|
6
7
|
import { version } from './package.json';
|
|
7
8
|
import users from "./users.json";
|
|
8
9
|
import tenants from "./tenants.json";
|
|
@@ -10,6 +11,8 @@ import configs from "./configs.json";
|
|
|
10
11
|
import workspaces from "./workspaces.json";
|
|
11
12
|
import tasksData from "./tasks";
|
|
12
13
|
import syncmilestones from './syncmilestones';
|
|
14
|
+
import resources from './resources';
|
|
15
|
+
import actors from './actors';
|
|
13
16
|
import { log } from "console";
|
|
14
17
|
const FILTER_FIELD = "workspaceIDs";
|
|
15
18
|
// called by unit tests
|
|
@@ -28,6 +31,11 @@ export class APIResult {
|
|
|
28
31
|
array: Array<Object> | null;
|
|
29
32
|
constructor() { this.result = true; this.status = 200; this.error = ""; this.version = version; this.array = null; }
|
|
30
33
|
}
|
|
34
|
+
export class azureConfig {
|
|
35
|
+
// azure graph REST API endpoints
|
|
36
|
+
static azureElevateAccess: string = "https://management.azure.com/providers/Microsoft.Authorization/elevateAccess?api-version=2016-07-01";
|
|
37
|
+
static azureListRootAssignments: string = "https://management.azure.com/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01&$filter=principalId+eq+";
|
|
38
|
+
};
|
|
31
39
|
export class mindlineConfig {
|
|
32
40
|
static environmentTag: string = "dev";
|
|
33
41
|
// config API endpoints
|
|
@@ -119,7 +127,9 @@ export class User {
|
|
|
119
127
|
workspaceIDs: string;
|
|
120
128
|
session: string; // button text
|
|
121
129
|
spacode: string; // to get front end access token
|
|
122
|
-
|
|
130
|
+
graphAccessToken: string; // front end graph access token
|
|
131
|
+
mindlineAccessToken: string; // front end mindline access token
|
|
132
|
+
azureAccessToken: string; // front end azure access token
|
|
123
133
|
loginHint: string; // to help sign out without prompt
|
|
124
134
|
scopes: string[]; // to detect if incremental consent has happened
|
|
125
135
|
authTS: Date; // timestamp user was authenticated
|
|
@@ -136,7 +146,9 @@ export class User {
|
|
|
136
146
|
this.workspaceIDs = "";
|
|
137
147
|
this.session = "Sign In";
|
|
138
148
|
this.spacode = "";
|
|
139
|
-
this.
|
|
149
|
+
this.graphAccessToken = "";
|
|
150
|
+
this.mindlineAccessToken = "";
|
|
151
|
+
this.azureAccessToken = "";
|
|
140
152
|
this.loginHint = "";
|
|
141
153
|
this.scopes = new Array();
|
|
142
154
|
this.authTS = new Date(0);
|
|
@@ -424,7 +436,7 @@ export class InitInfo {
|
|
|
424
436
|
newuser.workspaceIDs = user.workspaceIDs;
|
|
425
437
|
newuser.session = user.session;
|
|
426
438
|
newuser.spacode = user.spacode;
|
|
427
|
-
newuser.
|
|
439
|
+
newuser.graphAccessToken = user.graphAccessToken;
|
|
428
440
|
newuser.loginHint = user.loginHint;
|
|
429
441
|
newuser.scopes = user.scopes;
|
|
430
442
|
newuser.authTS = new Date(user.authTS);
|
|
@@ -1228,14 +1240,153 @@ export class TenantNode {
|
|
|
1228
1240
|
}
|
|
1229
1241
|
}
|
|
1230
1242
|
}
|
|
1243
|
+
export class ResourceArray {
|
|
1244
|
+
resourceNodes: ResourceNode[];
|
|
1245
|
+
constructor(bInitialize: boolean, bClearLocalStorage: boolean) {
|
|
1246
|
+
this.resourceNodes = new Array<ResourceNode>();
|
|
1247
|
+
if (bInitialize) {
|
|
1248
|
+
this.init(bClearLocalStorage);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
// get resource data from localStorage or file
|
|
1252
|
+
init(bClearLocalStorage: boolean): void {
|
|
1253
|
+
console.log(`Calling ResourceArray::init(bClearLocalStorage: ${bClearLocalStorage ? "true" : "false"})`);
|
|
1254
|
+
// if we have a non-empty string value stored, read it from localStorage
|
|
1255
|
+
if (storageAvailable("localStorage")) {
|
|
1256
|
+
let result = localStorage.getItem("ResourceArray");
|
|
1257
|
+
if (result != null && typeof result === "string" && result !== "") {
|
|
1258
|
+
if (bClearLocalStorage) {
|
|
1259
|
+
localStorage.removeItem("ResourceArray");
|
|
1260
|
+
}
|
|
1261
|
+
else {
|
|
1262
|
+
// read entire object from localstorage
|
|
1263
|
+
let raString: string = result;
|
|
1264
|
+
let resourceArray: ResourceArray = JSON.parse(raString);
|
|
1265
|
+
this.resourceNodes = resourceArray.resourceNodes;
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
// if storage unavailable or we were just asked to clear, read resources from file
|
|
1271
|
+
var resourceNodesString = JSON.stringify(resources);
|
|
1272
|
+
try {
|
|
1273
|
+
this.resourceNodes = deserializeArray(ResourceNode, resourceNodesString);
|
|
1274
|
+
} catch (e) {
|
|
1275
|
+
debugger;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
// read
|
|
1279
|
+
async read(instance: IPublicClientApplication, user: User): Promise<ResourceArray> {
|
|
1280
|
+
let resources: ResourceArray = new ResourceArray(false, false);
|
|
1281
|
+
resources.resourceNodes = await readResources(instance, user);
|
|
1282
|
+
return resources;
|
|
1283
|
+
}
|
|
1284
|
+
// save resource data to localstorage
|
|
1285
|
+
save(): void {
|
|
1286
|
+
// if we have localStorage, save resources
|
|
1287
|
+
if (storageAvailable("localStorage")) {
|
|
1288
|
+
let raString: string = JSON.stringify(this);
|
|
1289
|
+
localStorage.setItem("ResourceArray", raString);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
export class ResourceNode {
|
|
1294
|
+
type: string;
|
|
1295
|
+
resource: string;
|
|
1296
|
+
cost: number;
|
|
1297
|
+
expanded: boolean;
|
|
1298
|
+
resources: ResourceNode[];
|
|
1299
|
+
constructor(type: string, resource: string, cost: number) {
|
|
1300
|
+
this.type = type;
|
|
1301
|
+
this.resource = resource;
|
|
1302
|
+
this.cost = cost;
|
|
1303
|
+
this.expanded = false;
|
|
1304
|
+
this.resources = new Array<ResourceNode>();
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
export class ActorArray {
|
|
1308
|
+
actorNodes: ActorNode[];
|
|
1309
|
+
constructor(bClearLocalStorage: boolean) {
|
|
1310
|
+
this.actorNodes = new Array<ActorNode>();
|
|
1311
|
+
this.init(bClearLocalStorage);
|
|
1312
|
+
}
|
|
1313
|
+
// get initial data from localStorage or file
|
|
1314
|
+
init(bClearLocalStorage: boolean): void {
|
|
1315
|
+
console.log(`Calling ResourceArray::init(bClearLocalStorage: ${bClearLocalStorage ? "true" : "false"})`);
|
|
1316
|
+
// if we have a non-empty string value stored, read it from localStorage
|
|
1317
|
+
if (storageAvailable("localStorage")) {
|
|
1318
|
+
let result = localStorage.getItem("RBACActors");
|
|
1319
|
+
if (result != null && typeof result === "string" && result !== "") {
|
|
1320
|
+
if (bClearLocalStorage) {
|
|
1321
|
+
localStorage.removeItem("RBACActors");
|
|
1322
|
+
}
|
|
1323
|
+
else {
|
|
1324
|
+
let actorArrayString: string = result;
|
|
1325
|
+
let aaFromLocalStorage: ActorArray = JSON.parse(actorArrayString);
|
|
1326
|
+
this.actorNodes = aaFromLocalStorage.actorNodes;
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
// if storage unavailable or we were just asked to clear, read defaults to enable usable UI
|
|
1332
|
+
var actorsString = JSON.stringify(actors);
|
|
1333
|
+
try {
|
|
1334
|
+
this.actorNodes = deserializeArray(ActorNode, actorsString);
|
|
1335
|
+
} catch (e) {
|
|
1336
|
+
debugger;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
export class ActorNode {
|
|
1341
|
+
type: string;
|
|
1342
|
+
actor: string;
|
|
1343
|
+
resource: string;
|
|
1344
|
+
role: string;
|
|
1345
|
+
updatedby: string;
|
|
1346
|
+
updatedon: string;
|
|
1347
|
+
actors: ActorNode[];
|
|
1348
|
+
constructor(type: string, actor: string, resource: string, role: string, updatedby: string, updatedon: string) {
|
|
1349
|
+
this.type = type;
|
|
1350
|
+
this.actor = resource;
|
|
1351
|
+
this.resource = resource;
|
|
1352
|
+
this.role = role;
|
|
1353
|
+
this.updatedby = updatedby;
|
|
1354
|
+
this.updatedon = updatedon;
|
|
1355
|
+
this.actors = new Array<ActorNode>();
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1231
1358
|
// ======================= Azure AD Graph API ===============================
|
|
1359
|
+
// TODO: this is where you want to trigger a re-authentication if token expires
|
|
1360
|
+
async function graphDefineHeaders(
|
|
1361
|
+
instance: IPublicClientApplication,
|
|
1362
|
+
user: User
|
|
1363
|
+
): Promise<Headers> {
|
|
1364
|
+
const headers = new Headers();
|
|
1365
|
+
headers.append("Content-Type", "application/json");
|
|
1366
|
+
headers.append("accept", "*/*");
|
|
1367
|
+
// authorization header - if needed, retrieve and cache access token
|
|
1368
|
+
if (user.graphAccessToken == null || user.graphAccessToken === "") {
|
|
1369
|
+
try {
|
|
1370
|
+
let response: AuthenticationResult = await instance.acquireTokenByCode({
|
|
1371
|
+
code: user.spacode,
|
|
1372
|
+
});
|
|
1373
|
+
user.graphAccessToken = response.accessToken; // cache access token
|
|
1374
|
+
console.log("Front end token acquired: " + user.graphAccessToken.slice(0, 20));
|
|
1375
|
+
}
|
|
1376
|
+
catch (error: any) {
|
|
1377
|
+
console.log("Front end token failure: " + error);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
headers.append("Authorization", `Bearer ${user.graphAccessToken}`);
|
|
1381
|
+
return headers;
|
|
1382
|
+
}
|
|
1232
1383
|
export async function groupsGet(instance: IPublicClientApplication, user: User | undefined, groupSearchString: string): Promise<{ groups: Group[], error: string }> {
|
|
1233
1384
|
// need a logged in user to get graph users
|
|
1234
1385
|
if (user == null || user.spacode == "") {
|
|
1235
1386
|
return { groups: [], error: `500: invalid user passed to groupsGet` };
|
|
1236
1387
|
}
|
|
1237
1388
|
// create headers
|
|
1238
|
-
const headers = await
|
|
1389
|
+
const headers = await graphDefineHeaders(instance, user);
|
|
1239
1390
|
let options = { method: "GET", headers: headers };
|
|
1240
1391
|
// make /groups endpoint call
|
|
1241
1392
|
try {
|
|
@@ -1286,7 +1437,7 @@ export async function oauth2PermissionGrantsSet(instance: IPublicClientApplicati
|
|
|
1286
1437
|
let grantsurl: string = getGraphEndpoint(loggedInUser.authority);
|
|
1287
1438
|
grantsurl += graphConfig.graphOauth2PermissionGrantsPredicate + `/${id}`;
|
|
1288
1439
|
let scopesBody: string = `{ "scope": "${scopes}" }`;
|
|
1289
|
-
const headers = await
|
|
1440
|
+
const headers = await graphDefineHeaders(instance, loggedInUser);
|
|
1290
1441
|
let options: RequestInit = { method: "PATCH", headers: headers, body: scopesBody };
|
|
1291
1442
|
let response = await fetch(grantsurl, options);
|
|
1292
1443
|
if (response.status == 204 && response.statusText == "No Content") {
|
|
@@ -1472,12 +1623,12 @@ export async function tenantRelationshipsGetByDomain(loggedInUser: User, tenant:
|
|
|
1472
1623
|
// do we already have a valid tenant name? if so, nothing to add
|
|
1473
1624
|
if (tenant.name != null && tenant.name !== "") return false;
|
|
1474
1625
|
// if needed, retrieve and cache access token
|
|
1475
|
-
if (loggedInUser.
|
|
1626
|
+
if (loggedInUser.graphAccessToken != null && loggedInUser.graphAccessToken === "") {
|
|
1476
1627
|
console.log(`tenantRelationshipsGetByDomain called with invalid logged in user: ${loggedInUser.name}`);
|
|
1477
1628
|
try {
|
|
1478
|
-
let response: AuthenticationResult = await instance.acquireTokenByCode({ code: loggedInUser.spacode });
|
|
1479
|
-
loggedInUser.
|
|
1480
|
-
console.log("tenantRelationshipsGetByDomain: Front end token acquired: " + loggedInUser.
|
|
1629
|
+
let response: AuthenticationResult = await instance.acquireTokenByCode({ code: loggedInUser.spacode, scopes: ["user.read", "contacts.read", "CrossTenantInformation.ReadBasic.All"] });
|
|
1630
|
+
loggedInUser.graphAccessToken = response.accessToken; // cache access token on the user
|
|
1631
|
+
console.log("tenantRelationshipsGetByDomain: Front end token acquired: " + loggedInUser.graphAccessToken.slice(0, 20));
|
|
1481
1632
|
}
|
|
1482
1633
|
catch (error: any) {
|
|
1483
1634
|
console.log("tenantRelationshipsGetByDomain: Front end token failure: " + error);
|
|
@@ -1486,7 +1637,7 @@ export async function tenantRelationshipsGetByDomain(loggedInUser: User, tenant:
|
|
|
1486
1637
|
}
|
|
1487
1638
|
// prepare Authorization headers as part of options
|
|
1488
1639
|
const headers = new Headers();
|
|
1489
|
-
const bearer = `Bearer ${loggedInUser.
|
|
1640
|
+
const bearer = `Bearer ${loggedInUser.graphAccessToken}`;
|
|
1490
1641
|
headers.append("Authorization", bearer);
|
|
1491
1642
|
let options = { method: "GET", headers: headers };
|
|
1492
1643
|
// make tenant endpoint call
|
|
@@ -1530,11 +1681,11 @@ export async function tenantRelationshipsGetById(loggedInUser: User, tenant: Ten
|
|
|
1530
1681
|
console.log("**** tenantRelationshipsGetById");
|
|
1531
1682
|
if (debug) debugger;
|
|
1532
1683
|
// if needed, retrieve and cache access token
|
|
1533
|
-
if (loggedInUser.
|
|
1684
|
+
if (loggedInUser.graphAccessToken === "") {
|
|
1534
1685
|
try {
|
|
1535
|
-
let response: AuthenticationResult = await instance.acquireTokenByCode({ code: loggedInUser.spacode });
|
|
1536
|
-
loggedInUser.
|
|
1537
|
-
console.log("tenantRelationshipsGetById: Front end token acquired: " + loggedInUser.
|
|
1686
|
+
let response: AuthenticationResult = await instance.acquireTokenByCode({ code: loggedInUser.spacode, scopes: ["user.read", "contacts.read", "CrossTenantInformation.ReadBasic.All"] });
|
|
1687
|
+
loggedInUser.graphAccessToken = response.accessToken; // cache access token
|
|
1688
|
+
console.log("tenantRelationshipsGetById: Front end token acquired: " + loggedInUser.graphAccessToken.slice(0, 20));
|
|
1538
1689
|
}
|
|
1539
1690
|
catch (error: any) {
|
|
1540
1691
|
console.log("tenantRelationshipsGetById: Front end token failure: " + error);
|
|
@@ -1543,7 +1694,7 @@ export async function tenantRelationshipsGetById(loggedInUser: User, tenant: Ten
|
|
|
1543
1694
|
}
|
|
1544
1695
|
// prepare Authorization headers as part of options
|
|
1545
1696
|
const headers = new Headers();
|
|
1546
|
-
const bearer = `Bearer ${loggedInUser.
|
|
1697
|
+
const bearer = `Bearer ${loggedInUser.graphAccessToken}`;
|
|
1547
1698
|
headers.append("Authorization", bearer);
|
|
1548
1699
|
let options = { method: "GET", headers: headers };
|
|
1549
1700
|
// make tenant endpoint call
|
|
@@ -1600,7 +1751,7 @@ export async function tenantUnauthenticatedLookup(tenant: Tenant, debug: boolean
|
|
|
1600
1751
|
openidEndpoint += "/.well-known/openid-configuration";
|
|
1601
1752
|
console.log("Attempting GET from openid well-known endpoint: ", openidEndpoint);
|
|
1602
1753
|
response = await fetch(openidEndpoint);
|
|
1603
|
-
if (response.status == 200
|
|
1754
|
+
if (response.status == 200) {
|
|
1604
1755
|
let data = await response.json();
|
|
1605
1756
|
if (data) {
|
|
1606
1757
|
// store tenant ID and authority
|
|
@@ -1640,7 +1791,7 @@ export async function userDelegatedScopesGet(instance: IPublicClientApplication,
|
|
|
1640
1791
|
return { scopes: null, id: null, error: `500: invalid parameter(s) passed to getUserDelegatedScopes` };
|
|
1641
1792
|
}
|
|
1642
1793
|
// create headers
|
|
1643
|
-
const headers = await
|
|
1794
|
+
const headers = await graphDefineHeaders(instance, loggedInUser);
|
|
1644
1795
|
let options: RequestInit = { method: "GET", headers: headers };
|
|
1645
1796
|
try {
|
|
1646
1797
|
// first, cache Graph resource ID (service principal) for this tenant if we don't have it already
|
|
@@ -1696,12 +1847,12 @@ export async function userDelegatedScopesRemove(instance: IPublicClientApplicati
|
|
|
1696
1847
|
export async function usersGet(instance: IPublicClientApplication, user: User | undefined): Promise<{ users: string[], error: string }> {
|
|
1697
1848
|
// need a logged in user to get graph users
|
|
1698
1849
|
if (user == null || user.spacode == "") {
|
|
1699
|
-
return { users: [], error: `500: invalid user passed to
|
|
1850
|
+
return { users: [], error: `500: invalid user passed to usersGet` };
|
|
1700
1851
|
}
|
|
1701
1852
|
// make /users endpoint call
|
|
1702
1853
|
try {
|
|
1703
1854
|
// create headers
|
|
1704
|
-
const headers = await
|
|
1855
|
+
const headers = await graphDefineHeaders(instance, user);
|
|
1705
1856
|
let options = { method: "GET", headers: headers };
|
|
1706
1857
|
let usersEndpoint = getGraphEndpoint(user.authority);
|
|
1707
1858
|
usersEndpoint += graphConfig.graphUsersPredicate;
|
|
@@ -1796,7 +1947,8 @@ export async function configsRefresh(instance: IPublicClientApplication, authori
|
|
|
1796
1947
|
export async function initGet(instance: IPublicClientApplication, user: User, ii: InitInfo, tasks: TaskArray, debug: boolean): Promise<APIResult> {
|
|
1797
1948
|
console.log(`>>>>>> initGet`);
|
|
1798
1949
|
let result: APIResult = new APIResult();
|
|
1799
|
-
if (debug)
|
|
1950
|
+
if (debug)
|
|
1951
|
+
debugger;
|
|
1800
1952
|
// lookup authority for this user (the lookup call does it based on domain, but TID works as well to find authority)
|
|
1801
1953
|
let tenant: Tenant = new Tenant();
|
|
1802
1954
|
tenant.tid = user.tid;
|
|
@@ -2087,4 +2239,146 @@ async function workspaceInfoGet(instance: IPublicClientApplication, user: User,
|
|
|
2087
2239
|
result.result = false;
|
|
2088
2240
|
result.status = 500;
|
|
2089
2241
|
return result;
|
|
2242
|
+
}
|
|
2243
|
+
// ======================= Azure REST API ===============================
|
|
2244
|
+
// TODO: this is where you want to trigger a re-authentication if token expires
|
|
2245
|
+
async function azureDefineHeaders(
|
|
2246
|
+
instance: IPublicClientApplication,
|
|
2247
|
+
user: User
|
|
2248
|
+
): Promise<Headers> {
|
|
2249
|
+
const headers = new Headers();
|
|
2250
|
+
headers.append("Content-Type", "application/json");
|
|
2251
|
+
headers.append("accept", "*/*");
|
|
2252
|
+
// authorization header - if needed, retrieve and cache access token
|
|
2253
|
+
if (user.azureAccessToken == null || user.azureAccessToken === "") {
|
|
2254
|
+
try {
|
|
2255
|
+
let accounts: AccountInfo[] = instance.getAllAccounts();
|
|
2256
|
+
let homeAccountId = user.oid + "." + user.tid;
|
|
2257
|
+
let account: AccountInfo = null;
|
|
2258
|
+
for (let i: number = 0; i < accounts.length; i++) {
|
|
2259
|
+
if (accounts[i].homeAccountId == homeAccountId) {
|
|
2260
|
+
account = accounts[i];
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
debugger;
|
|
2264
|
+
let response: AuthenticationResult = await instance.acquireTokenSilent({
|
|
2265
|
+
scopes: ["https://management.azure.com/user_impersonation"],
|
|
2266
|
+
account: account
|
|
2267
|
+
});
|
|
2268
|
+
user.azureAccessToken = response.accessToken; // cache access token
|
|
2269
|
+
console.log("Front end token acquired silently: " + user.azureAccessToken.slice(0, 20));
|
|
2270
|
+
}
|
|
2271
|
+
catch (error: any) {
|
|
2272
|
+
try {
|
|
2273
|
+
console.log("Front end token silent acquisition failure: " + error);
|
|
2274
|
+
// fallback to redirect if silent acquisition fails
|
|
2275
|
+
let accounts: AccountInfo[] = instance.getAllAccounts();
|
|
2276
|
+
let homeAccountId = user.oid + "." + user.tid;
|
|
2277
|
+
let account: AccountInfo = null;
|
|
2278
|
+
for (let i: number = 0; i < accounts.length; i++) {
|
|
2279
|
+
if (accounts[i].homeAccountId == homeAccountId) {
|
|
2280
|
+
account = accounts[i];
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
instance.acquireTokenRedirect({
|
|
2284
|
+
scopes: ["https://management.azure.com/user_impersonation"],
|
|
2285
|
+
account: account
|
|
2286
|
+
});
|
|
2287
|
+
}
|
|
2288
|
+
catch (error: any) {
|
|
2289
|
+
console.log("Front end token popup acquisition failure: " + error);
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
headers.append("Authorization", `Bearer ${user.azureAccessToken}`);
|
|
2294
|
+
return headers;
|
|
2295
|
+
}
|
|
2296
|
+
export async function canListRootAssignments(instance: IPublicClientApplication, user: User): Promise<boolean> {
|
|
2297
|
+
// need a logged in user to call Azure REST API
|
|
2298
|
+
if (user == null || user.spacode == "") {
|
|
2299
|
+
return false;
|
|
2300
|
+
}
|
|
2301
|
+
// make Azure REST API call
|
|
2302
|
+
try {
|
|
2303
|
+
// create headers
|
|
2304
|
+
const headers = await azureDefineHeaders(instance, user);
|
|
2305
|
+
let options = { method: "GET", headers: headers };
|
|
2306
|
+
let listrootassignmentsEndpoint: string = azureConfig.azureListRootAssignments;
|
|
2307
|
+
listrootassignmentsEndpoint += "'";
|
|
2308
|
+
listrootassignmentsEndpoint += user.oid;
|
|
2309
|
+
listrootassignmentsEndpoint += "'";
|
|
2310
|
+
let response = await fetch(listrootassignmentsEndpoint, options);
|
|
2311
|
+
if (response.status == 200) {
|
|
2312
|
+
let data = await response.json();
|
|
2313
|
+
debugger;
|
|
2314
|
+
console.log("Successful call to Azure Resource Graph list root assignments");
|
|
2315
|
+
}
|
|
2316
|
+
else {
|
|
2317
|
+
console.log(await processErrors(response));
|
|
2318
|
+
return false;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
catch (error: any) {
|
|
2322
|
+
console.log(error);
|
|
2323
|
+
return false;
|
|
2324
|
+
}
|
|
2325
|
+
return true;
|
|
2326
|
+
}
|
|
2327
|
+
export async function elevateGlobalAdminToUserAccessAdmin(instance: IPublicClientApplication, user: User): Promise<boolean> {
|
|
2328
|
+
// need a logged in user to call Azure REST API
|
|
2329
|
+
if (user == null || user.spacode == "") {
|
|
2330
|
+
return false;
|
|
2331
|
+
}
|
|
2332
|
+
// make Azure REST API call
|
|
2333
|
+
try {
|
|
2334
|
+
// create headers
|
|
2335
|
+
const headers = await azureDefineHeaders(instance, user);
|
|
2336
|
+
let options = { method: "POST", headers: headers };
|
|
2337
|
+
let elevateaccessEndpoint: string = azureConfig.azureElevateAccess;
|
|
2338
|
+
let response = await fetch(elevateaccessEndpoint, options);
|
|
2339
|
+
if (response.status == 200) {
|
|
2340
|
+
console.log("Successful call to Azure Resource Graph list root assignments");
|
|
2341
|
+
}
|
|
2342
|
+
else {
|
|
2343
|
+
console.log(await processErrors(response));
|
|
2344
|
+
return false;
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
catch (error: any) {
|
|
2348
|
+
console.log(error);
|
|
2349
|
+
return false;
|
|
2350
|
+
}
|
|
2351
|
+
return true;
|
|
2352
|
+
}
|
|
2353
|
+
async function readResources(instance: IPublicClientApplication, user: User): Promise<ResourceNode[]> {
|
|
2354
|
+
// need a logged in user to call Azure REST API
|
|
2355
|
+
let resources: ResourceNode[] = new Array<ResourceNode>();
|
|
2356
|
+
if (user == null || user.spacode == "") {
|
|
2357
|
+
return resources;
|
|
2358
|
+
}
|
|
2359
|
+
// make Azure REST API call
|
|
2360
|
+
try {
|
|
2361
|
+
// create headers
|
|
2362
|
+
const headers = await azureDefineHeaders(instance, user);
|
|
2363
|
+
let options = { method: "GET", headers: headers };
|
|
2364
|
+
let listrootassignmentsEndpoint: string = azureConfig.azureListRootAssignments;
|
|
2365
|
+
listrootassignmentsEndpoint += "'";
|
|
2366
|
+
listrootassignmentsEndpoint += user.oid;
|
|
2367
|
+
listrootassignmentsEndpoint += "'";
|
|
2368
|
+
let response = await fetch(listrootassignmentsEndpoint, options);
|
|
2369
|
+
if (response.status == 200) {
|
|
2370
|
+
let data = await response.json();
|
|
2371
|
+
debugger;
|
|
2372
|
+
console.log("Successful call to Azure Resource Graph list root assignments");
|
|
2373
|
+
}
|
|
2374
|
+
else {
|
|
2375
|
+
console.log(await processErrors(response));
|
|
2376
|
+
return false;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
catch (error: any) {
|
|
2380
|
+
console.log(error);
|
|
2381
|
+
return false;
|
|
2382
|
+
}
|
|
2383
|
+
return true;
|
|
2090
2384
|
}
|
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.75",
|
|
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/resources.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"type": "mg",
|
|
4
|
+
"resource": "Tenant Root Group",
|
|
5
|
+
"cost": 0,
|
|
6
|
+
"expanded": true,
|
|
7
|
+
"resources": [
|
|
8
|
+
{
|
|
9
|
+
"type": "sub",
|
|
10
|
+
"resource": "Applications",
|
|
11
|
+
"cost": 16677.52,
|
|
12
|
+
"expanded": true,
|
|
13
|
+
"resources": [
|
|
14
|
+
{
|
|
15
|
+
"type": "rg",
|
|
16
|
+
"resource": "ssfdev",
|
|
17
|
+
"cost": 7500.08,
|
|
18
|
+
"expanded": true,
|
|
19
|
+
"resources": [
|
|
20
|
+
{
|
|
21
|
+
"type": "resources",
|
|
22
|
+
"resource": "SSFServices",
|
|
23
|
+
"cost": 0,
|
|
24
|
+
"expanded": false,
|
|
25
|
+
"resources": [
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"type": "sub",
|
|
34
|
+
"resource": "Infrastructure",
|
|
35
|
+
"cost": 8737.58,
|
|
36
|
+
"expanded": true,
|
|
37
|
+
"resources": [
|
|
38
|
+
{
|
|
39
|
+
"type": "rg",
|
|
40
|
+
"resource": "SFFA_Prod_Mgmt_Shared_Resources_RG",
|
|
41
|
+
"cost": 7500.08,
|
|
42
|
+
"expanded": true,
|
|
43
|
+
"resources": [
|
|
44
|
+
{
|
|
45
|
+
"type": "resources",
|
|
46
|
+
"resource": "SSFA-Prod-UTIL-01",
|
|
47
|
+
"cost": 0,
|
|
48
|
+
"expanded": false,
|
|
49
|
+
"resources": [
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
]
|
|
Binary file
|