@stamhoofd/backend 2.81.0 → 2.83.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 (108) hide show
  1. package/package.json +10 -10
  2. package/src/audit-logs/GroupLogger.ts +3 -3
  3. package/src/audit-logs/MemberResponsibilityRecordLogger.ts +1 -1
  4. package/src/audit-logs/OrderLogger.ts +1 -1
  5. package/src/audit-logs/RegistrationLogger.ts +1 -1
  6. package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +4 -4
  7. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
  8. package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +5 -5
  9. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +1 -1
  10. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +8 -8
  11. package/src/endpoints/auth/CreateAdminEndpoint.ts +2 -2
  12. package/src/endpoints/auth/CreateTokenEndpoint.ts +10 -10
  13. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +2 -2
  14. package/src/endpoints/auth/PatchUserEndpoint.ts +9 -9
  15. package/src/endpoints/auth/SignupEndpoint.ts +2 -2
  16. package/src/endpoints/auth/VerifyEmailEndpoint.ts +3 -3
  17. package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +1 -1
  18. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +1 -1
  19. package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
  20. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +1 -1
  21. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +139 -0
  22. package/src/endpoints/global/email/PatchEmailEndpoint.ts +30 -7
  23. package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +1 -1
  24. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +16 -35
  25. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +1 -1
  26. package/src/endpoints/global/events/PatchEventsEndpoint.ts +22 -16
  27. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +1 -1
  28. package/src/endpoints/global/files/UploadFile.ts +14 -2
  29. package/src/endpoints/global/files/UploadImage.ts +2 -2
  30. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -2
  31. package/src/endpoints/global/members/GetMembersEndpoint.ts +1 -1
  32. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +19 -19
  33. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +34 -34
  34. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
  35. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +5 -5
  36. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +5 -1
  37. package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +68 -0
  38. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +1 -1
  39. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +2 -2
  40. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +15 -17
  41. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +4 -4
  42. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +37 -37
  43. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +2 -2
  44. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
  45. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
  46. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +5 -5
  47. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +2 -2
  48. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
  49. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +3 -3
  50. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +9 -9
  51. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +4 -4
  52. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +1 -1
  53. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +1 -1
  54. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +11 -11
  55. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +13 -13
  56. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +16 -16
  57. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
  58. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -2
  59. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +1 -1
  60. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +1 -1
  61. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
  62. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +106 -0
  63. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +16 -3
  64. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +2 -2
  65. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +247 -0
  66. package/src/endpoints/{auth → organization/dashboard/users}/PatchApiUserEndpoint.ts +25 -6
  67. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +4 -4
  68. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +2 -2
  69. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +8 -8
  70. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
  71. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +1 -1
  72. package/src/endpoints/organization/shared/GetDocumentHtml.ts +2 -2
  73. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
  74. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +5 -0
  75. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +1 -1
  76. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +2 -2
  77. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +1 -1
  78. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +3 -3
  79. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +6 -1
  80. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +1 -1
  81. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +10 -8
  82. package/src/excel-loaders/event-notifications.ts +11 -11
  83. package/src/excel-loaders/members.ts +34 -34
  84. package/src/excel-loaders/organizations.ts +23 -23
  85. package/src/excel-loaders/payments.ts +39 -39
  86. package/src/excel-loaders/receivable-balances.ts +21 -21
  87. package/src/helpers/AddressValidator.ts +6 -6
  88. package/src/helpers/AdminPermissionChecker.ts +7 -4
  89. package/src/helpers/AuthenticatedStructures.ts +16 -8
  90. package/src/helpers/BuckarooHelper.ts +1 -1
  91. package/src/helpers/CheckSettlements.ts +1 -1
  92. package/src/helpers/Context.ts +31 -15
  93. package/src/helpers/FileCache.ts +7 -7
  94. package/src/helpers/ForwardHandler.ts +1 -1
  95. package/src/helpers/GlobalHelper.ts +6 -4
  96. package/src/helpers/MembershipCharger.ts +2 -2
  97. package/src/helpers/SetupStepUpdater.ts +1 -1
  98. package/src/helpers/StripeHelper.ts +18 -7
  99. package/src/helpers/XlsxTransformerColumnHelper.ts +18 -18
  100. package/src/services/DocumentService.ts +1 -1
  101. package/src/services/EventNotificationService.ts +1 -1
  102. package/src/services/MemberNumberService.ts +3 -3
  103. package/src/services/SSOService.ts +5 -5
  104. package/src/sql-filters/members.ts +1 -1
  105. package/tests/e2e/api-rate-limits.test.ts +188 -0
  106. package/tests/e2e/private-files.test.ts +3 -3
  107. package/tests/helpers/StripeMocker.ts +7 -1
  108. /package/src/endpoints/global/platform/{GetPlatformEnpoint.ts → GetPlatformEndpoint.ts} +0 -0
@@ -50,7 +50,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
50
50
  throw new SimpleError({
51
51
  code: 'permission_denied',
52
52
  message: 'You do not have permissions to edit an inactive organization',
53
- human: 'Je hebt geen toegangsrechten om een inactieve groep te bewerken',
53
+ human: $t(`b33ba9f0-c12d-4918-ac9d-7aeec5de41a5`),
54
54
  statusCode: 403,
55
55
  });
56
56
  }
@@ -85,7 +85,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
85
85
  throw new SimpleError({
86
86
  code: 'invalid_field',
87
87
  message: 'Field is too long',
88
- human: 'De URI van de vereniging is te lang',
88
+ human: $t(`864675a2-7376-4a61-9c7d-f488e7906d7b`),
89
89
  field: 'uri',
90
90
  });
91
91
  }
@@ -94,7 +94,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
94
94
  throw new SimpleError({
95
95
  code: 'invalid_field',
96
96
  message: 'Field is too short',
97
- human: 'De URI van de vereniging is te kort',
97
+ human: $t(`0fa660da-1e74-4940-af88-eafafb1094b6`),
98
98
  field: 'uri',
99
99
  });
100
100
  }
@@ -104,7 +104,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
104
104
  throw new SimpleError({
105
105
  code: 'name_taken',
106
106
  message: 'An organization with the same URI already exists',
107
- human: 'Er bestaat al een vereniging met dezelfde URI. Voeg bijvoorbeeld de naam van je gemeente toe.',
107
+ human: $t(`daa7dc61-e8c9-4629-97b6-a75f39eaebac`),
108
108
  field: 'uri',
109
109
  });
110
110
  }
@@ -301,7 +301,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
301
301
 
302
302
  if (request.body.active !== undefined) {
303
303
  if (!Context.auth.hasPlatformFullAccess()) {
304
- throw Context.auth.error('Enkel een platform hoofdbeheerder kan een groep (in)actief maken');
304
+ throw Context.auth.error($t(`e46f34cd-f166-421e-be97-28ccecdf9c73`));
305
305
  }
306
306
  organization.active = request.body.active;
307
307
  }
@@ -317,7 +317,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
317
317
  throw new SimpleError({
318
318
  code: 'name_taken',
319
319
  message: 'An organization with the same name already exists',
320
- human: 'Er bestaat al een vereniging met dezelfde URI. Pas deze aan zodat deze uniek is, en controleer of deze vereniging niet al bestaat.',
320
+ human: $t(`6e675a7d-b124-4507-bf0c-9b70013e98ca`),
321
321
  field: 'name',
322
322
  });
323
323
  }
@@ -384,7 +384,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
384
384
 
385
385
  if (!model || !await Context.auth.canAccessWebshop(model, PermissionLevel.Full)) {
386
386
  errors.addError(
387
- Context.auth.error('Je hebt geen toegangsrechten om deze webshop te wijzigen'),
387
+ Context.auth.error($t(`5e92a9b3-a94d-41db-9d0e-c58122400725`)),
388
388
  );
389
389
  continue;
390
390
  }
@@ -440,7 +440,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
440
440
  throw new SimpleError({
441
441
  code: 'invalid_field',
442
442
  message: 'Too many companies',
443
- human: 'Je kan maximaal 5 bedrijven toevoegen',
443
+ human: $t(`35d462bc-121e-46ad-824b-a7838a6665ae`),
444
444
  field: 'companies',
445
445
  });
446
446
  }
@@ -453,7 +453,7 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
453
453
  throw new SimpleError({
454
454
  code: 'invalid_field',
455
455
  message: 'Too many companies',
456
- human: 'Je kan maximaal 5 bedrijven toevoegen',
456
+ human: $t(`35d462bc-121e-46ad-824b-a7838a6665ae`),
457
457
  field: 'companies',
458
458
  });
459
459
  }
@@ -56,7 +56,7 @@ export class SetOrganizationDomainEndpoint extends Endpoint<Params, Query, Body,
56
56
  throw new SimpleError({
57
57
  code: 'invalid_domain',
58
58
  message: 'registerDomain is invalid',
59
- human: 'De subdomeinnaam voor jouw registratiepagina is ongeldig',
59
+ human: $t(`50fc6c16-b3d2-4015-b04c-7f11c6e4d497`),
60
60
  field: 'registerDomain',
61
61
  });
62
62
  }
@@ -65,7 +65,7 @@ export class SetOrganizationDomainEndpoint extends Endpoint<Params, Query, Body,
65
65
  throw new SimpleError({
66
66
  code: 'invalid_domain',
67
67
  message: 'mailDomain is invalid',
68
- human: 'De domeinnaam voor e-mails is ongeldig',
68
+ human: $t(`da31b8f3-ef8c-4235-b9ba-7eb6d6433a61`),
69
69
  field: 'mailDomain',
70
70
  });
71
71
  }
@@ -113,7 +113,7 @@ export class SetOrganizationDomainEndpoint extends Endpoint<Params, Query, Body,
113
113
  type: DNSRecordType.CNAME,
114
114
  name: organization.privateMeta.mailFromDomain + '.',
115
115
  // Use shops for mail domain, to allow reuse
116
- value: STAMHOOFD.domains.webshopCname + '.',
116
+ value: (STAMHOOFD.domains.webshopCname ?? STAMHOOFD.domains.registrationCname) + '.',
117
117
  }));
118
118
 
119
119
  if (STAMHOOFD.domains.registration && organization.privateMeta.pendingRegisterDomain) {
@@ -165,7 +165,7 @@ export class SetOrganizationDomainEndpoint extends Endpoint<Params, Query, Body,
165
165
  type: DNSRecordType.TXT,
166
166
  name: '_dmarc.' + organization.privateMeta.pendingMailDomain + '.',
167
167
  value: 'v=DMARC1; p=quarantine; pct=100; sp=quarantine; aspf=r; adkim=r;',
168
- description: 'Opgelet met het instellen van deze DMARC-record voor je domeinnaam. Mogelijks bestaat er al een record met deze naam, voeg deze dan zeker niet dubbel toe en behoud best de huidige waarde (wel zou aspf en adkim op r moeten staan). De waarde die we voorstellen zorgt voor een sterke beveiliging, maar kan mogelijks problemen veroorzaken als je andere diensten gebruikt die op een onveilige manier emails versturen (zonder DKIM of SPF).',
168
+ description: $t(`dfd0d85f-262e-4d5a-b8e4-8a9e67c60652`),
169
169
  optional: true,
170
170
  }));
171
171
  }
@@ -35,7 +35,7 @@ export class GetMemberBalanceEndpoint extends Endpoint<Params, Query, Body, Resp
35
35
  const member = (await Member.getWithRegistrations(request.params.id));
36
36
 
37
37
  if (!member || !await Context.auth.hasFinancialMemberAccess(member)) {
38
- throw Context.auth.notFoundOrNoAccess('Geen lid gevonden, of je hebt geen toegang tot dit lid');
38
+ throw Context.auth.notFoundOrNoAccess($t(`baf6fd8e-1937-4c8b-8510-fa811473c157`));
39
39
  }
40
40
 
41
41
  // Get all balance items for this member or users
@@ -171,7 +171,7 @@ export class GetPaymentsEndpoint extends Endpoint<Params, Query, Body, ResponseB
171
171
  throw new SimpleError({
172
172
  code: 'timeout',
173
173
  message: 'Query took too long',
174
- human: 'Deze opzoeking is te complex en duurt te lang. Probeer een eenvoudigere zoekopdracht of probeer terug op een rustiger tijdstip.',
174
+ human: $t(`dce51638-6129-448b-8a15-e6d778f3a76a`),
175
175
  });
176
176
  }
177
177
  throw error;
@@ -72,13 +72,13 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
72
72
  if (put.payingOrganizationId) {
73
73
  // Not allowed if not full admin
74
74
  if (!Context.auth.hasPlatformFullAccess()) {
75
- throw Context.auth.error('Je moet volledige platform beheerder zijn om schulden tussen verenigingen te wijzigen of toe te voegen');
75
+ throw Context.auth.error($t(`fafaf0c2-cbf4-4f5b-8669-eb09303efe1e`));
76
76
  }
77
77
  if (put.payingOrganizationId === model.organizationId) {
78
78
  throw new SimpleError({
79
79
  code: 'invalid_field',
80
80
  message: 'payingOrganizationId cannot be the same as organizationId',
81
- human: 'Dit is een ongeldige situatie. Een schuld moet tussen verschillende verenigingen zijn.',
81
+ human: $t(`7b181446-ef56-4957-ad13-54644e6d0987`),
82
82
  field: 'payingOrganizationId',
83
83
  });
84
84
  }
@@ -90,7 +90,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
90
90
  throw new SimpleError({
91
91
  code: 'invalid_price',
92
92
  message: 'Cannot create negative balance in the future',
93
- human: 'Het is niet mogelijk om een negatief openstaand bedrag toe te voegen in de toekomst',
93
+ human: $t(`ad8ee48a-8176-4b5f-b6fc-54bc615c2564`),
94
94
  });
95
95
  }
96
96
 
@@ -98,7 +98,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
98
98
  throw new SimpleError({
99
99
  code: 'invalid_field',
100
100
  message: 'createdAt cannot be in the future',
101
- human: 'De datum kan niet in de toekomst liggen',
101
+ human: $t(`8bdc9bf1-1d05-4579-9cc4-7fe11c1f031b`),
102
102
  field: 'createdAt',
103
103
  });
104
104
  }
@@ -130,13 +130,13 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
130
130
  if (patch.payingOrganizationId !== undefined) {
131
131
  // Not allowed if not full admin
132
132
  if (!Context.auth.hasPlatformFullAccess()) {
133
- throw Context.auth.error('Je moet volledige platform beheerder zijn om schulden tussen verenigingen te wijzigen of toe te voegen');
133
+ throw Context.auth.error($t(`fafaf0c2-cbf4-4f5b-8669-eb09303efe1e`));
134
134
  }
135
135
  if (patch.payingOrganizationId === model.organizationId) {
136
136
  throw new SimpleError({
137
137
  code: 'invalid_field',
138
138
  message: 'payingOrganizationId cannot be the same as organizationId',
139
- human: 'Dit is een ongeldige situatie. Een schuld moet tussen verschillende verenigingen zijn.',
139
+ human: $t(`7b181446-ef56-4957-ad13-54644e6d0987`),
140
140
  field: 'payingOrganizationId',
141
141
  });
142
142
  }
@@ -158,7 +158,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
158
158
  throw new SimpleError({
159
159
  code: 'invalid_field',
160
160
  message: 'No user or member provided',
161
- human: 'Een verschuild moet altijd aan een lid of gebruiker gelinkt zijn',
161
+ human: $t(`ace2c298-5b2a-42cd-900d-624e1e375aeb`),
162
162
  field: 'memberId',
163
163
  });
164
164
  }
@@ -176,7 +176,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
176
176
  throw new SimpleError({
177
177
  code: 'invalid_field',
178
178
  message: 'createdAt cannot be in the future',
179
- human: 'De datum kan niet in de toekomst liggen',
179
+ human: $t(`8bdc9bf1-1d05-4579-9cc4-7fe11c1f031b`),
180
180
  field: 'createdAt',
181
181
  });
182
182
  }
@@ -191,7 +191,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
191
191
  throw new SimpleError({
192
192
  code: 'invalid_price',
193
193
  message: 'Cannot create negative balance in the future',
194
- human: 'Het is niet mogelijk om een negatief openstaand bedrag toe te voegen in de toekomst',
194
+ human: $t(`ad8ee48a-8176-4b5f-b6fc-54bc615c2564`),
195
195
  field: 'dueAt',
196
196
  });
197
197
  }
@@ -251,7 +251,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
251
251
  throw new SimpleError({
252
252
  code: 'permission_denied',
253
253
  message: 'No permission to link balance items to this member',
254
- human: 'Je hebt geen toegang om aanrekeningen te maken verbonden met dit lid',
254
+ human: $t(`59197811-88ca-423c-b3b1-940d3c42704d`),
255
255
  field: 'memberId',
256
256
  });
257
257
  }
@@ -265,7 +265,7 @@ export class PatchBalanceItemsEndpoint extends Endpoint<Params, Query, Body, Res
265
265
  throw new SimpleError({
266
266
  code: 'permission_denied',
267
267
  message: 'No permission to link balance items to this user',
268
- human: 'Je hebt geen toegang om aanrekeningen te maken verbonden met deze gebruiker',
268
+ human: $t(`2ca03c93-d221-4a02-93be-9f187426f563`),
269
269
  field: 'userId',
270
270
  });
271
271
  }
@@ -52,7 +52,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
52
52
  throw new SimpleError({
53
53
  code: 'invalid_field',
54
54
  message: 'You need to add at least one balance item payment',
55
- human: 'Een betaling moet ten minste één item bestaan',
55
+ human: $t(`8b093b22-3e43-484d-b28c-da7f8db46aa0`),
56
56
  field: 'balanceItemPayments',
57
57
  });
58
58
  }
@@ -61,7 +61,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
61
61
  throw new SimpleError({
62
62
  code: 'invalid_field',
63
63
  message: 'Invalid payment method',
64
- human: 'Je kan zelf geen online betalingen aanmaken',
64
+ human: $t(`27b3013c-af5d-4c8e-9363-415c56b138b6`),
65
65
  field: 'method',
66
66
  });
67
67
  }
@@ -78,7 +78,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
78
78
  throw new SimpleError({
79
79
  code: 'invalid_field',
80
80
  message: 'Transfer settings are required',
81
- human: 'Je moet de overschrijvingsdetails invullen',
81
+ human: $t(`c6cdb321-3eb3-4689-aaed-0626e7ea0844`),
82
82
  field: 'transferSettings',
83
83
  });
84
84
  }
@@ -89,7 +89,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
89
89
  throw new SimpleError({
90
90
  code: 'invalid_field',
91
91
  message: 'Transfer description is required',
92
- human: 'Je moet een mededeling invullen voor de overschrijving',
92
+ human: $t(`16e792b1-bb90-4d09-b9c8-a1e382a2eafe`),
93
93
  field: 'transferDescription',
94
94
  });
95
95
  }
@@ -103,7 +103,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
103
103
  for (const item of put.balanceItemPayments) {
104
104
  const balanceItem = await BalanceItem.getByID(item.balanceItem.id);
105
105
  if (!balanceItem || balanceItem.organizationId !== organization.id) {
106
- throw Context.auth.notFoundOrNoAccess('Eén van de afrekeningen die je wilde markeren als betaald bestaat niet (meer).');
106
+ throw Context.auth.notFoundOrNoAccess($t(`701966b1-07e0-4ce6-9779-2193c1596825`));
107
107
  }
108
108
  balanceItems.push(balanceItem);
109
109
 
@@ -121,7 +121,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
121
121
 
122
122
  // Check permissions
123
123
  if (!(await Context.auth.canAccessBalanceItems(balanceItems, PermissionLevel.Write))) {
124
- throw Context.auth.error('Je hebt geen toegangsrechten tot één van de gekozen afrekeningen voor de betaling die je wilt aanmaken');
124
+ throw Context.auth.error($t(`19bc226c-2b4b-49bf-94a5-28e3d679ecc7`));
125
125
  }
126
126
 
127
127
  // Check total price
@@ -134,7 +134,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
134
134
  throw new SimpleError({
135
135
  code: 'invalid_field',
136
136
  message: 'The price should be greater than zero',
137
- human: 'Het totaalbedrag moet groter zijn dan 0 euro',
137
+ human: $t(`f4070936-7a8f-4373-af03-ca53200bb11b`),
138
138
  field: 'price',
139
139
  });
140
140
  }
@@ -147,7 +147,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
147
147
  throw new SimpleError({
148
148
  code: 'invalid_field',
149
149
  message: 'The price should be smaller than zero',
150
- human: 'Het totaalbedrag moet kleiner zijn dan 0 euro',
150
+ human: $t(`9ebba3e9-d126-4e87-971e-1563c17a122e`),
151
151
  field: 'price',
152
152
  });
153
153
  }
@@ -160,7 +160,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
160
160
  throw new SimpleError({
161
161
  code: 'invalid_field',
162
162
  message: 'Total price should be zero',
163
- human: 'Het totaalbedrag moet 0 euro zijn',
163
+ human: $t(`5b5ec7ff-d48f-4943-80c3-f3d0e4b32989`),
164
164
  field: 'price',
165
165
  });
166
166
  }
@@ -169,7 +169,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
169
169
  throw new SimpleError({
170
170
  code: 'missing_items',
171
171
  message: 'At least two items are required for a reallocation',
172
- human: 'Er moeten minstens twee items in een verrekening zitten',
172
+ human: $t(`7b2fb731-37d9-4601-82c4-162abb2803f9`),
173
173
  });
174
174
  }
175
175
  break;
@@ -216,7 +216,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
216
216
  throw new SimpleError({
217
217
  code: 'not_found',
218
218
  message: 'Payment not found',
219
- human: 'Deze betaling werd niet gevonden.',
219
+ human: $t(`29e79d32-8805-4458-84db-c90e591f9727`),
220
220
  });
221
221
  }
222
222
 
@@ -225,7 +225,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
225
225
  throw new SimpleError({
226
226
  code: 'invalid_field',
227
227
  message: 'Invalid payment method',
228
- human: 'Je kan online betalingen niet wijzigen',
228
+ human: $t(`6afcf043-3ccf-41e3-a840-36a18a8af809`),
229
229
  });
230
230
  }
231
231
  }
@@ -244,7 +244,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
244
244
  throw new SimpleError({
245
245
  code: 'invalid_field',
246
246
  message: 'Invalid payment method',
247
- human: 'De betaalmethode die je wilt gebruiken is niet toegestaan',
247
+ human: $t(`eb03febc-d83b-4789-bb4d-0d97d542edfa`),
248
248
  field: 'method',
249
249
  });
250
250
  }
@@ -74,7 +74,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
74
74
  throw new SimpleError({
75
75
  code: 'not_found',
76
76
  message: 'Period not found',
77
- human: 'Je kan geen wijzigingen meer aanbrengen in ' + period.getStructure().name + ' omdat deze is afgesloten',
77
+ human: $t(`d19ec3c4-6320-4f65-8890-74ba1a5bc375`) + ' ' + period.getStructure().name + ' ' + $t(`cb4b72b3-3990-4870-ab85-08ef7c7eadaa`),
78
78
  statusCode: 404,
79
79
  });
80
80
  }
@@ -120,7 +120,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
120
120
  }
121
121
 
122
122
  if (!await Context.auth.canCreateGroupInCategory(organization.id, category)) {
123
- throw Context.auth.error('Je hebt geen toegangsrechten om groepen toe te voegen in deze categorie');
123
+ throw Context.auth.error($t(`cdee3a74-4c03-46bc-ade1-7b8275a85400`));
124
124
  }
125
125
 
126
126
  // Only process puts
@@ -144,7 +144,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
144
144
  code: 'invalid_field',
145
145
  field: 'categories',
146
146
  message: 'Cannot have groups and categories combined',
147
- human: 'Een categorie kan niet zowel groepen als subcategorieën bevatten. Mogelijks zijn meerdere mensen tegelijk aanpassingen aan het maken aan de categorieën. Herlaadt de pagina en probeer opnieuw.',
147
+ human: $t(`c3834052-ea84-4d95-bc67-194e233cb11a`),
148
148
  });
149
149
  }
150
150
  }
@@ -165,7 +165,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
165
165
  const isDeleted = refCountAfter < refCountBefore;
166
166
 
167
167
  if (isDeleted) {
168
- throw Context.auth.error('Je hebt geen toegangsrechten om deze vergrendelde categorie te verwijderen.');
168
+ throw Context.auth.error($t(`55ddf8ff-ffcf-4577-bcbf-78c82637f245`));
169
169
  }
170
170
  }
171
171
 
@@ -173,11 +173,11 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
173
173
 
174
174
  if (!categoryAfter) {
175
175
  if (locked) {
176
- throw Context.auth.error('Je hebt geen toegangsrechten om deze vergrendelde categorie te verwijderen.');
176
+ throw Context.auth.error($t(`55ddf8ff-ffcf-4577-bcbf-78c82637f245`));
177
177
  }
178
178
  }
179
179
  else if (locked !== categoryAfter.settings.locked) {
180
- throw Context.auth.error('Je hebt geen toegangsrechten om deze categorie te vergrendelen of ontgrendelen.');
180
+ throw Context.auth.error($t(`a8982f4f-0c87-4f22-8f43-33ca26b73fc4`));
181
181
  }
182
182
 
183
183
  if (!locked || !categoryAfter) {
@@ -188,7 +188,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
188
188
  const settingsAfter = categoryAfter.settings;
189
189
 
190
190
  if (settingsBefore.name !== settingsAfter.name) {
191
- throw Context.auth.error('Je hebt geen toegangsrechten de naam van deze vergrendelde categorie te wijzigen.');
191
+ throw Context.auth.error($t(`2529c09e-f582-486a-b3eb-a8b96d8efa26`));
192
192
  }
193
193
  }
194
194
  }
@@ -256,7 +256,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
256
256
  throw new SimpleError({
257
257
  code: 'invalid_default_age_group',
258
258
  message: 'Invalid default age group',
259
- human: 'De standaard leeftijdsgroep is niet beschikbaar voor deze organisatie',
259
+ human: $t(`d81fc0f4-14e5-499c-9749-dd8941cdeb45`),
260
260
  statusCode: 400,
261
261
  });
262
262
  }
@@ -264,7 +264,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
264
264
  throw new SimpleError({
265
265
  code: 'invalid_default_age_group',
266
266
  message: 'Invalid default age group',
267
- human: 'De standaard leeftijdsgroep is ongeldig',
267
+ human: $t(`79672bcc-aa78-4d1d-ab82-f7c3c01ab4ff`),
268
268
  statusCode: 400,
269
269
  });
270
270
  }
@@ -313,7 +313,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
313
313
  static async deleteGroup(id: string) {
314
314
  const model = await Group.getByID(id);
315
315
  if (!model || !await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
316
- throw Context.auth.error('Je hebt geen toegangsrechten om deze groep te verwijderen');
316
+ throw Context.auth.error($t(`ab05b3fe-fb52-49d7-a7d5-2d3bcc6dde3e`));
317
317
  }
318
318
 
319
319
  model.deletedAt = new Date();
@@ -324,7 +324,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
324
324
  const model = await Group.getByID(struct.id);
325
325
 
326
326
  if (!model || !await Context.auth.canAccessGroup(model, PermissionLevel.Full)) {
327
- throw Context.auth.error('Je hebt geen toegangsrechten om deze groep te wijzigen');
327
+ throw Context.auth.error($t(`0d382103-4a15-4f12-8e3d-feb4d184639d`));
328
328
  }
329
329
 
330
330
  if (struct.settings) {
@@ -343,7 +343,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
343
343
  throw new SimpleError({
344
344
  code: 'missing_permissions',
345
345
  message: 'You cannot restrict your own permissions',
346
- human: 'Je kan je eigen volledige toegang tot deze inschrijvingsgroep niet verwijderen. Vraag aan een hoofdbeheerder om jouw toegang te verwijderen.',
346
+ human: $t(`8c220808-51af-4ea3-a941-9e9e39cd8d20`),
347
347
  });
348
348
  }
349
349
  }
@@ -423,7 +423,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
423
423
  code: 'invalid_field',
424
424
  field: 'waitingList',
425
425
  message: 'Waiting list group is already used in another period',
426
- human: 'Een wachtlijst kan momenteel niet gedeeld worden tussen verschillende werkjaren',
426
+ human: $t(`b906c293-2afa-4ebe-9b33-2d64ab46da34`),
427
427
  });
428
428
  }
429
429
 
@@ -461,7 +461,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
461
461
  // Ok
462
462
  }
463
463
  else {
464
- throw Context.auth.error('Je hebt geen toegangsrechten om groepen toe te voegen');
464
+ throw Context.auth.error($t(`153a7443-e2d9-4126-8e10-089b54964fb8`));
465
465
  }
466
466
  }
467
467
  }
@@ -500,7 +500,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
500
500
  throw new Error('Unexpected missing permissions');
501
501
  }
502
502
  const resourcePermissions = ResourcePermissions.create({
503
- resourceName: model.settings.name,
503
+ resourceName: model.settings.name.toString(),
504
504
  level: PermissionLevel.Full,
505
505
  });
506
506
  const patch = resourcePermissions.createInsertPatch(PermissionsResourceType.Groups, model.id, organizationPermissions);
@@ -513,7 +513,7 @@ export class PatchOrganizationRegistrationPeriodsEndpoint extends Endpoint<Param
513
513
  throw new SimpleError({
514
514
  code: 'missing_permissions',
515
515
  message: 'You cannot restrict your own permissions',
516
- human: 'Je kan geen inschrijvingsgroep maken zonder dat je zelf volledige toegang hebt tot de nieuwe groep',
516
+ human: $t(`4f4e52b6-288d-4893-a82b-15be8d07acbc`),
517
517
  });
518
518
  }
519
519
  }
@@ -46,7 +46,7 @@ export class ConnectMollieEndpoint extends Endpoint<Params, Query, Body, Respons
46
46
  });
47
47
  }
48
48
 
49
- const type = STAMHOOFD.STRIPE_CONNECT_METHOD;
49
+ const type = STAMHOOFD.STRIPE_CONNECT_METHOD ?? 'standard';
50
50
 
51
51
  const sharedData: Stripe.AccountCreateParams = {
52
52
  capabilities: {
@@ -39,10 +39,10 @@ export class DeleteStripeAccountEndpoint extends Endpoint<Params, Query, Body, R
39
39
  // Search account in database
40
40
  const model = await StripeAccount.getByID(request.params.id);
41
41
  if (!model || model.organizationId !== organization.id || model.status !== 'active') {
42
- throw Context.auth.notFoundOrNoAccess('Account niet gevonden');
42
+ throw Context.auth.notFoundOrNoAccess($t(`e5f6d64c-b3fe-4c8a-a711-ebdb14098c98`));
43
43
  }
44
44
 
45
- if (model.accountId === STAMHOOFD.STRIPE_ACCOUNT_ID) {
45
+ if (STAMHOOFD.STRIPE_ACCOUNT_ID && model.accountId === STAMHOOFD.STRIPE_ACCOUNT_ID) {
46
46
  throw new SimpleError({
47
47
  code: 'invalid_request',
48
48
  message: 'Je kan het hoofdaccount van het platform niet verwijderen.',
@@ -57,7 +57,7 @@ export class GetStripeAccountLinkEndpoint extends Endpoint<Params, Query, Body,
57
57
  // Search account in database
58
58
  const model = await StripeAccount.getByID(request.body.accountId);
59
59
  if (!model || model.organizationId !== organization.id || model.status !== 'active') {
60
- throw Context.auth.notFoundOrNoAccess('Account niet gevonden');
60
+ throw Context.auth.notFoundOrNoAccess($t(`e5f6d64c-b3fe-4c8a-a711-ebdb14098c98`));
61
61
  }
62
62
 
63
63
  // Get account
@@ -53,7 +53,7 @@ export class GetStripeLoginLinkEndpoint extends Endpoint<Params, Query, Body, Re
53
53
  if (!model || model.organizationId !== organization.id || model.status !== 'active') {
54
54
  throw new SimpleError({
55
55
  code: 'not_found',
56
- message: 'Account niet gevonden',
56
+ message: $t(`e5f6d64c-b3fe-4c8a-a711-ebdb14098c98`),
57
57
  statusCode: 400,
58
58
  });
59
59
  }
@@ -38,7 +38,7 @@ export class UpdateStripeAccountEndpoint extends Endpoint<Params, Query, Body, R
38
38
  // Search account in database
39
39
  const model = await StripeAccount.getByID(request.params.id);
40
40
  if (!model || model.organizationId !== organization.id) {
41
- throw Context.auth.notFoundOrNoAccess('Account niet gevonden');
41
+ throw Context.auth.notFoundOrNoAccess($t(`e5f6d64c-b3fe-4c8a-a711-ebdb14098c98`));
42
42
  }
43
43
 
44
44
  // Get account
@@ -0,0 +1,106 @@
1
+ import { Request } from '@simonbackx/simple-endpoints';
2
+ import { OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
3
+
4
+ import { testServer } from '../../../../../tests/helpers/TestServer';
5
+ import { PatchApiUserEndpoint } from './PatchApiUserEndpoint';
6
+ import { SHExpect, TestUtils } from '@stamhoofd/test-utils';
7
+ import { ApiUser, ApiUserRateLimits, PermissionLevel, Permissions, UserMeta, UserPermissions } from '@stamhoofd/structures';
8
+ import { CreateApiUserEndpoint } from './CreateApiUserEndpoint';
9
+
10
+ describe('Endpoint.CreateApiUserEndpoint', () => {
11
+ // Test endpoint
12
+ const endpoint = new CreateApiUserEndpoint();
13
+
14
+ beforeEach(() => {
15
+ TestUtils.setEnvironment('userMode', 'platform');
16
+ });
17
+
18
+ test('Only a platform admin can set the rate limits of a key', async () => {
19
+ const organization = await new OrganizationFactory({}).create();
20
+ const user = await new UserFactory({
21
+ globalPermissions: Permissions.create({
22
+ level: PermissionLevel.Full,
23
+ }),
24
+ }).create();
25
+ const token = await Token.createToken(user);
26
+
27
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
28
+ permissions: UserPermissions.create({
29
+ organizationPermissions: new Map([
30
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
31
+ ]),
32
+ }),
33
+ meta: UserMeta.create({
34
+ rateLimits: ApiUserRateLimits.High,
35
+ }),
36
+ }));
37
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
38
+ const response = await testServer.test(endpoint, createRequest);
39
+
40
+ expect(response.body).toBeDefined();
41
+ expect(response.body.meta?.rateLimits).toEqual(ApiUserRateLimits.High);
42
+ });
43
+
44
+ test('An organization admin cannot set rate limits', async () => {
45
+ const organization = await new OrganizationFactory({}).create();
46
+ const user = await new UserFactory({
47
+ permissions: Permissions.create({
48
+ level: PermissionLevel.Full,
49
+ }),
50
+ organization,
51
+ }).create();
52
+ const token = await Token.createToken(user);
53
+
54
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
55
+ permissions: UserPermissions.create({
56
+ organizationPermissions: new Map([
57
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
58
+ ]),
59
+ }),
60
+ meta: UserMeta.create({
61
+ rateLimits: ApiUserRateLimits.High,
62
+ }),
63
+ }));
64
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
65
+
66
+ await expect(testServer.test(endpoint, createRequest)).rejects.toThrow(SHExpect.simpleError({
67
+ code: 'permission_denied',
68
+ }));
69
+ });
70
+
71
+ test('An API-key cannot have permissions outside its organization', async () => {
72
+ const organization = await new OrganizationFactory({}).create();
73
+ const user = await new UserFactory({
74
+ permissions: Permissions.create({
75
+ level: PermissionLevel.Full,
76
+ }),
77
+ organization,
78
+ }).create();
79
+ const token = await Token.createToken(user);
80
+
81
+ const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
82
+ permissions: UserPermissions.create({
83
+ organizationPermissions: new Map([
84
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
85
+ ['other', Permissions.create({ level: PermissionLevel.Full })],
86
+ ]),
87
+ globalPermissions: Permissions.create({
88
+ level: PermissionLevel.Full,
89
+ }),
90
+ }),
91
+ }));
92
+ createRequest.headers.authorization = 'Bearer ' + token.accessToken;
93
+
94
+ const response = await testServer.test(endpoint, createRequest);
95
+
96
+ expect(response.body).toBeDefined();
97
+ expect(response.body.permissions).toEqual(
98
+ UserPermissions.create({
99
+ organizationPermissions: new Map([
100
+ [organization.id, Permissions.create({ level: PermissionLevel.Read })],
101
+ ]),
102
+ globalPermissions: null,
103
+ }),
104
+ );
105
+ });
106
+ });