@mindline/sync 1.0.18 → 1.0.20
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/configs.json +1 -1
- package/hybridspa.ts +126 -0
- package/{index.test.js → index.test.ts} +3 -4
- package/{index.js → index.ts} +108 -15
- package/package.json +4 -2
- package/sync.d.ts +6 -4
package/configs.json
CHANGED
package/hybridspa.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { IPublicClientApplication, AuthenticationResult } from '@azure/msal-browser';
|
|
2
|
+
import { User } from '@mindline/sync';
|
|
3
|
+
|
|
4
|
+
// Add here the endpoints for MS Graph API services you would like to use.
|
|
5
|
+
const graphConfig = {
|
|
6
|
+
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me",
|
|
7
|
+
graphMailEndpoint: "https://graph.microsoft.com/v1.0/me/messages",
|
|
8
|
+
graphTenantEndpoint:
|
|
9
|
+
"https://graph.microsoft.com/beta/tenantRelationships/findTenantInformationByTenantId",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function updateUI(data, endpoint) {
|
|
13
|
+
console.log("Graph API responded at: " + new Date().toString());
|
|
14
|
+
|
|
15
|
+
if (endpoint === graphConfig.graphMeEndpoint) {
|
|
16
|
+
console.log(data.jobTitle);
|
|
17
|
+
console.log(data.mail);
|
|
18
|
+
console.log(data.businessPhones[0]);
|
|
19
|
+
console.log(data.officeLocation);
|
|
20
|
+
} else if (endpoint === graphConfig.graphMailEndpoint) {
|
|
21
|
+
if (data.value.length < 1) {
|
|
22
|
+
alert("Your mailbox is empty!");
|
|
23
|
+
} else {
|
|
24
|
+
console.log("Displaying Emails for the signed in user.");
|
|
25
|
+
data.value.map((d, i) => {
|
|
26
|
+
// Keeping it simple
|
|
27
|
+
if (i < 10) {
|
|
28
|
+
console.log(data.officeLocation);
|
|
29
|
+
console.log(d.subject);
|
|
30
|
+
console.log(d.from.emailAddress.address);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Helper function to call MS Graph API endpoint
|
|
38
|
+
// using authorization bearer token scheme
|
|
39
|
+
function callMSGraph(endpoint, token, callback) {
|
|
40
|
+
const headers = new Headers();
|
|
41
|
+
const bearer = `Bearer ${token}`;
|
|
42
|
+
headers.append("Authorization", bearer);
|
|
43
|
+
|
|
44
|
+
const options = {
|
|
45
|
+
method: "GET",
|
|
46
|
+
headers: headers,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
console.log("request made to Graph API at: " + new Date().toString());
|
|
50
|
+
|
|
51
|
+
fetch(endpoint, options)
|
|
52
|
+
.then((response) => response.json())
|
|
53
|
+
.then((response) => callback(response, endpoint))
|
|
54
|
+
.then((result) => {
|
|
55
|
+
console.log("Successfully Fetched Data from Graph API:", result);
|
|
56
|
+
})
|
|
57
|
+
.catch((error) => console.log(error));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
///get Token
|
|
61
|
+
function getTokenByCode(spaCode) {
|
|
62
|
+
var code = spaCode;
|
|
63
|
+
const scopes = ["user.read"];
|
|
64
|
+
|
|
65
|
+
console.log("MSAL: acquireTokenByCode hybrid parameters present");
|
|
66
|
+
|
|
67
|
+
var authResult = msalInstance.acquireTokenByCode({
|
|
68
|
+
code,
|
|
69
|
+
scopes,
|
|
70
|
+
});
|
|
71
|
+
console.log(authResult);
|
|
72
|
+
|
|
73
|
+
return authResult;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
//See Profile
|
|
77
|
+
function seeProfile(spaCode) {
|
|
78
|
+
getTokenByCode(spaCode)
|
|
79
|
+
.then((response) => {
|
|
80
|
+
callMSGraph(graphConfig.graphMeEndpoint, response.accessToken, updateUI);
|
|
81
|
+
})
|
|
82
|
+
.catch((error) => {
|
|
83
|
+
console.log(error);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
//Get Tenant Info
|
|
88
|
+
export function getTenantInfo(user: User, instance: IPublicClientApplication) {
|
|
89
|
+
debugger;
|
|
90
|
+
|
|
91
|
+
let authResult: AuthenticationResult;
|
|
92
|
+
instance
|
|
93
|
+
.acquireTokenByCode({ code: user.spacode })
|
|
94
|
+
.then((result) => (authResult = result))
|
|
95
|
+
.catch((e: any) => {
|
|
96
|
+
console.log(e);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const headers = new Headers();
|
|
100
|
+
const bearer = `Bearer ${authResult.accessToken}`;
|
|
101
|
+
headers.append("Authorization", bearer);
|
|
102
|
+
|
|
103
|
+
const options = {
|
|
104
|
+
method: "GET",
|
|
105
|
+
headers: headers,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
var tenantEndpoint = graphConfig.graphTenantEndpoint;
|
|
109
|
+
tenantEndpoint += "(tenantId='";
|
|
110
|
+
tenantEndpoint += user.tid;
|
|
111
|
+
tenantEndpoint += "')";
|
|
112
|
+
|
|
113
|
+
console.log(
|
|
114
|
+
"request made to Graph API tenant endpoint at: " + new Date().toString()
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
fetch(tenantEndpoint, options)
|
|
118
|
+
.then((response) => response.json())
|
|
119
|
+
.then((response) => {
|
|
120
|
+
console.log("Successfully Fetched Data from Graph API:", response);
|
|
121
|
+
})
|
|
122
|
+
.catch((error) => console.log(error));
|
|
123
|
+
|
|
124
|
+
user.companyName = response.displayName;
|
|
125
|
+
user.companyDomain = response.defaultDomainName;
|
|
126
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {sum, User, InitInfo, InitGet } from "
|
|
1
|
+
import {sum, User, InitInfo, InitGet } from "index";
|
|
2
2
|
import {test, expect} from "vitest";
|
|
3
3
|
|
|
4
4
|
test("adds 1 + 2 to equal 3", () => {
|
|
5
5
|
expect(sum(1, 2)).toBe(3);
|
|
6
6
|
});
|
|
7
7
|
test("loads config based on a user and expects function to return true", () => {
|
|
8
|
-
let u = new User();
|
|
9
8
|
let ii = new InitInfo();
|
|
10
|
-
|
|
11
|
-
expect(
|
|
9
|
+
let bResult:boolean = InitGet(ii, null);
|
|
10
|
+
expect(bResult);
|
|
12
11
|
});
|
package/{index.js → index.ts}
RENAMED
|
@@ -11,7 +11,18 @@ export function helloNpm() {
|
|
|
11
11
|
const FILTER_FIELD = "workspaceIDs";
|
|
12
12
|
|
|
13
13
|
export class User {
|
|
14
|
-
|
|
14
|
+
oid: string;
|
|
15
|
+
name: string;
|
|
16
|
+
mail: string;
|
|
17
|
+
authority: string;
|
|
18
|
+
tid: string;
|
|
19
|
+
companyName: string;
|
|
20
|
+
companyDomain: string;
|
|
21
|
+
associatedWorkspaces: string[];
|
|
22
|
+
workspaceIDs: string;
|
|
23
|
+
session: string;
|
|
24
|
+
spacode: string;
|
|
25
|
+
constructor() {
|
|
15
26
|
this.oid = "";
|
|
16
27
|
this.name = "";
|
|
17
28
|
this.mail = "";
|
|
@@ -19,13 +30,22 @@ export class User {
|
|
|
19
30
|
this.tid = "";
|
|
20
31
|
this.companyName = "";
|
|
21
32
|
this.companyDomain = "";
|
|
22
|
-
this.associatedWorkspaces =
|
|
33
|
+
this.associatedWorkspaces = new Array();
|
|
23
34
|
this.workspaceIDs = "";
|
|
24
35
|
this.session = "";
|
|
36
|
+
this.spacode = "";
|
|
25
37
|
}
|
|
26
38
|
}
|
|
27
39
|
|
|
28
40
|
export class Target {
|
|
41
|
+
tid: string;
|
|
42
|
+
name: string;
|
|
43
|
+
domain: string;
|
|
44
|
+
type: string;
|
|
45
|
+
authority: string;
|
|
46
|
+
readServicePrincipal: string;
|
|
47
|
+
writeServicePrincipal: string;
|
|
48
|
+
workspaceIDs: string;
|
|
29
49
|
constructor(){
|
|
30
50
|
this.tid = "";
|
|
31
51
|
this.name = "";
|
|
@@ -38,35 +58,56 @@ export class Target {
|
|
|
38
58
|
}
|
|
39
59
|
}
|
|
40
60
|
|
|
61
|
+
export class TargetConfigInfo {
|
|
62
|
+
tid: string;
|
|
63
|
+
sourceGroups: string[];
|
|
64
|
+
targetGroup: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
export class Config {
|
|
68
|
+
id: string;
|
|
69
|
+
name: string;
|
|
70
|
+
description: string;
|
|
71
|
+
targetConfigs: TargetConfigInfo[];
|
|
72
|
+
enabled: boolean;
|
|
73
|
+
workspaceIDs: string;
|
|
42
74
|
constructor(){
|
|
43
75
|
this.id = "";
|
|
44
76
|
this.name = "";
|
|
45
77
|
this.description = "";
|
|
46
|
-
this.targetConfigs =
|
|
78
|
+
this.targetConfigs = new Array();
|
|
47
79
|
this.enabled = false;
|
|
48
80
|
this.workspaceIDs = "";
|
|
49
81
|
}
|
|
50
82
|
}
|
|
51
83
|
|
|
52
84
|
export class Workspace {
|
|
85
|
+
id: string;
|
|
86
|
+
name: string;
|
|
87
|
+
associatedUsers: string[];
|
|
88
|
+
associatedTargets: string[];
|
|
89
|
+
associatedConfigs: string[];
|
|
53
90
|
constructor(){
|
|
54
91
|
this.id = "";
|
|
55
92
|
this.name = "";
|
|
56
|
-
this.associatedUsers =
|
|
57
|
-
this.associatedTargets =
|
|
58
|
-
this.associatedConfigs =
|
|
93
|
+
this.associatedUsers = new Array();
|
|
94
|
+
this.associatedTargets = new Array();
|
|
95
|
+
this.associatedConfigs = new Array();
|
|
59
96
|
}
|
|
60
97
|
}
|
|
61
98
|
|
|
62
99
|
export class InitInfo {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
100
|
+
us: User[];
|
|
101
|
+
ts: Target[];
|
|
102
|
+
cs: Config[];
|
|
103
|
+
ws: Workspace[];
|
|
104
|
+
constructor(){
|
|
105
|
+
this.us = new Array();
|
|
106
|
+
this.ts = new Array();
|
|
107
|
+
this.cs = new Array();
|
|
108
|
+
this.ws = new Array();
|
|
68
109
|
}
|
|
69
|
-
|
|
110
|
+
tagWithWorkspaces(): boolean {
|
|
70
111
|
// for each Workspace tag associated User, Target, Config with Workspace.id
|
|
71
112
|
for (let workspace of this.ws) {
|
|
72
113
|
// find matching Users to tag with this workspace
|
|
@@ -118,15 +159,16 @@ export class InitInfo {
|
|
|
118
159
|
}
|
|
119
160
|
|
|
120
161
|
import { deserializeArray } from 'class-transformer';
|
|
162
|
+
import { IPublicClientApplication } from '@azure/msal-browser';
|
|
163
|
+
import { getTenantInfo } from './hybridspa';
|
|
121
164
|
import users from "./users.json";
|
|
122
165
|
import targets from "./targets.json";
|
|
123
166
|
import configs from "./configs.json";
|
|
124
167
|
import workspaces from "./workspaces.json";
|
|
125
168
|
|
|
126
|
-
//
|
|
127
|
-
|
|
169
|
+
// get hardcoded data from JSON
|
|
170
|
+
function DummyInit(ii)
|
|
128
171
|
{
|
|
129
|
-
// for now, just get hardcoded data from JSON
|
|
130
172
|
var usersString = JSON.stringify(users);
|
|
131
173
|
var targetsString = JSON.stringify(targets);
|
|
132
174
|
var configsString = JSON.stringify(configs);
|
|
@@ -143,6 +185,57 @@ export function InitGet(user, ii)
|
|
|
143
185
|
}
|
|
144
186
|
return true;
|
|
145
187
|
}
|
|
188
|
+
// retrieve Workspace(s), User(s), Target(s), Config(s) given logged in user
|
|
189
|
+
// three InitGet scenarios
|
|
190
|
+
// scenario 1: empty user array, read defaults
|
|
191
|
+
// scenario 2: user array with dummy user at end, return without doing anything
|
|
192
|
+
// scenario 3: user array with non-dummy user at end, call back end Init
|
|
193
|
+
// (1) TODO: Azure AD: lookup companyDomain and companyName
|
|
194
|
+
// look up tenant name
|
|
195
|
+
// look up tenant domain
|
|
196
|
+
// TODO: Mindline: post complete user to init endpoint
|
|
197
|
+
// create and push partial tenant at end of target array
|
|
198
|
+
// post user and partial tenant to InitInfo
|
|
199
|
+
// Return: success return value, need to query for associations
|
|
200
|
+
// (2) Sync NPM: ii.ws: once InitInfo completes, retrieve workspaces for the new user
|
|
201
|
+
// TODO: Mindline: retrieve associated workspaces
|
|
202
|
+
// Returns: associated workspaces
|
|
203
|
+
// (3) Sync NPM:
|
|
204
|
+
// TODO: Mindline: retrieve associated admins, targets for each workspace
|
|
205
|
+
// ii.us / ii.ts / ii.cs: query components of each associated workspaces
|
|
206
|
+
// Returns: users, targets, configs for each workspace
|
|
207
|
+
export function InitGet(ii: InitInfo, instance: IPublicClientApplication)
|
|
208
|
+
{
|
|
209
|
+
debugger;
|
|
210
|
+
|
|
211
|
+
// if empty user array, retrieve dummy data from JSON to populate UI
|
|
212
|
+
let l = ii.us.length;
|
|
213
|
+
if(l === 0) return DummyInit(ii);
|
|
214
|
+
|
|
215
|
+
// if last user is a dummy user, then we have nothing
|
|
216
|
+
let user:User = ii.us[l-1];
|
|
217
|
+
if(user.oid === "1") return true;
|
|
218
|
+
|
|
219
|
+
// have real user: remove dummy user, target, config, workspace if they exist
|
|
220
|
+
let dummyIndex = ii.us.findIndex((u) => u.oid === "1");
|
|
221
|
+
ii.us.splice(dummyIndex, 1);
|
|
222
|
+
dummyIndex = ii.ts.findIndex((u) => u.tid === "1");
|
|
223
|
+
ii.ts.splice(dummyIndex, 1);
|
|
224
|
+
dummyIndex = ii.cs.findIndex((u) => u.id === "1");
|
|
225
|
+
ii.cs.splice(dummyIndex, 1);
|
|
226
|
+
dummyIndex = ii.ws.findIndex((u) => u.id === "1");
|
|
227
|
+
ii.ws.splice(dummyIndex, 1);
|
|
228
|
+
|
|
229
|
+
// why would instance be null here? investigate!
|
|
230
|
+
if(instance===null) {
|
|
231
|
+
debugger;
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// valid user, query AAD for associated company name and domain
|
|
236
|
+
getTenantInfo(user, instance);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
146
239
|
|
|
147
240
|
export function AddTarget()
|
|
148
241
|
{
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindline/sync",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.20",
|
|
5
5
|
"description": "sync is a node.js package encapsulating javscript classes required for configuring Mindline sync service.",
|
|
6
|
-
"exports": "./index.
|
|
6
|
+
"exports": "./index.ts",
|
|
7
|
+
"main": "./index.ts",
|
|
7
8
|
"scripts": {
|
|
8
9
|
"test": "vitest",
|
|
9
10
|
"coverage": "vitest run --coverage"
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"vitest": "^0.29.8"
|
|
16
17
|
},
|
|
17
18
|
"dependencies": {
|
|
19
|
+
"@azure/msal-browser": "^2.37.0",
|
|
18
20
|
"class-transformer": "^0.5.1",
|
|
19
21
|
"reflect-metadata": "^0.1.13"
|
|
20
22
|
},
|
package/sync.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { IPublicClientApplication } from "@azure/msal-browser";
|
|
2
|
+
|
|
1
3
|
declare module "@mindline/sync" {
|
|
2
4
|
export function sum(a: number, b: number): number;
|
|
3
5
|
export function helloNpm(): string;
|
|
@@ -14,15 +16,15 @@ declare module "@mindline/sync" {
|
|
|
14
16
|
associatedWorkspaces: string[];
|
|
15
17
|
workspaceIDs: string;
|
|
16
18
|
session: string;
|
|
19
|
+
spacode: string;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
// target (Azure AD tenant, AD domain, Google workspace)
|
|
20
|
-
enum TargetType { AAD = 1, AD, Google }
|
|
21
23
|
export class Target {
|
|
22
24
|
tid: string; // from AAD ID token
|
|
23
25
|
name: string; // findTenantInformationByTenantId
|
|
24
26
|
domain: string; // findTenantInformationByTenantId
|
|
25
|
-
type:
|
|
27
|
+
type: string; // always AAD=1 for now
|
|
26
28
|
authority: string; // from AAD ID auth response
|
|
27
29
|
readServicePrincipal: string; // from AAD consent
|
|
28
30
|
writeServicePrincipal: string; // from AAD consent
|
|
@@ -32,7 +34,7 @@ declare module "@mindline/sync" {
|
|
|
32
34
|
// config
|
|
33
35
|
export class TargetConfigInfo {
|
|
34
36
|
tid: string; // target identifier
|
|
35
|
-
sourceGroups: string[]; // source groups - we can
|
|
37
|
+
sourceGroups: string[]; // source groups - we can configure multiple source groups for reading (*may* slow things down, but less work for admin)
|
|
36
38
|
targetGroup: string; // target group - we only write users to a single target group (complex to fiugure out which users get written to which groups)
|
|
37
39
|
}
|
|
38
40
|
export class Config {
|
|
@@ -61,7 +63,7 @@ declare module "@mindline/sync" {
|
|
|
61
63
|
tagWithWorkspaces(): boolean;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
export function InitGet(
|
|
66
|
+
export function InitGet(ii: InitInfo, instance: IPublicClientApplication): boolean;
|
|
65
67
|
export function AddTarget(): boolean;
|
|
66
68
|
export function CompleteTarget(): boolean;
|
|
67
69
|
export function AddUser(): boolean;
|