@stamhoofd/backend 2.83.5 → 2.84.1

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.
Files changed (100) hide show
  1. package/index.ts +19 -4
  2. package/package.json +18 -14
  3. package/src/crons/amazon-ses.ts +26 -5
  4. package/src/crons/balance-emails.ts +18 -17
  5. package/src/email-recipient-loaders/registrations.ts +87 -0
  6. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +5 -2
  7. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +40 -40
  8. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +28 -22
  9. package/src/endpoints/global/events/PatchEventsEndpoint.ts +81 -49
  10. package/src/endpoints/global/files/UploadFile.ts +11 -16
  11. package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +234 -0
  12. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +117 -43
  13. package/src/endpoints/global/members/GetMembersEndpoint.test.ts +1054 -0
  14. package/src/endpoints/global/members/GetMembersEndpoint.ts +163 -141
  15. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +6 -6
  16. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +0 -16
  17. package/src/endpoints/global/members/helpers/validateGroupFilter.ts +73 -0
  18. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +1 -2
  19. package/src/endpoints/global/registration/GetRegistrationsCountEndpoint.ts +43 -0
  20. package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +1016 -0
  21. package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +234 -0
  22. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +5 -5
  23. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +474 -554
  24. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +191 -52
  25. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +107 -9
  26. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +89 -0
  27. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +9 -6
  28. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +88 -0
  29. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +0 -6
  30. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +10 -6
  31. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +10 -25
  32. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +0 -5
  33. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +0 -5
  34. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.ts +4 -0
  35. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalancesEndpoint.ts +1 -0
  36. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +44 -19
  37. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +140 -25
  38. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +40 -10
  39. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +2 -2
  40. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +2 -2
  41. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +4 -1
  42. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +2 -2
  43. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +2 -2
  44. package/src/excel-loaders/members.ts +233 -232
  45. package/src/excel-loaders/payments.ts +1 -1
  46. package/src/excel-loaders/receivable-balances.ts +1 -1
  47. package/src/excel-loaders/registrations.ts +153 -0
  48. package/src/helpers/AdminPermissionChecker.ts +65 -37
  49. package/src/helpers/AuthenticatedStructures.ts +43 -3
  50. package/src/helpers/Context.ts +29 -1
  51. package/src/helpers/GlobalHelper.ts +3 -1
  52. package/src/helpers/GroupedThrottledQueue.test.ts +219 -0
  53. package/src/helpers/GroupedThrottledQueue.ts +108 -0
  54. package/src/helpers/LimitedFilteredRequestHelper.ts +26 -1
  55. package/src/helpers/MemberCharger.ts +0 -5
  56. package/src/helpers/MembershipCharger.ts +3 -9
  57. package/src/helpers/OrganizationCharger.ts +0 -5
  58. package/src/helpers/ThrottledQueue.test.ts +194 -0
  59. package/src/helpers/ThrottledQueue.ts +145 -0
  60. package/src/helpers/XlsxTransformerColumnHelper.ts +44 -1
  61. package/src/middleware/ContextMiddleware.ts +1 -1
  62. package/src/seeds/1728928974-update-cached-outstanding-balance-from-items.ts +2 -1
  63. package/src/seeds/1735577912-update-cached-outstanding-balance-from-items.ts +2 -1
  64. package/src/services/BalanceItemPaymentService.ts +1 -33
  65. package/src/services/BalanceItemService.ts +167 -48
  66. package/src/services/FileSignService.ts +18 -13
  67. package/src/services/MemberRecordStore.ts +28 -19
  68. package/src/services/PaymentReallocationService.test.ts +25 -14
  69. package/src/services/PaymentReallocationService.ts +29 -10
  70. package/src/services/PaymentService.ts +4 -16
  71. package/src/services/PlatformMembershipService.ts +8 -4
  72. package/src/services/RegistrationService.ts +66 -2
  73. package/src/sql-filters/base-registration-filter-compilers.ts +43 -0
  74. package/src/sql-filters/groups.ts +67 -0
  75. package/src/sql-filters/members.ts +33 -58
  76. package/src/sql-filters/organization-registration-periods.ts +8 -0
  77. package/src/sql-filters/registration-periods.ts +8 -0
  78. package/src/sql-filters/registrations.ts +11 -22
  79. package/src/sql-sorters/groups.ts +24 -0
  80. package/src/sql-sorters/organization-registration-periods.ts +24 -0
  81. package/src/sql-sorters/registration-periods.ts +47 -0
  82. package/src/sql-sorters/registrations.ts +77 -0
  83. package/tests/actions/patchOrganizationMember.ts +27 -0
  84. package/tests/actions/patchPaymentStatus.ts +45 -0
  85. package/tests/actions/patchUserMember.ts +27 -0
  86. package/tests/assertions/assertBalances.ts +49 -0
  87. package/tests/e2e/api-rate-limits.test.ts +5 -5
  88. package/tests/e2e/bundle-discounts.test.ts +4060 -0
  89. package/tests/e2e/charge-members.test.ts +27 -24
  90. package/tests/e2e/documents.test.ts +398 -0
  91. package/tests/e2e/register.test.ts +292 -312
  92. package/tests/helpers/PayconiqMocker.ts +55 -0
  93. package/tests/init/index.ts +5 -0
  94. package/tests/init/initAdmin.ts +14 -0
  95. package/tests/init/initBundleDiscount.ts +47 -0
  96. package/tests/init/initPayconiq.ts +9 -0
  97. package/tests/init/initPlatformAdmin.ts +13 -0
  98. package/tests/init/initStripe.ts +21 -0
  99. package/tests/jest.setup.ts +29 -0
  100. package/src/seeds-temporary/1736266448-recall-balance-item-price-paid.ts +0 -70
@@ -0,0 +1,1054 @@
1
+ import { Endpoint, Request } from '@simonbackx/simple-endpoints';
2
+ import { EventFactory, GroupFactory, MemberFactory, OrganizationFactory, RegistrationFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
3
+ import { AccessRight, EventMeta, GroupType, LimitedFilteredRequest, NamedObject, PermissionLevel, PermissionRoleDetailed, Permissions, PermissionsResourceType, ResourcePermissions } from '@stamhoofd/structures';
4
+ import { STExpect, TestUtils } from '@stamhoofd/test-utils';
5
+ import { GetMembersEndpoint } from './GetMembersEndpoint';
6
+ import { testServer } from '../../../../tests/helpers/TestServer';
7
+
8
+ const baseUrl = `/members`;
9
+ const endpoint = new GetMembersEndpoint();
10
+ type EndpointType = typeof endpoint;
11
+ type Body = EndpointType extends Endpoint<any, any, infer B, any> ? B : never;
12
+
13
+ describe('Endpoint.GetMembersEndpoint', () => {
14
+ let period: RegistrationPeriod;
15
+
16
+ beforeEach(async () => {
17
+ TestUtils.setEnvironment('userMode', 'platform');
18
+ });
19
+
20
+ beforeAll(async () => {
21
+ period = await new RegistrationPeriodFactory({
22
+ startDate: new Date(2023, 0, 1),
23
+ endDate: new Date(2023, 11, 31),
24
+ }).create();
25
+ });
26
+
27
+ describe('Permission checking', () => {
28
+ test('Allowed: Can fetch members for a given group', async () => {
29
+ // Setup
30
+ const role = PermissionRoleDetailed.create({
31
+ name: 'Test Role',
32
+ accessRights: [],
33
+ });
34
+
35
+ const resources = new Map();
36
+
37
+ const organization = await new OrganizationFactory({ period, roles: [role] })
38
+ .create();
39
+
40
+ const user = await new UserFactory({
41
+ organization,
42
+ permissions: Permissions.create({
43
+ level: PermissionLevel.None,
44
+ roles: [
45
+ role,
46
+ ],
47
+ resources,
48
+ }),
49
+ })
50
+ .create();
51
+
52
+ const token = await Token.createToken(user);
53
+ const member1 = await new MemberFactory({ }).create();
54
+ const member2 = await new MemberFactory({ }).create();
55
+ const group = await new GroupFactory({ organization, period }).create();
56
+
57
+ resources.set(
58
+ PermissionsResourceType.Groups, new Map([[
59
+ group.id,
60
+ ResourcePermissions.create({
61
+ level: PermissionLevel.Read,
62
+ }),
63
+ ]]),
64
+ );
65
+
66
+ await user.save();
67
+
68
+ await new RegistrationFactory({ member: member1, group }).create();
69
+ await new RegistrationFactory({ member: member2, group }).create();
70
+
71
+ // Try to request members for this group
72
+ const request = Request.get({
73
+ path: baseUrl,
74
+ host: organization.getApiHost(),
75
+ query: new LimitedFilteredRequest({
76
+ filter: {
77
+ registrations: {
78
+ $elemMatch: {
79
+ groupId: group.id,
80
+ },
81
+ },
82
+ },
83
+ limit: 10,
84
+ }),
85
+ headers: {
86
+ authorization: 'Bearer ' + token.accessToken,
87
+ },
88
+ });
89
+ const response = await testServer.test(endpoint, request);
90
+ expect(response.status).toBe(200);
91
+ expect(response.body.results.members).toHaveLength(2);
92
+
93
+ // Check ids are matching without depending on ordering using jest extended
94
+ expect(response.body.results.members).toIncludeSameMembers([
95
+ expect.objectContaining({ id: member1.id }),
96
+ expect.objectContaining({ id: member2.id }),
97
+ ]);
98
+ });
99
+
100
+ test('Not allowed: Cannot fetch members for a given group', async () => {
101
+ // Same test, but without giving the user permissions to read the group
102
+ // Setup
103
+ const role = PermissionRoleDetailed.create({
104
+ name: 'Test Role',
105
+ accessRights: [],
106
+ });
107
+
108
+ const resources = new Map();
109
+
110
+ const organization = await new OrganizationFactory({ period, roles: [role] })
111
+ .create();
112
+
113
+ const user = await new UserFactory({
114
+ organization,
115
+ permissions: Permissions.create({
116
+ level: PermissionLevel.None,
117
+ roles: [
118
+ role,
119
+ ],
120
+ resources,
121
+ }),
122
+ })
123
+ .create();
124
+
125
+ const token = await Token.createToken(user);
126
+ const member1 = await new MemberFactory({ }).create();
127
+ const member2 = await new MemberFactory({ }).create();
128
+ const group = await new GroupFactory({ organization, period }).create();
129
+ const group2 = await new GroupFactory({ organization, period }).create();
130
+
131
+ // Give permission to a different group
132
+ resources.set(
133
+ PermissionsResourceType.Groups, new Map([[
134
+ group2.id,
135
+ ResourcePermissions.create({
136
+ level: PermissionLevel.Read,
137
+ }),
138
+ ]]),
139
+ );
140
+
141
+ await user.save();
142
+
143
+ await new RegistrationFactory({ member: member1, group }).create();
144
+ await new RegistrationFactory({ member: member2, group }).create();
145
+
146
+ // Try to request members for this group
147
+ const request = Request.get({
148
+ path: baseUrl,
149
+ host: organization.getApiHost(),
150
+ query: new LimitedFilteredRequest({
151
+ filter: {
152
+ registrations: {
153
+ $elemMatch: {
154
+ groupId: group.id,
155
+ },
156
+ },
157
+ },
158
+ limit: 10,
159
+ }),
160
+ headers: {
161
+ authorization: 'Bearer ' + token.accessToken,
162
+ },
163
+ });
164
+ await expect(testServer.test(endpoint, request)).rejects.toThrow(
165
+ STExpect.errorWithCode('permission_denied'),
166
+ );
167
+ });
168
+
169
+ test('Allowed: Can fetch members for multiple groups at the same time', async () => {
170
+ // Same test, but without giving the user permissions to read the group
171
+ // Setup
172
+ const role = PermissionRoleDetailed.create({
173
+ name: 'Test Role',
174
+ accessRights: [],
175
+ });
176
+
177
+ const resources = new Map();
178
+
179
+ const organization = await new OrganizationFactory({ period, roles: [role] })
180
+ .create();
181
+
182
+ const user = await new UserFactory({
183
+ organization,
184
+ permissions: Permissions.create({
185
+ level: PermissionLevel.None,
186
+ roles: [
187
+ role,
188
+ ],
189
+ resources,
190
+ }),
191
+ })
192
+ .create();
193
+
194
+ const token = await Token.createToken(user);
195
+ const member1 = await new MemberFactory({ }).create();
196
+ const member2 = await new MemberFactory({ }).create();
197
+ const group = await new GroupFactory({ organization, period }).create();
198
+ const group2 = await new GroupFactory({ organization, period }).create();
199
+
200
+ // Give permission to a different group
201
+ resources.set(
202
+ PermissionsResourceType.Groups, new Map([[
203
+ group.id,
204
+ ResourcePermissions.create({
205
+ level: PermissionLevel.Read,
206
+ }),
207
+ ], [
208
+ group2.id,
209
+ ResourcePermissions.create({
210
+ level: PermissionLevel.Write,
211
+ }),
212
+ ]]),
213
+ );
214
+
215
+ await user.save();
216
+
217
+ await new RegistrationFactory({ member: member1, group }).create();
218
+ await new RegistrationFactory({ member: member2, group: group2 }).create();
219
+
220
+ // Try to request members for this group
221
+ const request = Request.get({
222
+ path: baseUrl,
223
+ host: organization.getApiHost(),
224
+ query: new LimitedFilteredRequest({
225
+ filter: {
226
+ registrations: {
227
+ $elemMatch: {
228
+ groupId: {
229
+ $in: [group.id, group2.id],
230
+ },
231
+ },
232
+ },
233
+ },
234
+ limit: 10,
235
+ }),
236
+ headers: {
237
+ authorization: 'Bearer ' + token.accessToken,
238
+ },
239
+ });
240
+ const response = await testServer.test(endpoint, request);
241
+ expect(response.status).toBe(200);
242
+ expect(response.body.results.members).toHaveLength(2);
243
+ // Check ids are matching without depending on ordering using jest extended
244
+ expect(response.body.results.members).toIncludeSameMembers([
245
+ expect.objectContaining({ id: member1.id }),
246
+ expect.objectContaining({ id: member2.id }),
247
+ ]);
248
+ });
249
+
250
+ test('Not allowed: Cannot fetch members for multiple groups at the same time if no permissions for one of them', async () => {
251
+ // Same test, but without giving the user permissions to read the group
252
+ // Setup
253
+ const role = PermissionRoleDetailed.create({
254
+ name: 'Test Role',
255
+ accessRights: [],
256
+ });
257
+
258
+ const resources = new Map();
259
+
260
+ const organization = await new OrganizationFactory({ period, roles: [role] })
261
+ .create();
262
+
263
+ const user = await new UserFactory({
264
+ organization,
265
+ permissions: Permissions.create({
266
+ level: PermissionLevel.None,
267
+ roles: [
268
+ role,
269
+ ],
270
+ resources,
271
+ }),
272
+ })
273
+ .create();
274
+
275
+ const token = await Token.createToken(user);
276
+ const member1 = await new MemberFactory({ }).create();
277
+ const member2 = await new MemberFactory({ }).create();
278
+ const group = await new GroupFactory({ organization, period }).create();
279
+ const group2 = await new GroupFactory({ organization, period }).create();
280
+
281
+ // Only give permissions for one group
282
+ resources.set(
283
+ PermissionsResourceType.Groups, new Map([[
284
+ group.id,
285
+ ResourcePermissions.create({
286
+ level: PermissionLevel.Read,
287
+ }),
288
+ ]]),
289
+ );
290
+
291
+ await user.save();
292
+
293
+ await new RegistrationFactory({ member: member1, group }).create();
294
+ await new RegistrationFactory({ member: member2, group: group2 }).create();
295
+
296
+ // Try to request members for this group
297
+ const request = Request.get({
298
+ path: baseUrl,
299
+ host: organization.getApiHost(),
300
+ query: new LimitedFilteredRequest({
301
+ filter: {
302
+ registrations: {
303
+ $elemMatch: {
304
+ groupId: {
305
+ $in: [group.id, group2.id],
306
+ },
307
+ },
308
+ },
309
+ },
310
+ limit: 10,
311
+ }),
312
+ headers: {
313
+ authorization: 'Bearer ' + token.accessToken,
314
+ },
315
+ });
316
+ await expect(testServer.test(endpoint, request)).rejects.toThrow(
317
+ STExpect.errorWithCode('permission_denied'),
318
+ );
319
+ });
320
+
321
+ test('Allowed: Can fetch all members if permissions for at least one group', async () => {
322
+ // Same test, but without giving the user permissions to read the group
323
+ // Setup
324
+ const role = PermissionRoleDetailed.create({
325
+ name: 'Test Role',
326
+ accessRights: [],
327
+ });
328
+
329
+ const resources = new Map();
330
+
331
+ const organization = await new OrganizationFactory({ period, roles: [role] })
332
+ .create();
333
+
334
+ const user = await new UserFactory({
335
+ organization,
336
+ permissions: Permissions.create({
337
+ level: PermissionLevel.None,
338
+ roles: [
339
+ role,
340
+ ],
341
+ resources,
342
+ }),
343
+ })
344
+ .create();
345
+
346
+ const token = await Token.createToken(user);
347
+ const member1 = await new MemberFactory({ }).create();
348
+ const member2 = await new MemberFactory({ }).create();
349
+ const member3 = await new MemberFactory({ }).create();
350
+ const group = await new GroupFactory({ organization, period }).create();
351
+ const group2 = await new GroupFactory({ organization, period }).create();
352
+ const group3 = await new GroupFactory({ organization, period }).create();
353
+
354
+ // Give permission for 2 / 3 groups
355
+ resources.set(
356
+ PermissionsResourceType.Groups, new Map([[
357
+ group.id,
358
+ ResourcePermissions.create({
359
+ level: PermissionLevel.Read,
360
+ }),
361
+ ], [
362
+ group2.id,
363
+ ResourcePermissions.create({
364
+ level: PermissionLevel.Write,
365
+ }),
366
+ ]]),
367
+ );
368
+
369
+ await user.save();
370
+
371
+ await new RegistrationFactory({ member: member1, group }).create();
372
+ await new RegistrationFactory({ member: member2, group: group2 }).create();
373
+ await new RegistrationFactory({ member: member3, group: group3 }).create();
374
+
375
+ // Try to request members for this group
376
+ const request = Request.get({
377
+ path: baseUrl,
378
+ host: organization.getApiHost(),
379
+ query: new LimitedFilteredRequest({
380
+ limit: 10,
381
+ }),
382
+ headers: {
383
+ authorization: 'Bearer ' + token.accessToken,
384
+ },
385
+ });
386
+ const response = await testServer.test(endpoint, request);
387
+ expect(response.status).toBe(200);
388
+ expect(response.body.results.members).toHaveLength(2);
389
+ // Check ids are matching without depending on ordering using jest extended
390
+ expect(response.body.results.members).toIncludeSameMembers([
391
+ expect.objectContaining({ id: member1.id }),
392
+ expect.objectContaining({ id: member2.id }),
393
+ ]);
394
+ });
395
+
396
+ test('Not allowed: Cannot fetch all members if no permissions for a single group', async () => {
397
+ // Same test, but without giving the user permissions to read the group
398
+ // Setup
399
+ const role = PermissionRoleDetailed.create({
400
+ name: 'Test Role',
401
+ accessRights: [AccessRight.WebshopScanTickets],
402
+ });
403
+
404
+ const resources = new Map();
405
+
406
+ const organization = await new OrganizationFactory({ period, roles: [role] })
407
+ .create();
408
+
409
+ const user = await new UserFactory({
410
+ organization,
411
+ permissions: Permissions.create({
412
+ level: PermissionLevel.None,
413
+ roles: [
414
+ role,
415
+ ],
416
+ resources,
417
+ }),
418
+ })
419
+ .create();
420
+
421
+ const token = await Token.createToken(user);
422
+ const member1 = await new MemberFactory({ }).create();
423
+ const member2 = await new MemberFactory({ }).create();
424
+ const member3 = await new MemberFactory({ }).create();
425
+ const group = await new GroupFactory({ organization, period }).create();
426
+ const group2 = await new GroupFactory({ organization, period }).create();
427
+ const group3 = await new GroupFactory({ organization, period }).create();
428
+
429
+ await user.save();
430
+
431
+ await new RegistrationFactory({ member: member1, group }).create();
432
+ await new RegistrationFactory({ member: member2, group: group2 }).create();
433
+ await new RegistrationFactory({ member: member3, group: group3 }).create();
434
+
435
+ // Try to request members for this group
436
+ const request = Request.get({
437
+ path: baseUrl,
438
+ host: organization.getApiHost(),
439
+ query: new LimitedFilteredRequest({
440
+ limit: 10,
441
+ }),
442
+ headers: {
443
+ authorization: 'Bearer ' + token.accessToken,
444
+ },
445
+ });
446
+ await expect(testServer.test(endpoint, request)).rejects.toThrow(
447
+ STExpect.errorWithCode('permission_denied'),
448
+ );
449
+ });
450
+
451
+ test('Allowed: A user inherits permissions for an event', async () => {
452
+ // Same test, but without giving the user permissions to read the group
453
+ // Setup
454
+ const role = PermissionRoleDetailed.create({
455
+ name: 'Test Role',
456
+ accessRights: [],
457
+ });
458
+
459
+ const resources = new Map();
460
+
461
+ const organization = await new OrganizationFactory({ period, roles: [role] })
462
+ .create();
463
+
464
+ const user = await new UserFactory({
465
+ organization,
466
+ permissions: Permissions.create({
467
+ level: PermissionLevel.None,
468
+ roles: [
469
+ role,
470
+ ],
471
+ resources,
472
+ }),
473
+ })
474
+ .create();
475
+
476
+ const token = await Token.createToken(user);
477
+ const member1 = await new MemberFactory({ }).create();
478
+ const member2 = await new MemberFactory({ }).create();
479
+
480
+ const defaultGroup = await new GroupFactory({ organization, period }).create();
481
+ const group = await new GroupFactory({ organization, period, type: GroupType.EventRegistration }).create();
482
+ const event = await new EventFactory({
483
+ organization,
484
+ group,
485
+ startDate: period.startDate,
486
+ endDate: period.endDate,
487
+ meta: EventMeta.create({
488
+ groups: [
489
+ NamedObject.create({
490
+ id: defaultGroup.id,
491
+ name: defaultGroup.settings.name.toString(),
492
+ }),
493
+ ],
494
+ }),
495
+ }).create();
496
+
497
+ // Give the user write permissions to all events targeted to the default group
498
+ resources.set(
499
+ PermissionsResourceType.Groups, new Map([[
500
+ defaultGroup.id,
501
+ ResourcePermissions.create({
502
+ level: PermissionLevel.None,
503
+ accessRights: [AccessRight.EventWrite],
504
+ }),
505
+ ]]),
506
+ );
507
+
508
+ await user.save();
509
+
510
+ await new RegistrationFactory({ member: member1, group }).create();
511
+ await new RegistrationFactory({ member: member2, group }).create();
512
+
513
+ // Try to request members for this group
514
+ const request = Request.get({
515
+ path: baseUrl,
516
+ host: organization.getApiHost(),
517
+ query: new LimitedFilteredRequest({
518
+ filter: {
519
+ registrations: {
520
+ $elemMatch: {
521
+ groupId: group.id,
522
+ },
523
+ },
524
+ },
525
+ limit: 10,
526
+ }),
527
+ headers: {
528
+ authorization: 'Bearer ' + token.accessToken,
529
+ },
530
+ });
531
+ const response = await testServer.test(endpoint, request);
532
+ expect(response.status).toBe(200);
533
+ expect(response.body.results.members).toHaveLength(2);
534
+ // Check ids are matching without depending on ordering using jest extended
535
+ expect(response.body.results.members).toIncludeSameMembers([
536
+ expect.objectContaining({ id: member1.id }),
537
+ expect.objectContaining({ id: member2.id }),
538
+ ]);
539
+ });
540
+
541
+ test('Allowed: A user inherits permissions for the waiting list of an event', async () => {
542
+ // Same test, but without giving the user permissions to read the group
543
+ // Setup
544
+ const role = PermissionRoleDetailed.create({
545
+ name: 'Test Role',
546
+ accessRights: [],
547
+ });
548
+
549
+ const resources = new Map();
550
+
551
+ const organization = await new OrganizationFactory({ period, roles: [role] })
552
+ .create();
553
+
554
+ const user = await new UserFactory({
555
+ organization,
556
+ permissions: Permissions.create({
557
+ level: PermissionLevel.None,
558
+ roles: [
559
+ role,
560
+ ],
561
+ resources,
562
+ }),
563
+ })
564
+ .create();
565
+
566
+ const token = await Token.createToken(user);
567
+ const member1 = await new MemberFactory({ }).create();
568
+ const member2 = await new MemberFactory({ }).create();
569
+ const member3 = await new MemberFactory({ }).create();
570
+
571
+ const defaultGroup = await new GroupFactory({ organization, period }).create();
572
+ const waitingList = await new GroupFactory({ organization, period, type: GroupType.WaitingList }).create();
573
+ const group = await new GroupFactory({ organization, period, type: GroupType.EventRegistration, waitingListId: waitingList.id }).create();
574
+
575
+ const event = await new EventFactory({
576
+ organization,
577
+ group,
578
+ startDate: period.startDate,
579
+ endDate: period.endDate,
580
+ meta: EventMeta.create({
581
+ groups: [
582
+ NamedObject.create({
583
+ id: defaultGroup.id,
584
+ name: defaultGroup.settings.name.toString(),
585
+ }),
586
+ ],
587
+ }),
588
+ }).create();
589
+
590
+ // Give the user write permissions to all events targeted to the default group
591
+ resources.set(
592
+ PermissionsResourceType.Groups, new Map([[
593
+ defaultGroup.id,
594
+ ResourcePermissions.create({
595
+ level: PermissionLevel.None,
596
+ accessRights: [AccessRight.EventWrite],
597
+ }),
598
+ ]]),
599
+ );
600
+
601
+ await user.save();
602
+
603
+ await new RegistrationFactory({ member: member1, group: waitingList }).create();
604
+ await new RegistrationFactory({ member: member2, group: waitingList }).create();
605
+ await new RegistrationFactory({ member: member3, group }).create();
606
+
607
+ // Try to request members for this group
608
+ const request = Request.get({
609
+ path: baseUrl,
610
+ host: organization.getApiHost(),
611
+ query: new LimitedFilteredRequest({
612
+ filter: {
613
+ registrations: {
614
+ $elemMatch: {
615
+ groupId: waitingList.id,
616
+ },
617
+ },
618
+ },
619
+ limit: 10,
620
+ }),
621
+ headers: {
622
+ authorization: 'Bearer ' + token.accessToken,
623
+ },
624
+ });
625
+ const response = await testServer.test(endpoint, request);
626
+ expect(response.status).toBe(200);
627
+ expect(response.body.results.members).toHaveLength(2);
628
+ // Check ids are matching without depending on ordering using jest extended
629
+ expect(response.body.results.members).toIncludeSameMembers([
630
+ expect.objectContaining({ id: member1.id }),
631
+ expect.objectContaining({ id: member2.id }),
632
+ ]);
633
+ });
634
+
635
+ test('Not allowed: A user cannot request registrations of an event if no permission to events', async () => {
636
+ // Same test, but without giving the user permissions to read the group
637
+ // Setup
638
+ const role = PermissionRoleDetailed.create({
639
+ name: 'Test Role',
640
+ accessRights: [],
641
+ });
642
+
643
+ const resources = new Map();
644
+
645
+ const organization = await new OrganizationFactory({ period, roles: [role] })
646
+ .create();
647
+
648
+ const user = await new UserFactory({
649
+ organization,
650
+ permissions: Permissions.create({
651
+ level: PermissionLevel.None,
652
+ roles: [
653
+ role,
654
+ ],
655
+ resources,
656
+ }),
657
+ })
658
+ .create();
659
+
660
+ const token = await Token.createToken(user);
661
+ const member1 = await new MemberFactory({ }).create();
662
+ const member2 = await new MemberFactory({ }).create();
663
+
664
+ const defaultGroup = await new GroupFactory({ organization, period }).create();
665
+ const group = await new GroupFactory({ organization, period, type: GroupType.EventRegistration }).create();
666
+ const event = await new EventFactory({
667
+ organization,
668
+ group,
669
+ startDate: period.startDate,
670
+ endDate: period.endDate,
671
+ meta: EventMeta.create({
672
+ groups: [
673
+ NamedObject.create({
674
+ id: defaultGroup.id,
675
+ name: defaultGroup.settings.name.toString(),
676
+ }),
677
+ ],
678
+ }),
679
+ }).create();
680
+
681
+ // The user can read members registered for default group, but not events for default group
682
+ resources.set(
683
+ PermissionsResourceType.Groups, new Map([[
684
+ defaultGroup.id,
685
+ ResourcePermissions.create({
686
+ level: PermissionLevel.Read,
687
+ accessRights: [],
688
+ }),
689
+ ]]),
690
+ );
691
+
692
+ await user.save();
693
+
694
+ await new RegistrationFactory({ member: member1, group }).create();
695
+ await new RegistrationFactory({ member: member2, group }).create();
696
+
697
+ // Try to request members for this group
698
+ const request = Request.get({
699
+ path: baseUrl,
700
+ host: organization.getApiHost(),
701
+ query: new LimitedFilteredRequest({
702
+ filter: {
703
+ registrations: {
704
+ $elemMatch: {
705
+ groupId: group.id,
706
+ },
707
+ },
708
+ },
709
+ limit: 10,
710
+ }),
711
+ headers: {
712
+ authorization: 'Bearer ' + token.accessToken,
713
+ },
714
+ });
715
+ await expect(testServer.test(endpoint, request)).rejects.toThrow(
716
+ STExpect.errorWithCode('permission_denied'),
717
+ );
718
+ });
719
+
720
+ test('Not allowed: A user cannot request registrations of an event for all members if only access to events for a single group', async () => {
721
+ // Same test, but without giving the user permissions to read the group
722
+ // Setup
723
+ const role = PermissionRoleDetailed.create({
724
+ name: 'Test Role',
725
+ accessRights: [],
726
+ });
727
+
728
+ const resources = new Map();
729
+
730
+ const organization = await new OrganizationFactory({ period, roles: [role] })
731
+ .create();
732
+
733
+ const user = await new UserFactory({
734
+ organization,
735
+ permissions: Permissions.create({
736
+ level: PermissionLevel.None,
737
+ roles: [
738
+ role,
739
+ ],
740
+ resources,
741
+ }),
742
+ })
743
+ .create();
744
+
745
+ const token = await Token.createToken(user);
746
+ const member1 = await new MemberFactory({ }).create();
747
+ const member2 = await new MemberFactory({ }).create();
748
+
749
+ const defaultGroup = await new GroupFactory({ organization, period }).create();
750
+ const group = await new GroupFactory({ organization, period, type: GroupType.EventRegistration }).create();
751
+
752
+ // Event for all members of the organization (so no permission to this event)
753
+ const event = await new EventFactory({
754
+ organization,
755
+ group,
756
+ startDate: period.startDate,
757
+ endDate: period.endDate,
758
+ }).create();
759
+
760
+ // The user can read members registered for default group, but not events for default group
761
+ resources.set(
762
+ PermissionsResourceType.Groups, new Map([[
763
+ defaultGroup.id,
764
+ ResourcePermissions.create({
765
+ level: PermissionLevel.Read,
766
+ accessRights: [AccessRight.EventWrite],
767
+ }),
768
+ ]]),
769
+ );
770
+
771
+ await user.save();
772
+
773
+ await new RegistrationFactory({ member: member1, group }).create();
774
+ await new RegistrationFactory({ member: member2, group }).create();
775
+
776
+ // Try to request members for this group
777
+ const request = Request.get({
778
+ path: baseUrl,
779
+ host: organization.getApiHost(),
780
+ query: new LimitedFilteredRequest({
781
+ filter: {
782
+ registrations: {
783
+ $elemMatch: {
784
+ groupId: group.id,
785
+ },
786
+ },
787
+ },
788
+ limit: 10,
789
+ }),
790
+ headers: {
791
+ authorization: 'Bearer ' + token.accessToken,
792
+ },
793
+ });
794
+ await expect(testServer.test(endpoint, request)).rejects.toThrow(
795
+ STExpect.errorWithCode('permission_denied'),
796
+ );
797
+ });
798
+
799
+ test('Not allowed: A user cannot request registrations for the waiting list of an event if no permissions for event', async () => {
800
+ // Same test, but without giving the user permissions to read the group
801
+ // Setup
802
+ const role = PermissionRoleDetailed.create({
803
+ name: 'Test Role',
804
+ accessRights: [],
805
+ });
806
+
807
+ const resources = new Map();
808
+
809
+ const organization = await new OrganizationFactory({ period, roles: [role] })
810
+ .create();
811
+
812
+ const user = await new UserFactory({
813
+ organization,
814
+ permissions: Permissions.create({
815
+ level: PermissionLevel.None,
816
+ roles: [
817
+ role,
818
+ ],
819
+ resources,
820
+ }),
821
+ })
822
+ .create();
823
+
824
+ const token = await Token.createToken(user);
825
+ const member1 = await new MemberFactory({ }).create();
826
+ const member2 = await new MemberFactory({ }).create();
827
+ const member3 = await new MemberFactory({ }).create();
828
+
829
+ const defaultGroup = await new GroupFactory({ organization, period }).create();
830
+ const waitingList = await new GroupFactory({ organization, period, type: GroupType.WaitingList }).create();
831
+ const group = await new GroupFactory({ organization, period, type: GroupType.EventRegistration, waitingListId: waitingList.id }).create();
832
+
833
+ const event = await new EventFactory({
834
+ organization,
835
+ group,
836
+ startDate: period.startDate,
837
+ endDate: period.endDate,
838
+ meta: EventMeta.create({
839
+ groups: [
840
+ NamedObject.create({
841
+ id: defaultGroup.id,
842
+ name: defaultGroup.settings.name.toString(),
843
+ }),
844
+ ],
845
+ }),
846
+ }).create();
847
+
848
+ // The user can read members registered for default group, but not events for default group
849
+ resources.set(
850
+ PermissionsResourceType.Groups, new Map([[
851
+ defaultGroup.id,
852
+ ResourcePermissions.create({
853
+ level: PermissionLevel.Read,
854
+ accessRights: [],
855
+ }),
856
+ ]]),
857
+ );
858
+
859
+ await user.save();
860
+
861
+ await new RegistrationFactory({ member: member1, group: waitingList }).create();
862
+ await new RegistrationFactory({ member: member2, group: waitingList }).create();
863
+ await new RegistrationFactory({ member: member3, group }).create();
864
+
865
+ // Try to request members for this group
866
+ const request = Request.get({
867
+ path: baseUrl,
868
+ host: organization.getApiHost(),
869
+ query: new LimitedFilteredRequest({
870
+ filter: {
871
+ registrations: {
872
+ $elemMatch: {
873
+ groupId: waitingList.id,
874
+ },
875
+ },
876
+ },
877
+ limit: 10,
878
+ }),
879
+ headers: {
880
+ authorization: 'Bearer ' + token.accessToken,
881
+ },
882
+ });
883
+ await expect(testServer.test(endpoint, request)).rejects.toThrow(
884
+ STExpect.errorWithCode('permission_denied'),
885
+ );
886
+ });
887
+ });
888
+
889
+ describe('Default filtering', () => {
890
+ test('A user without read permissions for all groups will only see members of membership groups', async () => {
891
+ // Same test, but without giving the user permissions to read the group
892
+ // Setup
893
+ const role = PermissionRoleDetailed.create({
894
+ name: 'Test Role',
895
+ accessRights: [],
896
+ });
897
+
898
+ const resources = new Map();
899
+
900
+ const organization = await new OrganizationFactory({ period, roles: [role] })
901
+ .create();
902
+
903
+ const user = await new UserFactory({
904
+ organization,
905
+ permissions: Permissions.create({
906
+ level: PermissionLevel.None,
907
+ roles: [
908
+ role,
909
+ ],
910
+ resources,
911
+ }),
912
+ })
913
+ .create();
914
+
915
+ const token = await Token.createToken(user);
916
+ const member1 = await new MemberFactory({ }).create();
917
+ const member2 = await new MemberFactory({ }).create();
918
+ const member3 = await new MemberFactory({ }).create();
919
+ const member4 = await new MemberFactory({ }).create();
920
+ const group = await new GroupFactory({ organization, period }).create();
921
+ const group2 = await new GroupFactory({ organization, period }).create();
922
+ const group3 = await new GroupFactory({ organization, period }).create();
923
+
924
+ const group4 = await new GroupFactory({ organization, period, type: GroupType.EventRegistration }).create();
925
+
926
+ // Give permission for 2 / 3 groups + one event group
927
+ resources.set(
928
+ PermissionsResourceType.Groups, new Map([[
929
+ group.id,
930
+ ResourcePermissions.create({
931
+ level: PermissionLevel.Read,
932
+ }),
933
+ ], [
934
+ group2.id,
935
+ ResourcePermissions.create({
936
+ level: PermissionLevel.Write,
937
+ }),
938
+ ], [
939
+ group4.id,
940
+ ResourcePermissions.create({
941
+ level: PermissionLevel.Read,
942
+ }),
943
+ ]]),
944
+ );
945
+
946
+ await user.save();
947
+
948
+ await new RegistrationFactory({ member: member1, group }).create();
949
+ await new RegistrationFactory({ member: member2, group: group2 }).create();
950
+ await new RegistrationFactory({ member: member3, group: group3 }).create();
951
+ await new RegistrationFactory({ member: member4, group: group4 }).create();
952
+
953
+ // Try to request all members
954
+ const request = Request.get({
955
+ path: baseUrl,
956
+ host: organization.getApiHost(),
957
+ query: new LimitedFilteredRequest({
958
+ limit: 10,
959
+ }),
960
+ headers: {
961
+ authorization: 'Bearer ' + token.accessToken,
962
+ },
963
+ });
964
+
965
+ // Response only includes members registered in a membership group, not the event group
966
+ const response = await testServer.test(endpoint, request);
967
+ expect(response.status).toBe(200);
968
+ expect(response.body.results.members).toHaveLength(2);
969
+ // Check ids are matching without depending on ordering using jest extended
970
+ expect(response.body.results.members).toIncludeSameMembers([
971
+ expect.objectContaining({ id: member1.id }),
972
+ expect.objectContaining({ id: member2.id }),
973
+ ]);
974
+ });
975
+
976
+ test('A user with read permissions for all groups will also see members of event groups', async () => {
977
+ // Same test, but without giving the user permissions to read the group
978
+ // Setup
979
+ const role = PermissionRoleDetailed.create({
980
+ name: 'Test Role',
981
+ accessRights: [],
982
+ });
983
+
984
+ const resources = new Map();
985
+
986
+ const organization = await new OrganizationFactory({ period, roles: [role] })
987
+ .create();
988
+
989
+ const user = await new UserFactory({
990
+ organization,
991
+ permissions: Permissions.create({
992
+ level: PermissionLevel.None,
993
+ roles: [
994
+ role,
995
+ ],
996
+ resources,
997
+ }),
998
+ })
999
+ .create();
1000
+
1001
+ const token = await Token.createToken(user);
1002
+ const member1 = await new MemberFactory({ }).create();
1003
+ const member2 = await new MemberFactory({ }).create();
1004
+ const member3 = await new MemberFactory({ }).create();
1005
+ const member4 = await new MemberFactory({ }).create();
1006
+ const group = await new GroupFactory({ organization, period }).create();
1007
+ const group2 = await new GroupFactory({ organization, period }).create();
1008
+ const group3 = await new GroupFactory({ organization, period }).create();
1009
+
1010
+ const group4 = await new GroupFactory({ organization, period, type: GroupType.EventRegistration }).create();
1011
+
1012
+ // Give permission to all groups
1013
+ resources.set(
1014
+ PermissionsResourceType.Groups, new Map([[
1015
+ '',
1016
+ ResourcePermissions.create({
1017
+ level: PermissionLevel.Read,
1018
+ }),
1019
+ ]]),
1020
+ );
1021
+
1022
+ await user.save();
1023
+
1024
+ await new RegistrationFactory({ member: member1, group }).create();
1025
+ await new RegistrationFactory({ member: member2, group: group2 }).create();
1026
+ await new RegistrationFactory({ member: member3, group: group3 }).create();
1027
+ await new RegistrationFactory({ member: member4, group: group4 }).create();
1028
+
1029
+ // Try to request all members
1030
+ const request = Request.get({
1031
+ path: baseUrl,
1032
+ host: organization.getApiHost(),
1033
+ query: new LimitedFilteredRequest({
1034
+ limit: 10,
1035
+ }),
1036
+ headers: {
1037
+ authorization: 'Bearer ' + token.accessToken,
1038
+ },
1039
+ });
1040
+
1041
+ // Response only includes members registered in a membership group, not the event group
1042
+ const response = await testServer.test(endpoint, request);
1043
+ expect(response.status).toBe(200);
1044
+ expect(response.body.results.members).toHaveLength(4);
1045
+ // Check ids are matching without depending on ordering using jest extended
1046
+ expect(response.body.results.members).toIncludeSameMembers([
1047
+ expect.objectContaining({ id: member1.id }),
1048
+ expect.objectContaining({ id: member2.id }),
1049
+ expect.objectContaining({ id: member3.id }),
1050
+ expect.objectContaining({ id: member4.id }),
1051
+ ]);
1052
+ });
1053
+ });
1054
+ });