@stamhoofd/backend 2.83.5 → 2.84.0

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