@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 CHANGED
@@ -16,6 +16,6 @@
16
16
  "targetGroup": "TODO"
17
17
  }
18
18
  ],
19
- "enabled": "false"
19
+ "enabled": ""
20
20
  }
21
21
  ]
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 "./index.js";
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
- expect(InitGet(u, ii)).toBe(true);
11
- expect(ii.us.length).toBe(6);
9
+ let bResult:boolean = InitGet(ii, null);
10
+ expect(bResult);
12
11
  });
@@ -11,7 +11,18 @@ export function helloNpm() {
11
11
  const FILTER_FIELD = "workspaceIDs";
12
12
 
13
13
  export class User {
14
- constructor(){
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
- constructor() {
64
- this.us = {};
65
- this.ts = {};
66
- this.cs = {};
67
- this.ws = {};
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
- initWorkspaceIDs() {
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
- // retrieve Workspace(s), User(s), Target(s), Config(s) given logged in user
127
- export function InitGet(user, ii)
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.18",
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.js",
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: TargetType; // always AAD for now
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 confiure multiple source groups for reading (*may* slow things down, but less work for admin)
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(u: User, ii: InitInfo): boolean;
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;