@jskit-ai/users-core 0.1.32 → 0.1.35
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/package.descriptor.mjs +16 -245
- package/package.json +7 -7
- package/src/server/UsersCoreServiceProvider.js +4 -28
- package/src/server/UsersWorkspacesServiceProvider.js +44 -0
- package/src/server/accountNotifications/accountNotificationsService.js +3 -3
- package/src/server/accountNotifications/registerAccountNotifications.js +1 -1
- package/src/server/accountPreferences/accountPreferencesService.js +3 -3
- package/src/server/accountPreferences/registerAccountPreferences.js +1 -1
- package/src/server/accountProfile/accountProfileActions.js +8 -2
- package/src/server/accountProfile/accountProfileService.js +10 -10
- package/src/server/accountProfile/avatarService.js +9 -9
- package/src/server/accountProfile/bootAccountProfileRoutes.js +5 -3
- package/src/server/accountProfile/registerAccountProfile.js +2 -2
- package/src/server/accountSecurity/accountSecurityService.js +3 -3
- package/src/server/accountSecurity/registerAccountSecurity.js +1 -1
- package/src/server/common/contributors/workspaceActionContextContributor.js +24 -17
- package/src/server/common/registerCommonRepositories.js +3 -22
- package/src/server/common/repositories/userSettingsRepository.js +1 -12
- package/src/server/common/repositories/{userProfilesRepository.js → usersRepository.js} +1 -1
- package/src/server/common/services/accountContextService.js +4 -4
- package/src/server/common/services/authProfileSyncService.js +10 -10
- package/src/server/registerUsersBootstrap.js +22 -0
- package/src/server/registerUsersCore.js +30 -0
- package/src/server/registerWorkspaceBootstrap.js +3 -6
- package/src/server/registerWorkspaceCore.js +5 -17
- package/src/server/registerWorkspaceRepositories.js +26 -0
- package/src/server/usersBootstrapContributor.js +248 -0
- package/src/server/workspaceBootstrapContributor.js +65 -259
- package/src/shared/roles.js +31 -6
- package/src/shared/settings.js +1 -2
- package/templates/migrations/users_core_generic_initial.cjs +69 -0
- package/test/authProfileSyncService.test.js +3 -3
- package/test/avatarService.test.js +2 -2
- package/test/registerUsersCore.test.js +42 -0
- package/test/roles.test.js +90 -5
- package/test/usersBootstrapContributor.test.js +172 -0
- package/test/usersRouteRequestInputValidator.test.js +7 -390
- package/test/workspaceActionContextContributor.test.js +98 -5
- package/test/workspaceBootstrapContributor.test.js +34 -346
- package/test/workspaceMembersService.test.js +4 -2
- package/test/workspaceService.test.js +12 -8
- package/test/workspaceSettingsResource.test.js +4 -2
- package/test-support/registerDefaultSettingsFields.js +1 -1
- package/templates/config/workspaceRoles.js +0 -30
- package/templates/migrations/users_core_initial.cjs +0 -123
- package/templates/migrations/users_core_workspace_settings_single_name_source.cjs +0 -71
- package/templates/migrations/users_core_workspaces_drop_color.cjs +0 -85
- package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +0 -197
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import { createWorkspaceBootstrapContributor } from "../src/server/workspaceBootstrapContributor.js";
|
|
4
|
-
import {
|
|
5
|
-
TENANCY_MODE_PERSONAL,
|
|
6
|
-
WORKSPACE_SLUG_POLICY_IMMUTABLE_USERNAME
|
|
7
|
-
} from "../src/shared/tenancyProfile.js";
|
|
8
4
|
|
|
9
5
|
function createAuthenticatedProfile(overrides = {}) {
|
|
10
6
|
return {
|
|
@@ -39,27 +35,11 @@ test("workspace bootstrap contributor passes actor context to pending invites se
|
|
|
39
35
|
return [];
|
|
40
36
|
}
|
|
41
37
|
},
|
|
42
|
-
|
|
43
|
-
async
|
|
38
|
+
usersRepository: {
|
|
39
|
+
async findById() {
|
|
44
40
|
return profile;
|
|
45
41
|
}
|
|
46
42
|
},
|
|
47
|
-
userSettingsRepository: {
|
|
48
|
-
async ensureForUserId() {
|
|
49
|
-
return {
|
|
50
|
-
theme: "system",
|
|
51
|
-
locale: "en",
|
|
52
|
-
timeZone: "UTC",
|
|
53
|
-
dateFormat: "YYYY-MM-DD",
|
|
54
|
-
numberFormat: "1,234.56",
|
|
55
|
-
currencyCode: "USD",
|
|
56
|
-
avatarSize: 64,
|
|
57
|
-
productUpdates: true,
|
|
58
|
-
accountActivity: true,
|
|
59
|
-
securityAlerts: true
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
43
|
workspaceInvitationsEnabled: true,
|
|
64
44
|
appConfig: {
|
|
65
45
|
tenancyMode: "workspaces"
|
|
@@ -67,15 +47,12 @@ test("workspace bootstrap contributor passes actor context to pending invites se
|
|
|
67
47
|
});
|
|
68
48
|
|
|
69
49
|
await contributor.contribute({
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
profile
|
|
75
|
-
};
|
|
50
|
+
payload: {
|
|
51
|
+
session: {
|
|
52
|
+
authenticated: true,
|
|
53
|
+
userId: profile.id
|
|
76
54
|
}
|
|
77
|
-
}
|
|
78
|
-
reply: {}
|
|
55
|
+
}
|
|
79
56
|
});
|
|
80
57
|
|
|
81
58
|
assert.equal(pendingServiceCalls.length, 1);
|
|
@@ -83,133 +60,6 @@ test("workspace bootstrap contributor passes actor context to pending invites se
|
|
|
83
60
|
assert.equal(pendingServiceCalls[0].options?.context?.actor?.id, profile.id);
|
|
84
61
|
});
|
|
85
62
|
|
|
86
|
-
test("workspace bootstrap contributor seeds the initial console owner on authenticated bootstrap", async () => {
|
|
87
|
-
const profile = createAuthenticatedProfile({ id: 12 });
|
|
88
|
-
const consoleOwnerSeeds = [];
|
|
89
|
-
|
|
90
|
-
const contributor = createWorkspaceBootstrapContributor({
|
|
91
|
-
workspaceService: {
|
|
92
|
-
async listWorkspacesForUser() {
|
|
93
|
-
return [];
|
|
94
|
-
},
|
|
95
|
-
async resolveWorkspaceContextForUserBySlug() {
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
workspacePendingInvitationsService: {
|
|
100
|
-
async listPendingInvitesForUser() {
|
|
101
|
-
return [];
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
userProfilesRepository: {
|
|
105
|
-
async findByIdentity() {
|
|
106
|
-
return profile;
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
userSettingsRepository: {
|
|
110
|
-
async ensureForUserId() {
|
|
111
|
-
return {
|
|
112
|
-
theme: "system",
|
|
113
|
-
locale: "en",
|
|
114
|
-
timeZone: "UTC",
|
|
115
|
-
dateFormat: "YYYY-MM-DD",
|
|
116
|
-
numberFormat: "1,234.56",
|
|
117
|
-
currencyCode: "USD",
|
|
118
|
-
avatarSize: 64,
|
|
119
|
-
productUpdates: true,
|
|
120
|
-
accountActivity: true,
|
|
121
|
-
securityAlerts: true
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
workspaceInvitationsEnabled: false,
|
|
126
|
-
consoleService: {
|
|
127
|
-
async ensureInitialConsoleMember(userId) {
|
|
128
|
-
consoleOwnerSeeds.push(Number(userId));
|
|
129
|
-
return Number(userId);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
const payload = await contributor.contribute({
|
|
135
|
-
request: {
|
|
136
|
-
async executeAction() {
|
|
137
|
-
return {
|
|
138
|
-
authenticated: true,
|
|
139
|
-
profile
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
reply: {}
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
assert.deepEqual(consoleOwnerSeeds, [12]);
|
|
147
|
-
assert.equal(payload.surfaceAccess?.consoleowner, true);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test("workspace bootstrap contributor emits canonical tenancy profile from users-core", async () => {
|
|
151
|
-
const contributor = createWorkspaceBootstrapContributor({
|
|
152
|
-
workspaceService: {
|
|
153
|
-
async listWorkspacesForUser() {
|
|
154
|
-
return [];
|
|
155
|
-
},
|
|
156
|
-
async resolveWorkspaceContextForUserBySlug() {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
workspacePendingInvitationsService: {
|
|
161
|
-
async listPendingInvitesForUser() {
|
|
162
|
-
return [];
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
userProfilesRepository: {
|
|
166
|
-
async findByIdentity() {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
userSettingsRepository: {
|
|
171
|
-
async ensureForUserId() {
|
|
172
|
-
return {};
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
workspaceInvitationsEnabled: false,
|
|
176
|
-
tenancyProfile: {
|
|
177
|
-
mode: TENANCY_MODE_PERSONAL,
|
|
178
|
-
workspace: {
|
|
179
|
-
enabled: true,
|
|
180
|
-
autoProvision: true,
|
|
181
|
-
allowSelfCreate: false,
|
|
182
|
-
slugPolicy: WORKSPACE_SLUG_POLICY_IMMUTABLE_USERNAME
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
appConfig: {
|
|
186
|
-
tenancyMode: "none"
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
const payload = await contributor.contribute({
|
|
191
|
-
request: {
|
|
192
|
-
async executeAction() {
|
|
193
|
-
return {
|
|
194
|
-
authenticated: false
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
reply: {}
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
assert.deepEqual(payload.tenancy, {
|
|
202
|
-
mode: TENANCY_MODE_PERSONAL,
|
|
203
|
-
workspace: {
|
|
204
|
-
enabled: true,
|
|
205
|
-
autoProvision: true,
|
|
206
|
-
allowSelfCreate: false,
|
|
207
|
-
slugPolicy: WORKSPACE_SLUG_POLICY_IMMUTABLE_USERNAME
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
assert.equal(payload.app.tenancyMode, undefined);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
63
|
test("workspace bootstrap contributor resolves workspace slug from bootstrap query", async () => {
|
|
214
64
|
const profile = createAuthenticatedProfile();
|
|
215
65
|
const calls = [];
|
|
@@ -228,163 +78,57 @@ test("workspace bootstrap contributor resolves workspace slug from bootstrap que
|
|
|
228
78
|
return [];
|
|
229
79
|
}
|
|
230
80
|
},
|
|
231
|
-
|
|
232
|
-
async
|
|
81
|
+
usersRepository: {
|
|
82
|
+
async findById() {
|
|
233
83
|
return profile;
|
|
234
84
|
}
|
|
235
85
|
},
|
|
236
|
-
userSettingsRepository: {
|
|
237
|
-
async ensureForUserId() {
|
|
238
|
-
return {
|
|
239
|
-
theme: "system",
|
|
240
|
-
locale: "en",
|
|
241
|
-
timeZone: "UTC",
|
|
242
|
-
dateFormat: "YYYY-MM-DD",
|
|
243
|
-
numberFormat: "1,234.56",
|
|
244
|
-
currencyCode: "USD",
|
|
245
|
-
avatarSize: 64,
|
|
246
|
-
productUpdates: true,
|
|
247
|
-
accountActivity: true,
|
|
248
|
-
securityAlerts: true
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
},
|
|
252
86
|
workspaceInvitationsEnabled: true,
|
|
253
87
|
appConfig: {
|
|
254
88
|
tenancyMode: "workspaces"
|
|
255
89
|
}
|
|
256
90
|
});
|
|
257
91
|
|
|
258
|
-
await contributor.contribute({
|
|
92
|
+
const payload = await contributor.contribute({
|
|
259
93
|
query: {
|
|
260
94
|
workspaceSlug: " AcMe "
|
|
261
95
|
},
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
profile
|
|
267
|
-
};
|
|
96
|
+
payload: {
|
|
97
|
+
session: {
|
|
98
|
+
authenticated: true,
|
|
99
|
+
userId: profile.id
|
|
268
100
|
}
|
|
269
|
-
},
|
|
270
|
-
reply: {}
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
assert.deepEqual(calls, ["acme"]);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
test("workspace bootstrap contributor returns global payload with requestedWorkspace=forbidden when slug access is denied", async () => {
|
|
277
|
-
const profile = createAuthenticatedProfile();
|
|
278
|
-
const contributor = createWorkspaceBootstrapContributor({
|
|
279
|
-
workspaceService: {
|
|
280
|
-
async listWorkspacesForUser() {
|
|
281
|
-
return [{ id: 3, slug: "chiara", name: "Chiara Workspace" }];
|
|
282
|
-
},
|
|
283
|
-
async resolveWorkspaceContextForUserBySlug() {
|
|
284
|
-
const error = new Error("Forbidden.");
|
|
285
|
-
error.status = 403;
|
|
286
|
-
throw error;
|
|
287
|
-
}
|
|
288
|
-
},
|
|
289
|
-
workspacePendingInvitationsService: {
|
|
290
|
-
async listPendingInvitesForUser() {
|
|
291
|
-
return [];
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
userProfilesRepository: {
|
|
295
|
-
async findByIdentity() {
|
|
296
|
-
return profile;
|
|
297
|
-
}
|
|
298
|
-
},
|
|
299
|
-
userSettingsRepository: {
|
|
300
|
-
async ensureForUserId() {
|
|
301
|
-
return {
|
|
302
|
-
theme: "system",
|
|
303
|
-
locale: "en",
|
|
304
|
-
timeZone: "UTC",
|
|
305
|
-
dateFormat: "YYYY-MM-DD",
|
|
306
|
-
numberFormat: "1,234.56",
|
|
307
|
-
currencyCode: "USD",
|
|
308
|
-
avatarSize: 64,
|
|
309
|
-
productUpdates: true,
|
|
310
|
-
accountActivity: true,
|
|
311
|
-
securityAlerts: true
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
},
|
|
315
|
-
workspaceInvitationsEnabled: true,
|
|
316
|
-
appConfig: {
|
|
317
|
-
tenancyMode: "workspaces"
|
|
318
101
|
}
|
|
319
102
|
});
|
|
320
103
|
|
|
321
|
-
|
|
322
|
-
query: {
|
|
323
|
-
workspaceSlug: "tonymobily"
|
|
324
|
-
},
|
|
325
|
-
request: {
|
|
326
|
-
async executeAction() {
|
|
327
|
-
return {
|
|
328
|
-
authenticated: true,
|
|
329
|
-
profile
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
},
|
|
333
|
-
reply: {}
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
assert.equal(payload.session.authenticated, true);
|
|
337
|
-
assert.deepEqual(payload.workspaces, [{ id: 3, slug: "chiara", name: "Chiara Workspace" }]);
|
|
104
|
+
assert.deepEqual(calls, ["acme"]);
|
|
338
105
|
assert.deepEqual(payload.requestedWorkspace, {
|
|
339
|
-
slug: "
|
|
340
|
-
status: "
|
|
106
|
+
slug: "acme",
|
|
107
|
+
status: "resolved"
|
|
341
108
|
});
|
|
342
|
-
assert.equal(payload.activeWorkspace, null);
|
|
343
|
-
assert.equal(payload.membership, null);
|
|
344
|
-
assert.deepEqual(payload.permissions, []);
|
|
345
|
-
assert.equal(payload.workspaceSettings, null);
|
|
346
109
|
});
|
|
347
110
|
|
|
348
|
-
test("workspace bootstrap contributor
|
|
349
|
-
const profile = createAuthenticatedProfile();
|
|
111
|
+
test("workspace bootstrap contributor reports unauthenticated requested workspace without generic bootstrap work", async () => {
|
|
350
112
|
const contributor = createWorkspaceBootstrapContributor({
|
|
351
113
|
workspaceService: {
|
|
352
114
|
async listWorkspacesForUser() {
|
|
353
|
-
|
|
115
|
+
assert.fail("listWorkspacesForUser should not run for unauthenticated payloads");
|
|
354
116
|
},
|
|
355
117
|
async resolveWorkspaceContextForUserBySlug() {
|
|
356
|
-
|
|
357
|
-
error.status = 404;
|
|
358
|
-
throw error;
|
|
118
|
+
assert.fail("resolveWorkspaceContextForUserBySlug should not run for unauthenticated payloads");
|
|
359
119
|
}
|
|
360
120
|
},
|
|
361
121
|
workspacePendingInvitationsService: {
|
|
362
122
|
async listPendingInvitesForUser() {
|
|
363
|
-
|
|
123
|
+
assert.fail("listPendingInvitesForUser should not run for unauthenticated payloads");
|
|
364
124
|
}
|
|
365
125
|
},
|
|
366
|
-
|
|
367
|
-
async
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
},
|
|
371
|
-
userSettingsRepository: {
|
|
372
|
-
async ensureForUserId() {
|
|
373
|
-
return {
|
|
374
|
-
theme: "system",
|
|
375
|
-
locale: "en",
|
|
376
|
-
timeZone: "UTC",
|
|
377
|
-
dateFormat: "YYYY-MM-DD",
|
|
378
|
-
numberFormat: "1,234.56",
|
|
379
|
-
currencyCode: "USD",
|
|
380
|
-
avatarSize: 64,
|
|
381
|
-
productUpdates: true,
|
|
382
|
-
accountActivity: true,
|
|
383
|
-
securityAlerts: true
|
|
384
|
-
};
|
|
126
|
+
usersRepository: {
|
|
127
|
+
async findById() {
|
|
128
|
+
assert.fail("findById should not run for unauthenticated payloads");
|
|
385
129
|
}
|
|
386
130
|
},
|
|
387
|
-
workspaceInvitationsEnabled:
|
|
131
|
+
workspaceInvitationsEnabled: true,
|
|
388
132
|
appConfig: {
|
|
389
133
|
tenancyMode: "workspaces"
|
|
390
134
|
}
|
|
@@ -392,75 +136,19 @@ test("workspace bootstrap contributor returns requestedWorkspace=not_found when
|
|
|
392
136
|
|
|
393
137
|
const payload = await contributor.contribute({
|
|
394
138
|
query: {
|
|
395
|
-
workspaceSlug: "
|
|
139
|
+
workspaceSlug: "AcMe"
|
|
396
140
|
},
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
authenticated: true,
|
|
401
|
-
profile
|
|
402
|
-
};
|
|
141
|
+
payload: {
|
|
142
|
+
session: {
|
|
143
|
+
authenticated: false
|
|
403
144
|
}
|
|
404
|
-
},
|
|
405
|
-
reply: {}
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
assert.deepEqual(payload.requestedWorkspace, {
|
|
409
|
-
slug: "missing-workspace",
|
|
410
|
-
status: "not_found"
|
|
411
|
-
});
|
|
412
|
-
assert.deepEqual(payload.workspaces, [{ id: 1, slug: "acme", name: "Acme Workspace" }]);
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
test("workspace bootstrap contributor returns requestedWorkspace=unauthenticated for anonymous workspace slug query", async () => {
|
|
416
|
-
const contributor = createWorkspaceBootstrapContributor({
|
|
417
|
-
workspaceService: {
|
|
418
|
-
async listWorkspacesForUser() {
|
|
419
|
-
return [];
|
|
420
|
-
},
|
|
421
|
-
async resolveWorkspaceContextForUserBySlug() {
|
|
422
|
-
return null;
|
|
423
|
-
}
|
|
424
|
-
},
|
|
425
|
-
workspacePendingInvitationsService: {
|
|
426
|
-
async listPendingInvitesForUser() {
|
|
427
|
-
return [];
|
|
428
|
-
}
|
|
429
|
-
},
|
|
430
|
-
userProfilesRepository: {
|
|
431
|
-
async findByIdentity() {
|
|
432
|
-
return null;
|
|
433
|
-
}
|
|
434
|
-
},
|
|
435
|
-
userSettingsRepository: {
|
|
436
|
-
async ensureForUserId() {
|
|
437
|
-
return {};
|
|
438
|
-
}
|
|
439
|
-
},
|
|
440
|
-
workspaceInvitationsEnabled: false,
|
|
441
|
-
appConfig: {
|
|
442
|
-
tenancyMode: "workspaces"
|
|
443
145
|
}
|
|
444
146
|
});
|
|
445
147
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
async executeAction() {
|
|
452
|
-
return {
|
|
453
|
-
authenticated: false
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
reply: {}
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
assert.equal(payload.session.authenticated, false);
|
|
461
|
-
assert.deepEqual(payload.requestedWorkspace, {
|
|
462
|
-
slug: "tonymobily",
|
|
463
|
-
status: "unauthenticated"
|
|
148
|
+
assert.deepEqual(payload, {
|
|
149
|
+
requestedWorkspace: {
|
|
150
|
+
slug: "acme",
|
|
151
|
+
status: "unauthenticated"
|
|
152
|
+
}
|
|
464
153
|
});
|
|
465
|
-
assert.deepEqual(payload.workspaces, []);
|
|
466
154
|
});
|
|
@@ -16,8 +16,10 @@ function authorizedOptions(permissions = []) {
|
|
|
16
16
|
|
|
17
17
|
function createRoleCatalog() {
|
|
18
18
|
return createWorkspaceRoleCatalog({
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
roleCatalog: {
|
|
20
|
+
workspace: {
|
|
21
|
+
defaultInviteRole: "member"
|
|
22
|
+
},
|
|
21
23
|
roles: {
|
|
22
24
|
owner: {
|
|
23
25
|
assignable: false,
|
|
@@ -2,9 +2,11 @@ import test from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { createService } from "../src/server/common/services/workspaceContextService.js";
|
|
4
4
|
|
|
5
|
-
function
|
|
5
|
+
function createRoleCatalog() {
|
|
6
6
|
return {
|
|
7
|
-
|
|
7
|
+
workspace: {
|
|
8
|
+
defaultInviteRole: "member"
|
|
9
|
+
},
|
|
8
10
|
roles: {
|
|
9
11
|
owner: {
|
|
10
12
|
assignable: false,
|
|
@@ -21,7 +23,7 @@ function createWorkspaceRoles() {
|
|
|
21
23
|
function createWorkspaceServiceFixture({
|
|
22
24
|
tenancyMode = "workspaces",
|
|
23
25
|
tenancyPolicy = {},
|
|
24
|
-
|
|
26
|
+
roleCatalog = createRoleCatalog(),
|
|
25
27
|
additionalWorkspaces = [],
|
|
26
28
|
userWorkspaceRows = null,
|
|
27
29
|
membershipResolver = null,
|
|
@@ -69,7 +71,7 @@ function createWorkspaceServiceFixture({
|
|
|
69
71
|
appConfig: {
|
|
70
72
|
tenancyMode,
|
|
71
73
|
tenancyPolicy,
|
|
72
|
-
|
|
74
|
+
roleCatalog: roleCatalog && typeof roleCatalog === "object" ? { ...roleCatalog } : roleCatalog
|
|
73
75
|
},
|
|
74
76
|
workspacesRepository: {
|
|
75
77
|
async findBySlug(slug) {
|
|
@@ -404,7 +406,7 @@ test("workspaceService.resolveWorkspaceContextForUserBySlug grants owner access
|
|
|
404
406
|
const service = createService({
|
|
405
407
|
appConfig: {
|
|
406
408
|
tenancyMode: "personal",
|
|
407
|
-
|
|
409
|
+
roleCatalog: createRoleCatalog()
|
|
408
410
|
},
|
|
409
411
|
workspacesRepository: {
|
|
410
412
|
async findBySlug(slug) {
|
|
@@ -468,10 +470,12 @@ test("workspaceService.resolveWorkspaceContextForUserBySlug grants owner access
|
|
|
468
470
|
assert.deepEqual(context.permissions, ["*"]);
|
|
469
471
|
});
|
|
470
472
|
|
|
471
|
-
test("workspaceService.resolveWorkspaceContextForUserBySlug resolves permissions from appConfig.
|
|
473
|
+
test("workspaceService.resolveWorkspaceContextForUserBySlug resolves permissions from appConfig.roleCatalog", async () => {
|
|
472
474
|
const { service } = createWorkspaceServiceFixture({
|
|
473
|
-
|
|
474
|
-
|
|
475
|
+
roleCatalog: {
|
|
476
|
+
workspace: {
|
|
477
|
+
defaultInviteRole: "member"
|
|
478
|
+
},
|
|
475
479
|
roles: {
|
|
476
480
|
owner: {
|
|
477
481
|
assignable: false,
|
|
@@ -8,8 +8,10 @@ import { createWorkspaceRoleCatalog } from "../src/shared/roles.js";
|
|
|
8
8
|
|
|
9
9
|
function createRoleCatalog() {
|
|
10
10
|
return createWorkspaceRoleCatalog({
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
roleCatalog: {
|
|
12
|
+
workspace: {
|
|
13
|
+
defaultInviteRole: "member"
|
|
14
|
+
},
|
|
13
15
|
roles: {
|
|
14
16
|
owner: {
|
|
15
17
|
assignable: false,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "
|
|
1
|
+
import "../../workspaces-core/templates/packages/main/src/shared/resources/workspaceSettingsFields.js";
|
|
2
2
|
import "../templates/packages/main/src/shared/resources/consoleSettingsFields.js";
|
|
3
3
|
import "../templates/packages/main/src/shared/resources/userSettingsFields.js";
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export const workspaceRoles = {};
|
|
2
|
-
|
|
3
|
-
workspaceRoles.defaultInviteRole = "member";
|
|
4
|
-
workspaceRoles.roles = {};
|
|
5
|
-
|
|
6
|
-
workspaceRoles.roles.owner = {
|
|
7
|
-
assignable: false,
|
|
8
|
-
permissions: []
|
|
9
|
-
};
|
|
10
|
-
workspaceRoles.roles.owner.permissions.push("*");
|
|
11
|
-
|
|
12
|
-
workspaceRoles.roles.admin = {
|
|
13
|
-
assignable: true,
|
|
14
|
-
permissions: []
|
|
15
|
-
};
|
|
16
|
-
workspaceRoles.roles.admin.permissions.push(
|
|
17
|
-
"workspace.roles.view",
|
|
18
|
-
"workspace.settings.view",
|
|
19
|
-
"workspace.settings.update",
|
|
20
|
-
"workspace.members.view",
|
|
21
|
-
"workspace.members.invite",
|
|
22
|
-
"workspace.members.manage",
|
|
23
|
-
"workspace.invites.revoke"
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
workspaceRoles.roles.member = {
|
|
27
|
-
assignable: true,
|
|
28
|
-
permissions: []
|
|
29
|
-
};
|
|
30
|
-
workspaceRoles.roles.member.permissions.push("workspace.settings.view");
|