@stamhoofd/backend 2.39.1 → 2.40.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 (198) hide show
  1. package/eslint.config.mjs +5 -0
  2. package/index.ts +81 -74
  3. package/jest.config.cjs +10 -0
  4. package/migrations.ts +16 -14
  5. package/package.json +11 -11
  6. package/src/crons/clear-excel-cache.test.ts +48 -50
  7. package/src/crons/clear-excel-cache.ts +18 -18
  8. package/src/crons/setup-steps.ts +2 -2
  9. package/src/crons.ts +325 -306
  10. package/src/decoders/StringArrayDecoder.ts +7 -7
  11. package/src/decoders/StringNullableDecoder.ts +1 -2
  12. package/src/email-recipient-loaders/members.ts +22 -22
  13. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +8 -9
  14. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +39 -40
  15. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +8 -8
  16. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +44 -45
  17. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +58 -57
  18. package/src/endpoints/auth/CreateAdminEndpoint.ts +48 -45
  19. package/src/endpoints/auth/CreateTokenEndpoint.test.ts +31 -31
  20. package/src/endpoints/auth/CreateTokenEndpoint.ts +146 -147
  21. package/src/endpoints/auth/DeleteTokenEndpoint.ts +7 -7
  22. package/src/endpoints/auth/DeleteUserEndpoint.ts +15 -15
  23. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +17 -18
  24. package/src/endpoints/auth/GetOtherUserEndpoint.ts +9 -10
  25. package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -35
  26. package/src/endpoints/auth/GetUserEndpoint.ts +5 -6
  27. package/src/endpoints/auth/PatchApiUserEndpoint.ts +35 -33
  28. package/src/endpoints/auth/PatchUserEndpoint.ts +55 -52
  29. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +9 -9
  30. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +8 -8
  31. package/src/endpoints/auth/SignupEndpoint.ts +37 -36
  32. package/src/endpoints/auth/VerifyEmailEndpoint.ts +29 -28
  33. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +33 -33
  34. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +7 -7
  35. package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +37 -37
  36. package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -30
  37. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +13 -13
  38. package/src/endpoints/global/email/GetEmailEndpoint.ts +13 -13
  39. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +16 -16
  40. package/src/endpoints/global/email/PatchEmailEndpoint.ts +25 -25
  41. package/src/endpoints/global/events/GetEventsEndpoint.ts +43 -44
  42. package/src/endpoints/global/events/PatchEventsEndpoint.ts +127 -172
  43. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +49 -50
  44. package/src/endpoints/global/files/GetFileCache.ts +13 -13
  45. package/src/endpoints/global/files/UploadFile.ts +51 -54
  46. package/src/endpoints/global/files/UploadImage.ts +53 -53
  47. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +25 -25
  48. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +24 -23
  49. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +8 -8
  50. package/src/endpoints/global/members/GetMembersEndpoint.ts +105 -102
  51. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +240 -239
  52. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +12 -14
  53. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +32 -33
  54. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +48 -57
  55. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +21 -22
  56. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +28 -28
  57. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +18 -18
  58. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +20 -20
  59. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +17 -17
  60. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +81 -75
  61. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +14 -14
  62. package/src/endpoints/global/platform/GetPlatformEnpoint.ts +11 -11
  63. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +71 -68
  64. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +27 -27
  65. package/src/endpoints/global/registration/GetUserBillingStatusEndpoint.ts +30 -30
  66. package/src/endpoints/global/registration/GetUserDetailedBillingStatusEndpoint.ts +34 -34
  67. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +26 -26
  68. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +12 -12
  69. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +90 -90
  70. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +118 -121
  71. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +362 -350
  72. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +8 -9
  73. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +21 -21
  74. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +65 -65
  75. package/src/endpoints/organization/dashboard/billing/GetOrganizationBillingStatusEndpoint.ts +9 -9
  76. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedBillingStatusEndpoint.ts +14 -14
  77. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +17 -17
  78. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +21 -21
  79. package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +15 -15
  80. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +52 -52
  81. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +37 -37
  82. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +14 -14
  83. package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +113 -112
  84. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +29 -29
  85. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +48 -47
  86. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +22 -21
  87. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +13 -14
  88. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +12 -13
  89. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +24 -24
  90. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +10 -12
  91. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +14 -14
  92. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +13 -13
  93. package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +12 -12
  94. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +120 -124
  95. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +172 -173
  96. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +88 -89
  97. package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +12 -12
  98. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +17 -17
  99. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +8 -8
  100. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +66 -67
  101. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +47 -47
  102. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +93 -91
  103. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +16 -17
  104. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +170 -167
  105. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +25 -24
  106. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +22 -23
  107. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +22 -22
  108. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +17 -18
  109. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +8 -9
  110. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +17 -18
  111. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -15
  112. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +19 -19
  113. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +19 -19
  114. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +14 -14
  115. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +12 -12
  116. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +103 -100
  117. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +11 -12
  118. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +15 -15
  119. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +14 -14
  120. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +14 -14
  121. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +23 -23
  122. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +54 -52
  123. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +84 -81
  124. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +120 -111
  125. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +24 -24
  126. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +18 -18
  127. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +141 -130
  128. package/src/endpoints/organization/shared/GetDocumentHtml.ts +25 -25
  129. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +18 -18
  130. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +36 -37
  131. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +9 -9
  132. package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +11 -11
  133. package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +28 -27
  134. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +20 -20
  135. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +22 -22
  136. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -14
  137. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +57 -56
  138. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +65 -66
  139. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +18 -17
  140. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +124 -128
  141. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +154 -145
  142. package/src/excel-loaders/members.ts +102 -103
  143. package/src/excel-loaders/payments.ts +155 -156
  144. package/src/helpers/AddressValidator.test.ts +32 -32
  145. package/src/helpers/AddressValidator.ts +128 -122
  146. package/src/helpers/AdminPermissionChecker.ts +339 -236
  147. package/src/helpers/AuthenticatedStructures.ts +233 -134
  148. package/src/helpers/BuckarooHelper.ts +134 -134
  149. package/src/helpers/CheckSettlements.ts +94 -88
  150. package/src/helpers/Context.ts +87 -86
  151. package/src/helpers/CookieHelper.ts +23 -22
  152. package/src/helpers/EmailResumer.ts +10 -10
  153. package/src/helpers/FileCache.ts +62 -62
  154. package/src/helpers/ForwardHandler.test.ts +122 -124
  155. package/src/helpers/ForwardHandler.ts +76 -70
  156. package/src/helpers/MemberUserSyncer.ts +101 -96
  157. package/src/helpers/MembershipCharger.ts +69 -69
  158. package/src/helpers/MembershipHelper.ts +11 -12
  159. package/src/helpers/OpenIDConnectHelper.ts +85 -82
  160. package/src/helpers/PeriodHelper.ts +65 -70
  161. package/src/helpers/StripeHelper.ts +146 -137
  162. package/src/helpers/StripePayoutChecker.ts +51 -52
  163. package/src/helpers/ViesHelper.ts +46 -44
  164. package/src/helpers/fetchToAsyncIterator.ts +14 -14
  165. package/src/helpers/xlsxAddressTransformerColumnFactory.ts +50 -52
  166. package/src/middleware/ContextMiddleware.ts +5 -5
  167. package/src/migrations/1646578856-validate-addresses.ts +6 -9
  168. package/src/seeds/0000000000-example.ts +3 -5
  169. package/src/seeds/1715028563-user-permissions.ts +16 -18
  170. package/src/seeds/1722256498-group-update-occupancy.ts +12 -12
  171. package/src/seeds/1722344162-sync-member-users.ts +14 -15
  172. package/src/seeds/1722344162-update-membership.ts +6 -6
  173. package/src/seeds/1726055544-balance-item-paid.ts +4 -4
  174. package/src/seeds/1726055545-balance-item-pending.ts +4 -4
  175. package/src/seeds/1726494419-update-cached-outstanding-balance.ts +16 -16
  176. package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +12 -12
  177. package/src/seeds/1726572303-schedule-stock-updates.ts +12 -12
  178. package/src/seeds/1726847064-setup-steps.ts +16 -0
  179. package/src/sql-filters/balance-item-payments.ts +7 -7
  180. package/src/sql-filters/events.ts +14 -14
  181. package/src/sql-filters/members.ts +96 -96
  182. package/src/sql-filters/organizations.ts +139 -75
  183. package/src/sql-filters/payments.ts +28 -28
  184. package/src/sql-filters/registrations.ts +14 -14
  185. package/src/sql-sorters/events.ts +25 -25
  186. package/src/sql-sorters/members.ts +26 -26
  187. package/src/sql-sorters/organizations.ts +36 -36
  188. package/src/sql-sorters/payments.ts +26 -26
  189. package/tests/e2e/stock.test.ts +616 -621
  190. package/tests/e2e/tickets.test.ts +255 -260
  191. package/tests/helpers/StripeMocker.ts +177 -179
  192. package/tests/helpers/TestServer.ts +9 -9
  193. package/tests/jest.global.setup.ts +14 -13
  194. package/tests/jest.setup.ts +33 -32
  195. package/.eslintrc.js +0 -61
  196. package/jest.config.js +0 -11
  197. package/src/helpers/SetupStepsUpdater.ts +0 -359
  198. package/src/seeds/1724076679-setup-steps.ts +0 -16
@@ -1,62 +1,62 @@
1
- import { AutoEncoderPatchType, PatchMap } from "@simonbackx/simple-encoding"
2
- import { SimpleError } from "@simonbackx/simple-errors"
3
- import { BalanceItem, CachedOutstandingBalance, Document, DocumentTemplate, EmailTemplate, Event, Group, Member, MemberPlatformMembership, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, Registration, User, Webshop } from "@stamhoofd/models"
4
- import { AccessRight, FinancialSupportSettings, GroupCategory, GroupStatus, MemberWithRegistrationsBlob, PermissionLevel, PermissionsResourceType, Platform as PlatformStruct, RecordCategory } from "@stamhoofd/structures"
5
- import { Formatter } from "@stamhoofd/utility"
1
+ import { AutoEncoderPatchType, PatchMap } from '@simonbackx/simple-encoding';
2
+ import { SimpleError } from '@simonbackx/simple-errors';
3
+ import { BalanceItem, CachedOutstandingBalance, Document, DocumentTemplate, EmailTemplate, Event, Group, Member, MemberPlatformMembership, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, Registration, User, Webshop } from '@stamhoofd/models';
4
+ import { AccessRight, FinancialSupportSettings, GroupCategory, GroupStatus, LoadedPermissions, MemberWithRegistrationsBlob, PermissionLevel, PermissionsResourceType, Platform as PlatformStruct, RecordCategory } from '@stamhoofd/structures';
5
+ import { Formatter } from '@stamhoofd/utility';
6
6
 
7
7
  /**
8
8
  * One class with all the responsabilities of checking permissions to each resource in the system by a given user, possibly in an organization context.
9
9
  * This helps when dependencies of permissions change, such as parent categories for groups
10
10
  */
11
11
  export class AdminPermissionChecker {
12
- organization: Organization|null
13
- user: User
12
+ organization: Organization | null;
13
+ user: User;
14
14
  /**
15
15
  * The member that is linked to this user = is this user
16
16
  */
17
- member: MemberWithRegistrations|null = null
18
- platform: PlatformStruct
17
+ member: MemberWithRegistrations | null = null;
18
+ platform: PlatformStruct;
19
19
 
20
- organizationCache: Map<string, Organization|Promise<Organization|undefined>> = new Map()
21
- organizationGroupsCache: Map<string, Group[]|Promise<Group[]>> = new Map()
20
+ organizationCache: Map<string, Organization | Promise<Organization | undefined>> = new Map();
21
+ organizationGroupsCache: Map<string, Group[] | Promise<Group[]>> = new Map();
22
22
 
23
- constructor(user: User, platform: PlatformStruct, organization?: Organization,) {
24
- this.user = user
25
- this.platform = platform
23
+ constructor(user: User, platform: PlatformStruct, organization?: Organization) {
24
+ this.user = user;
25
+ this.platform = platform;
26
26
 
27
27
  if (user.organizationId && (!organization || organization.id !== user.organizationId)) {
28
28
  throw new SimpleError({
29
29
  code: 'invalid_scope',
30
30
  message: 'Tried accessing a resource without an organization context, but this user is limited to the organization context',
31
- statusCode: 403
32
- })
31
+ statusCode: 403,
32
+ });
33
33
  }
34
34
 
35
- this.organization = organization ?? null
35
+ this.organization = organization ?? null;
36
36
  }
37
37
 
38
- async getOrganization(id: string|Organization): Promise<Organization> {
38
+ async getOrganization(id: string | Organization): Promise<Organization> {
39
39
  if (this.organization && id === this.organization.id) {
40
- return this.organization
40
+ return this.organization;
41
41
  }
42
42
  if (typeof id === 'string') {
43
43
  const c = this.organizationCache.get(id);
44
44
  if (c) {
45
45
  const result = await c;
46
46
  if (!result) {
47
- throw new Error('Unexpected missing organization in AdminPermissionChecker.getOrganization')
47
+ throw new Error('Unexpected missing organization in AdminPermissionChecker.getOrganization');
48
48
  }
49
49
  return result;
50
50
  }
51
- const promise = Organization.getByID(id)
52
- this.organizationCache.set(id, promise)
51
+ const promise = Organization.getByID(id);
52
+ this.organizationCache.set(id, promise);
53
53
  const result = await promise;
54
54
  if (!result) {
55
- console.error('Unexpected missing organization in AdminPermissionChecker.getOrganization', id)
56
- this.organizationCache.delete(id)
57
- throw new Error('Unexpected missing organization in AdminPermissionChecker.getOrganization')
55
+ console.error('Unexpected missing organization in AdminPermissionChecker.getOrganization', id);
56
+ this.organizationCache.delete(id);
57
+ throw new Error('Unexpected missing organization in AdminPermissionChecker.getOrganization');
58
58
  }
59
- this.organizationCache.set(id, result)
59
+ this.organizationCache.set(id, result);
60
60
  return result;
61
61
  }
62
62
  return id;
@@ -67,52 +67,52 @@ export class AdminPermissionChecker {
67
67
  if (c) {
68
68
  return await c;
69
69
  }
70
- const organization = await this.getOrganization(id)
71
- const promise = Group.getAll(id, organization.periodId, true)
72
- this.organizationGroupsCache.set(id, promise)
70
+ const organization = await this.getOrganization(id);
71
+ const promise = Group.getAll(id, organization.periodId, true);
72
+ this.organizationGroupsCache.set(id, promise);
73
73
  const result = await promise;
74
- this.organizationGroupsCache.set(id, result)
74
+ this.organizationGroupsCache.set(id, result);
75
75
  return result;
76
76
  }
77
77
 
78
- async getOrganizationCurrentPeriod(id: string|Organization): Promise<OrganizationRegistrationPeriod> {
79
- const organization = await this.getOrganization(id);
80
- return await organization.getPeriod()
78
+ async getOrganizationCurrentPeriod(id: string | Organization): Promise<OrganizationRegistrationPeriod> {
79
+ const organization = await this.getOrganization(id);
80
+ return await organization.getPeriod();
81
81
  }
82
82
 
83
83
  error(message?: string): SimpleError {
84
84
  return new SimpleError({
85
- code: "permission_denied",
86
- message: "You do not have permissions for this action",
85
+ code: 'permission_denied',
86
+ message: 'You do not have permissions for this action',
87
87
  human: message ?? 'Je hebt geen toegangsrechten voor deze actie',
88
- statusCode: 403
89
- })
88
+ statusCode: 403,
89
+ });
90
90
  }
91
91
 
92
92
  notFoundOrNoAccess(message?: string): SimpleError {
93
93
  return new SimpleError({
94
- code: "not_found",
95
- message: "Resource not found or no access",
94
+ code: 'not_found',
95
+ message: 'Resource not found or no access',
96
96
  human: message ?? 'Niet gevonden of geen toegang tot dit object',
97
- statusCode: 404
98
- })
97
+ statusCode: 404,
98
+ });
99
99
  }
100
100
 
101
101
  get platformPermissions() {
102
- return this.user.permissions?.forPlatform(this.platform)
102
+ return this.user.permissions?.forPlatform(this.platform);
103
103
  }
104
-
105
- async getOrganizationPermissions(organizationOrId: string|Organization) {
104
+
105
+ async getOrganizationPermissions(organizationOrId: string | Organization) {
106
106
  if (!this.user.permissions) {
107
107
  return null;
108
108
  }
109
- const organization = await this.getOrganization(organizationOrId)
109
+ const organization = await this.getOrganization(organizationOrId);
110
110
 
111
111
  const p = this.user.permissions.forOrganization(
112
112
  organization,
113
- this.platform
114
- )
115
- return p
113
+ this.platform,
114
+ );
115
+ return p;
116
116
  }
117
117
 
118
118
  async canAccessPrivateOrganizationData(organization: Organization) {
@@ -120,27 +120,28 @@ export class AdminPermissionChecker {
120
120
  return false;
121
121
  }
122
122
 
123
- if (!await this.hasSomeAccess(organization.id)) {
123
+ if (!await this.hasSomeAccess(organization)) {
124
124
  return false;
125
125
  }
126
126
  return true;
127
127
  }
128
128
 
129
- checkScope(organizationId: string|null) {
129
+ checkScope(organizationId: string | null) {
130
130
  if (organizationId) {
131
131
  // If request is scoped to a different organization
132
132
  if (this.organization && organizationId !== this.organization.id) {
133
- return false
133
+ return false;
134
134
  }
135
135
 
136
136
  // If user is limited to scope
137
137
  if (this.user.organizationId && organizationId !== this.user.organizationId) {
138
- return false
138
+ return false;
139
139
  }
140
- } else {
140
+ }
141
+ else {
141
142
  // User is limited to a scope
142
143
  if (this.user.organizationId) {
143
- return false
144
+ return false;
144
145
  }
145
146
  }
146
147
 
@@ -150,13 +151,13 @@ export class AdminPermissionChecker {
150
151
  async canAccessGroup(group: Group, permissionLevel: PermissionLevel = PermissionLevel.Read): Promise<boolean> {
151
152
  // Check permissions aren't scoped to a specific organization, and they mismatch
152
153
  if (!this.checkScope(group.organizationId)) {
153
- return false
154
+ return false;
154
155
  }
155
- const organization = await this.getOrganization(group.organizationId)
156
+ const organization = await this.getOrganization(group.organizationId);
156
157
 
157
158
  if (group.periodId !== organization.periodId) {
158
159
  if (!await this.hasFullAccess(group.organizationId)) {
159
- return false
160
+ return false;
160
161
  }
161
162
  }
162
163
 
@@ -164,7 +165,7 @@ export class AdminPermissionChecker {
164
165
  return await this.canAccessArchivedGroups(group.organizationId);
165
166
  }
166
167
 
167
- const organizationPermissions = await this.getOrganizationPermissions(group.organizationId)
168
+ const organizationPermissions = await this.getOrganizationPermissions(group.organizationId);
168
169
 
169
170
  if (!organizationPermissions) {
170
171
  return false;
@@ -176,41 +177,142 @@ export class AdminPermissionChecker {
176
177
  }
177
178
 
178
179
  // Check parent categories
179
- const organizationPeriod = await this.getOrganizationCurrentPeriod(organization)
180
- const parentCategories = group.getParentCategories(organizationPeriod.settings.categories)
180
+ const organizationPeriod = await this.getOrganizationCurrentPeriod(organization);
181
+ const parentCategories = group.getParentCategories(organizationPeriod.settings.categories);
181
182
  for (const category of parentCategories) {
182
183
  if (organizationPermissions.hasResourceAccess(PermissionsResourceType.GroupCategories, category.id, permissionLevel)) {
183
- return true
184
+ return true;
184
185
  }
185
186
  }
186
187
 
187
188
  return false;
188
189
  }
189
190
 
190
- async canAccessEvent(event: Event, permissionLevel: PermissionLevel = PermissionLevel.Read): Promise<boolean> {
191
- // Check permissions aren't scoped to a specific organization, and they mismatch
192
- if (!this.checkScope(event.organizationId)) {
193
- return false
194
- }
191
+ /**
192
+ * Will throw error if not allowed to edit/add/delete this event
193
+ * @param event
194
+ * @returns Organization if event for specific organization, else null
195
+ * @throws error if not allowed to write this event
196
+ */
197
+ async checkEventAccess(event: Event): Promise<Organization | null> {
198
+ const accessRight: AccessRight = AccessRight.EventWrite;
199
+
200
+ // #region organization and groups
201
+ if (event.organizationId !== null) {
202
+ let organization: Organization | null = null;
203
+ let organizationPermissions: LoadedPermissions | null = null;
204
+
205
+ try {
206
+ organization = await this.getOrganization(event.organizationId);
207
+ organizationPermissions = await this.getOrganizationPermissions(organization);
208
+ }
209
+ catch (error) {
210
+ console.error(error);
211
+ throw new SimpleError({
212
+ code: 'not_found',
213
+ message: 'Event not found',
214
+ human: 'De activiteit werd niet gevonden',
215
+ });
216
+ }
195
217
 
196
- if (permissionLevel !== PermissionLevel.Read) {
197
- if (event.organizationId) {
198
- // Need full access for now
199
- if (!await this.hasFullAccess(event.organizationId)) {
200
- return false
218
+ if (organizationPermissions === null) {
219
+ throw new SimpleError({
220
+ code: 'permission_denied',
221
+ message: 'Je hebt geen toegangsrechten om een activiteit te beheren voor deze organisatie.',
222
+ statusCode: 403,
223
+ });
224
+ }
225
+
226
+ if (event.meta.groups === null) {
227
+ if (!organizationPermissions.hasResourceAccessRight(PermissionsResourceType.Groups, '', accessRight)) {
228
+ throw new SimpleError({
229
+ code: 'permission_denied',
230
+ message: 'Je hebt geen toegangsrechten om een activiteit te beheren voor deze organisatie.',
231
+ statusCode: 403,
232
+ });
201
233
  }
202
- } else {
203
- if (!this.hasPlatformFullAccess()) {
204
- return false
234
+ }
235
+ else {
236
+ for (const group of event.meta.groups) {
237
+ if (!organizationPermissions.hasResourceAccessRight(PermissionsResourceType.Groups, group.id, accessRight)) {
238
+ throw new SimpleError({
239
+ code: 'permission_denied',
240
+ message: 'Je hebt geen toegangsrechten om een activiteit te beheren voor deze groep(en).',
241
+ statusCode: 403,
242
+ });
243
+ }
205
244
  }
206
245
  }
246
+
247
+ if (event.meta.organizationTagIds !== null) {
248
+ // not supported currently
249
+ throw new SimpleError({
250
+ code: 'invalid_field',
251
+ message: 'Een activiteit voor een organisatie kan geen tags bevatten.',
252
+ statusCode: 403,
253
+ });
254
+ }
255
+
256
+ if (event.meta.defaultAgeGroupIds !== null) {
257
+ // not supported currently
258
+ throw new SimpleError({
259
+ code: 'invalid_field',
260
+ message: 'Een activiteit voor een organisatie kan niet beperkt worden tot specifieke standaard leeftijdsgroepen.',
261
+ statusCode: 403,
262
+ });
263
+ }
264
+
265
+ return organization;
207
266
  }
267
+ // #endregion
208
268
 
209
- return true;
269
+ // #region platform
270
+ if (event.meta.groups !== null) {
271
+ // not supported currently
272
+ throw new SimpleError({
273
+ code: 'permission_denied',
274
+ message: 'Een nationale of regionale activiteit kan (momenteel) niet beperkt worden tot specifieke groepen.',
275
+ statusCode: 403,
276
+ });
277
+ }
278
+
279
+ const platformPermissions = this.platformPermissions;
280
+ if (!platformPermissions) {
281
+ throw new SimpleError({
282
+ code: 'permission_denied',
283
+ message: 'Je hebt geen toegangsrechten om een nationale of regionale activiteit te beheren.',
284
+ statusCode: 403,
285
+ });
286
+ }
287
+
288
+ // organization tags
289
+ if (event.meta.organizationTagIds === null) {
290
+ if (!(platformPermissions.hasAccessRight(accessRight) || platformPermissions.hasResourceAccessRight(PermissionsResourceType.OrganizationTags, '', accessRight))) {
291
+ throw new SimpleError({
292
+ code: 'permission_denied',
293
+ message: 'Je kan geen nationale activiteiten beheren',
294
+ statusCode: 403,
295
+ });
296
+ }
297
+ }
298
+ else {
299
+ for (const tagId of event.meta.organizationTagIds) {
300
+ if (!platformPermissions.hasResourceAccessRight(PermissionsResourceType.OrganizationTags, tagId, accessRight)) {
301
+ throw new SimpleError({
302
+ code: 'permission_denied',
303
+ message: "Je hebt geen toegangsrechten om een nationale of regionale activiteit te beheren voor deze regio('s).",
304
+ statusCode: 403,
305
+ });
306
+ }
307
+ }
308
+ }
309
+ // #endregion
310
+
311
+ return null;
210
312
  }
211
313
 
212
314
  async canAccessArchivedGroups(organizationId: string) {
213
- return await this.hasFullAccess(organizationId)
315
+ return await this.hasFullAccess(organizationId);
214
316
  }
215
317
 
216
318
  async canAccessMember(member: MemberWithRegistrations, permissionLevel: PermissionLevel = PermissionLevel.Read) {
@@ -220,15 +322,15 @@ export class AdminPermissionChecker {
220
322
 
221
323
  // Check user has permissions
222
324
  if (!this.user.permissions) {
223
- return false
325
+ return false;
224
326
  }
225
327
 
226
328
  if (this.hasPlatformFullAccess()) {
227
- return true
329
+ return true;
228
330
  }
229
331
 
230
332
  if (member.organizationId && await this.hasFullAccess(member.organizationId, permissionLevel)) {
231
- return true
333
+ return true;
232
334
  }
233
335
 
234
336
  if (member.registrations.length === 0 && permissionLevel !== PermissionLevel.Full && (this.organization && await this.hasFullAccess(this.organization.id, PermissionLevel.Full))) {
@@ -251,12 +353,12 @@ export class AdminPermissionChecker {
251
353
  */
252
354
  async canDeleteMember(member: MemberWithRegistrations) {
253
355
  if (member.registrations.length === 0 && this.isUserManager(member)) {
254
- const platformMemberships = await MemberPlatformMembership.where({ memberId: member.id })
356
+ const platformMemberships = await MemberPlatformMembership.where({ memberId: member.id });
255
357
  if (platformMemberships.length === 0) {
256
358
  return true;
257
359
  }
258
360
 
259
- const cachedBalance = await CachedOutstandingBalance.getForObjects([member.id])
361
+ const cachedBalance = await CachedOutstandingBalance.getForObjects([member.id]);
260
362
  if (cachedBalance.length === 0 || (cachedBalance[0].amount === 0 && cachedBalance[0].amountPending === 0)) {
261
363
  return true;
262
364
  }
@@ -264,16 +366,16 @@ export class AdminPermissionChecker {
264
366
 
265
367
  if (member.organizationId) {
266
368
  // Not a platform
267
- return await this.hasFullAccess(member.organizationId)
369
+ return await this.hasFullAccess(member.organizationId);
268
370
  }
269
- return this.hasPlatformFullAccess()
371
+ return this.hasPlatformFullAccess();
270
372
  }
271
373
 
272
374
  /**
273
375
  * Note: only checks admin permissions. Users that 'own' this member can also access it but that does not use the AdminPermissionChecker
274
376
  */
275
377
  async canAccessRegistration(registration: Registration, permissionLevel: PermissionLevel = PermissionLevel.Read) {
276
- const organizationPermissions = await this.getOrganizationPermissions(registration.organizationId)
378
+ const organizationPermissions = await this.getOrganizationPermissions(registration.organizationId);
277
379
 
278
380
  if (!organizationPermissions) {
279
381
  return false;
@@ -289,8 +391,8 @@ export class AdminPermissionChecker {
289
391
  return false;
290
392
  }
291
393
 
292
- const allGroups = await this.getOrganizationGroups(registration.organizationId)
293
- const group = allGroups.find(g => g.id === registration.groupId)
394
+ const allGroups = await this.getOrganizationGroups(registration.organizationId);
395
+ const group = allGroups.find(g => g.id === registration.groupId);
294
396
  if (!group || group.deletedAt) {
295
397
  return false;
296
398
  }
@@ -303,7 +405,7 @@ export class AdminPermissionChecker {
303
405
  }
304
406
 
305
407
  async canAccessWebshop(webshop: Webshop, permissionLevel: PermissionLevel = PermissionLevel.Read) {
306
- const organizationPermissions = await this.getOrganizationPermissions(webshop.organizationId)
408
+ const organizationPermissions = await this.getOrganizationPermissions(webshop.organizationId);
307
409
 
308
410
  if (!organizationPermissions) {
309
411
  return false;
@@ -322,10 +424,10 @@ export class AdminPermissionChecker {
322
424
 
323
425
  async canAccessWebshopTickets(webshop: Webshop, permissionLevel: PermissionLevel = PermissionLevel.Read) {
324
426
  if (!this.checkScope(webshop.organizationId)) {
325
- return false
427
+ return false;
326
428
  }
327
429
 
328
- const organizationPermissions = await this.getOrganizationPermissions(webshop.organizationId)
430
+ const organizationPermissions = await this.getOrganizationPermissions(webshop.organizationId);
329
431
 
330
432
  if (!organizationPermissions) {
331
433
  return false;
@@ -347,7 +449,7 @@ export class AdminPermissionChecker {
347
449
  }
348
450
 
349
451
  async canAccessPayment(payment: Payment, permissionLevel: PermissionLevel = PermissionLevel.Read) {
350
- return await this.canAccessPayments([payment], permissionLevel)
452
+ return await this.canAccessPayments([payment], permissionLevel);
351
453
  }
352
454
 
353
455
  async canAccessPayments(payments: Payment[], permissionLevel: PermissionLevel = PermissionLevel.Read) {
@@ -362,7 +464,7 @@ export class AdminPermissionChecker {
362
464
  return false;
363
465
  }
364
466
 
365
- const organizationId = payments[0].organizationId
467
+ const organizationId = payments[0].organizationId;
366
468
  for (const item of payments) {
367
469
  if (item.organizationId !== organizationId) {
368
470
  // Cannot merge multiple organizations for now
@@ -372,24 +474,24 @@ export class AdminPermissionChecker {
372
474
 
373
475
  // First try without queries
374
476
  if (!organizationId) {
375
- return this.hasPlatformFullAccess()
477
+ return this.hasPlatformFullAccess();
376
478
  }
377
479
 
378
480
  if (await this.canManagePayments(organizationId)) {
379
481
  return true;
380
482
  }
381
483
 
382
- const {balanceItems} = await Payment.loadBalanceItems(payments)
383
- return await this.canAccessBalanceItems(balanceItems, permissionLevel)
484
+ const { balanceItems } = await Payment.loadBalanceItems(payments);
485
+ return await this.canAccessBalanceItems(balanceItems, permissionLevel);
384
486
  }
385
487
 
386
488
  async canAccessBalanceItems(
387
489
  balanceItems: BalanceItem[],
388
490
  permissionLevel: PermissionLevel = PermissionLevel.Read,
389
491
  data?: {
390
- registrations: Registration[],
391
- orders: Order[]
392
- }
492
+ registrations: Registration[];
493
+ orders: Order[];
494
+ },
393
495
  ): Promise<boolean> {
394
496
  for (const balanceItem of balanceItems) {
395
497
  if (!this.checkScope(balanceItem.organizationId)) {
@@ -402,7 +504,7 @@ export class AdminPermissionChecker {
402
504
  return false;
403
505
  }
404
506
 
405
- const organizationId = balanceItems[0].organizationId
507
+ const organizationId = balanceItems[0].organizationId;
406
508
  for (const item of balanceItems) {
407
509
  if (item.organizationId !== organizationId) {
408
510
  // Cannot merge multiple organizations for now
@@ -424,7 +526,7 @@ export class AdminPermissionChecker {
424
526
  }
425
527
 
426
528
  // Slight optimization possible here
427
- const {registrations, orders} = data ?? (this.user.permissions || permissionLevel === PermissionLevel.Read) ? (await Payment.loadBalanceItemRelations(balanceItems)) : {registrations: [], orders: []}
529
+ const { registrations, orders } = data ?? (this.user.permissions || permissionLevel === PermissionLevel.Read) ? (await Payment.loadBalanceItemRelations(balanceItems)) : { registrations: [], orders: [] };
428
530
 
429
531
  if (this.user.permissions) {
430
532
  // We grant permission for a whole payment when the user has at least permission for a part of that payment.
@@ -434,12 +536,12 @@ export class AdminPermissionChecker {
434
536
  }
435
537
  }
436
538
 
437
- const webshopCache: Map<string, Webshop> = new Map()
539
+ const webshopCache: Map<string, Webshop> = new Map();
438
540
 
439
541
  for (const order of orders) {
440
- const webshop = webshopCache.get(order.webshopId) ?? await Webshop.getByID(order.webshopId)
542
+ const webshop = webshopCache.get(order.webshopId) ?? await Webshop.getByID(order.webshopId);
441
543
  if (webshop) {
442
- webshopCache.set(order.webshopId, webshop)
544
+ webshopCache.set(order.webshopId, webshop);
443
545
 
444
546
  if (await this.canAccessWebshop(webshop, permissionLevel)) {
445
547
  return true;
@@ -450,7 +552,7 @@ export class AdminPermissionChecker {
450
552
 
451
553
  if (permissionLevel === PermissionLevel.Read) {
452
554
  // Check members
453
- const userMembers = await Member.getMembersWithRegistrationForUser(this.user)
555
+ const userMembers = await Member.getMembersWithRegistrationForUser(this.user);
454
556
  for (const member of userMembers) {
455
557
  if (balanceItems.find(m => m.memberId === member.id)) {
456
558
  return true;
@@ -463,23 +565,23 @@ export class AdminPermissionChecker {
463
565
 
464
566
  async canAccessDocumentTemplate(documentTemplate: DocumentTemplate, _: PermissionLevel = PermissionLevel.Read) {
465
567
  if (!this.checkScope(documentTemplate.organizationId)) {
466
- return false
568
+ return false;
467
569
  }
468
570
 
469
- return await this.hasFullAccess(documentTemplate.organizationId)
571
+ return await this.hasFullAccess(documentTemplate.organizationId);
470
572
  }
471
573
 
472
574
  async canAccessDocument(document: Document, level: PermissionLevel = PermissionLevel.Read) {
473
575
  if (!this.checkScope(document.organizationId)) {
474
- return false
576
+ return false;
475
577
  }
476
578
 
477
579
  if (await this.hasFullAccess(document.organizationId)) {
478
- return true
580
+ return true;
479
581
  }
480
582
 
481
583
  if (level === PermissionLevel.Read && document.memberId) {
482
- const members = await Member.getMembersWithRegistrationForUser(this.user)
584
+ const members = await Member.getMembersWithRegistrationForUser(this.user);
483
585
 
484
586
  if (members.find(m => m.id == document.memberId)) {
485
587
  return true;
@@ -501,7 +603,7 @@ export class AdminPermissionChecker {
501
603
  }
502
604
 
503
605
  if (!user.organizationId) {
504
- return this.hasPlatformFullAccess()
606
+ return this.hasPlatformFullAccess();
505
607
  }
506
608
 
507
609
  return await this.canManageAdmins(user.organizationId);
@@ -514,19 +616,19 @@ export class AdminPermissionChecker {
514
616
 
515
617
  if (user.organizationId) {
516
618
  // normal behaviour
517
- return this.canAccessUser(user, PermissionLevel.Write)
619
+ return this.canAccessUser(user, PermissionLevel.Write);
518
620
  }
519
621
 
520
622
  // platform user: only allowed to change names if not platform admins
521
623
  if (user.permissions?.globalPermissions) {
522
- return this.hasPlatformFullAccess()
624
+ return this.hasPlatformFullAccess();
523
625
  }
524
626
 
525
- return this.canAccessUser(user, PermissionLevel.Write)
627
+ return this.canAccessUser(user, PermissionLevel.Write);
526
628
  }
527
629
 
528
630
  async canEditUserEmail(user: User) {
529
- return this.canEditUserName(user)
631
+ return this.canEditUserName(user);
530
632
  }
531
633
 
532
634
  async canAccessEmailTemplate(template: EmailTemplate, level: PermissionLevel = PermissionLevel.Read) {
@@ -537,7 +639,7 @@ export class AdminPermissionChecker {
537
639
  }
538
640
  return this.canReadEmailTemplates(template.organizationId);
539
641
  }
540
-
642
+
541
643
  // Note: if the template has an organizationId of null, everyone can access it, but only for reading
542
644
  // that is why we only check the scope afterwards
543
645
  if (!this.checkScope(template.organizationId)) {
@@ -545,7 +647,7 @@ export class AdminPermissionChecker {
545
647
  }
546
648
 
547
649
  if (!template.organizationId) {
548
- return this.hasPlatformFullAccess()
650
+ return this.hasPlatformFullAccess();
549
651
  }
550
652
 
551
653
  if (await this.hasFullAccess(template.organizationId)) {
@@ -553,7 +655,7 @@ export class AdminPermissionChecker {
553
655
  }
554
656
 
555
657
  if (template.webshopId) {
556
- const webshop = await Webshop.getByID(template.webshopId)
658
+ const webshop = await Webshop.getByID(template.webshopId);
557
659
  if (!webshop || !(await this.canAccessWebshop(webshop, PermissionLevel.Full))) {
558
660
  return false;
559
661
  }
@@ -562,7 +664,7 @@ export class AdminPermissionChecker {
562
664
  }
563
665
 
564
666
  if (template.groupId) {
565
- const group = await Group.getByID(template.groupId)
667
+ const group = await Group.getByID(template.groupId);
566
668
  if (!group || !(await this.canAccessGroup(group, PermissionLevel.Full))) {
567
669
  return false;
568
670
  }
@@ -572,7 +674,7 @@ export class AdminPermissionChecker {
572
674
 
573
675
  return false;
574
676
  }
575
-
677
+
576
678
  async canLinkBalanceItemToUser(balanceItem: BalanceItem, linkingUser: User) {
577
679
  if (!this.checkScope(linkingUser.organizationId)) {
578
680
  return false;
@@ -598,8 +700,9 @@ export class AdminPermissionChecker {
598
700
  if (await this.canManagePayments(member.organizationId)) {
599
701
  return true;
600
702
  }
601
- } else {
602
- const organizationIds = Formatter.uniqueArray(member.registrations.map(r => r.organizationId))
703
+ }
704
+ else {
705
+ const organizationIds = Formatter.uniqueArray(member.registrations.map(r => r.organizationId));
603
706
  for (const organizationId of organizationIds) {
604
707
  if (await this.canManagePayments(organizationId)) {
605
708
  return true;
@@ -615,39 +718,39 @@ export class AdminPermissionChecker {
615
718
  }
616
719
 
617
720
  async canManageFinances(organizationId: string) {
618
- const organizationPermissions = await this.getOrganizationPermissions(organizationId)
721
+ const organizationPermissions = await this.getOrganizationPermissions(organizationId);
619
722
 
620
723
  if (!organizationPermissions) {
621
724
  return false;
622
725
  }
623
-
624
- return organizationPermissions.hasAccessRight(AccessRight.OrganizationFinanceDirector)
726
+
727
+ return organizationPermissions.hasAccessRight(AccessRight.OrganizationFinanceDirector);
625
728
  }
626
729
 
627
730
  /**
628
731
  * Mainly for transfer payment management
629
732
  */
630
733
  async canManagePayments(organizationId: string) {
631
- const organizationPermissions = await this.getOrganizationPermissions(organizationId)
734
+ const organizationPermissions = await this.getOrganizationPermissions(organizationId);
632
735
 
633
736
  if (!organizationPermissions) {
634
737
  return false;
635
738
  }
636
-
739
+
637
740
  return !!organizationPermissions && (
638
741
  organizationPermissions.hasAccessRight(AccessRight.OrganizationManagePayments)
639
742
  || organizationPermissions.hasAccessRight(AccessRight.OrganizationFinanceDirector)
640
- )
743
+ );
641
744
  }
642
745
 
643
746
  async canCreateWebshops(organizationId: string) {
644
- const organizationPermissions = await this.getOrganizationPermissions(organizationId)
747
+ const organizationPermissions = await this.getOrganizationPermissions(organizationId);
645
748
 
646
749
  if (!organizationPermissions) {
647
750
  return false;
648
751
  }
649
752
 
650
- return !!organizationPermissions && organizationPermissions.hasAccessRight(AccessRight.OrganizationCreateWebshops)
753
+ return !!organizationPermissions && organizationPermissions.hasAccessRight(AccessRight.OrganizationCreateWebshops);
651
754
  }
652
755
 
653
756
  async canManagePaymentAccounts(organizationId: string, level: PermissionLevel = PermissionLevel.Read) {
@@ -655,47 +758,47 @@ export class AdminPermissionChecker {
655
758
  return await this.hasSomeAccess(organizationId);
656
759
  }
657
760
 
658
- return await this.canManageFinances(organizationId)
761
+ return await this.canManageFinances(organizationId);
659
762
  }
660
763
 
661
764
  async canActivatePackages(organizationId: string) {
662
- return this.canManageFinances(organizationId)
765
+ return this.canManageFinances(organizationId);
663
766
  }
664
767
 
665
768
  async canDeactivatePackages(organizationId: string) {
666
- return this.canManageFinances(organizationId)
769
+ return this.canManageFinances(organizationId);
667
770
  }
668
771
 
669
772
  async canManageDocuments(organizationId: string, _: PermissionLevel = PermissionLevel.Read) {
670
- const organizationPermissions = await this.getOrganizationPermissions(organizationId)
773
+ const organizationPermissions = await this.getOrganizationPermissions(organizationId);
671
774
 
672
775
  if (!organizationPermissions) {
673
776
  return false;
674
777
  }
675
778
 
676
- return this.hasFullAccess(organizationId)
779
+ return this.hasFullAccess(organizationId);
677
780
  }
678
781
 
679
782
  async canAccessEmailBounces(organizationId: string) {
680
- return this.hasSomeAccess(organizationId)
783
+ return this.hasSomeAccess(organizationId);
681
784
  }
682
785
 
683
786
  canSendEmails() {
684
- return !!this.user.permissions
787
+ return !!this.user.permissions;
685
788
  }
686
789
 
687
790
  async canReadEmailTemplates(organizationId: string) {
688
- const organizationPermissions = await this.getOrganizationPermissions(organizationId)
791
+ const organizationPermissions = await this.getOrganizationPermissions(organizationId);
689
792
 
690
793
  if (!organizationPermissions) {
691
794
  return false;
692
795
  }
693
796
 
694
- return !!this.user.permissions
797
+ return !!this.user.permissions;
695
798
  }
696
799
 
697
800
  async canCreateGroupInCategory(organizationId: string, category: GroupCategory) {
698
- const organizationPermissions = await this.getOrganizationPermissions(organizationId)
801
+ const organizationPermissions = await this.getOrganizationPermissions(organizationId);
699
802
 
700
803
  if (!organizationPermissions) {
701
804
  return false;
@@ -706,9 +809,9 @@ export class AdminPermissionChecker {
706
809
  }
707
810
 
708
811
  // Check parents
709
- const organization = await this.getOrganization(organizationId)
710
- const organizationPeriod = await this.getOrganizationCurrentPeriod(organization)
711
- const parentCategories = category.getParentCategories(organizationPeriod.settings.categories)
812
+ const organization = await this.getOrganization(organizationId);
813
+ const organizationPeriod = await this.getOrganizationCurrentPeriod(organization);
814
+ const parentCategories = category.getParentCategories(organizationPeriod.settings.categories);
712
815
 
713
816
  for (const parentCategory of parentCategories) {
714
817
  if (organizationPermissions.hasResourceAccessRight(PermissionsResourceType.GroupCategories, parentCategory.id, AccessRight.OrganizationCreateGroups)) {
@@ -720,15 +823,15 @@ export class AdminPermissionChecker {
720
823
  }
721
824
 
722
825
  canUpload() {
723
- return !!this.user.permissions
826
+ return !!this.user.permissions;
724
827
  }
725
828
 
726
829
  canManageOrganizationDomain(organizationId: string) {
727
- return this.hasFullAccess(organizationId)
830
+ return this.hasFullAccess(organizationId);
728
831
  }
729
832
 
730
833
  canManageSSOSettings(organizationId: string) {
731
- return this.hasFullAccess(organizationId)
834
+ return this.hasFullAccess(organizationId);
732
835
  }
733
836
 
734
837
  async canManageOrganizationSettings(organizationId: string) {
@@ -738,66 +841,66 @@ export class AdminPermissionChecker {
738
841
  /**
739
842
  * Use this as a circuit breaker to avoid queries for non-admin users
740
843
  */
741
- async hasSomeAccess(organizationId: string): Promise<boolean> {
742
- const organizationPermissions = await this.getOrganizationPermissions(organizationId)
844
+ async hasSomeAccess(organizationOrId: string | Organization): Promise<boolean> {
845
+ const organizationPermissions = await this.getOrganizationPermissions(organizationOrId);
743
846
  return !!organizationPermissions;
744
847
  }
745
848
 
746
849
  async canManageAdmins(organizationId: string) {
747
- return !this.user.isApiUser && (await this.hasFullAccess(organizationId))
850
+ return !this.user.isApiUser && (await this.hasFullAccess(organizationId));
748
851
  }
749
852
 
750
853
  async hasFullAccess(organizationId: string, level = PermissionLevel.Full): Promise<boolean> {
751
- const organizationPermissions = await this.getOrganizationPermissions(organizationId)
854
+ const organizationPermissions = await this.getOrganizationPermissions(organizationId);
752
855
 
753
856
  if (!organizationPermissions) {
754
857
  return false;
755
858
  }
756
859
 
757
- return !!organizationPermissions && organizationPermissions.hasAccess(level)
860
+ return !!organizationPermissions && organizationPermissions.hasAccess(level);
758
861
  }
759
862
 
760
863
  isUserManager(member: MemberWithRegistrations) {
761
- return !!member.users.find(u => u.id === this.user.id)
864
+ return !!member.users.find(u => u.id === this.user.id);
762
865
  }
763
866
 
764
867
  /**
765
868
  * Return a list of RecordSettings the current user can view or edit
766
869
  */
767
870
  async getAccessibleRecordCategories(member: MemberWithRegistrations, level: PermissionLevel = PermissionLevel.Read): Promise<RecordCategory[]> {
768
- const isUserManager = this.isUserManager(member)
871
+ const isUserManager = this.isUserManager(member);
769
872
 
770
873
  // First list all organizations this member is part of
771
874
  const organizations: Organization[] = [];
772
875
 
773
876
  if (member.organizationId) {
774
877
  if (this.checkScope(member.organizationId)) {
775
- organizations.push(await this.getOrganization(member.organizationId))
878
+ organizations.push(await this.getOrganization(member.organizationId));
776
879
  }
777
880
  }
778
881
 
779
882
  for (const registration of member.registrations) {
780
883
  if (this.checkScope(registration.organizationId)) {
781
884
  if (!organizations.find(o => o.id === registration.organizationId)) {
782
- organizations.push(await this.getOrganization(registration.organizationId))
885
+ organizations.push(await this.getOrganization(registration.organizationId));
783
886
  }
784
887
  }
785
888
  }
786
889
 
787
890
  // Loop all organizations.
788
891
  // Check if we have access to their data
789
- const recordCategories: RecordCategory[] = []
892
+ const recordCategories: RecordCategory[] = [];
790
893
  for (const organization of organizations) {
791
894
  if (isUserManager) {
792
895
  // If the user is a manager, we can always access all records
793
896
  // if we ever add private records, we can exclude them here
794
897
  for (const category of organization.meta.recordsConfiguration.recordCategories) {
795
- recordCategories.push(category)
898
+ recordCategories.push(category);
796
899
  }
797
900
  continue;
798
901
  }
799
902
 
800
- const permissions = await this.getOrganizationPermissions(organization)
903
+ const permissions = await this.getOrganizationPermissions(organization);
801
904
  if (!permissions) {
802
905
  continue;
803
906
  }
@@ -805,7 +908,7 @@ export class AdminPermissionChecker {
805
908
  // Now add all records of this organization
806
909
  for (const category of organization.meta.recordsConfiguration.recordCategories) {
807
910
  if (permissions.hasResourceAccess(PermissionsResourceType.RecordCategories, category.id, level)) {
808
- recordCategories.push(category)
911
+ recordCategories.push(category);
809
912
  }
810
913
  }
811
914
 
@@ -817,13 +920,13 @@ export class AdminPermissionChecker {
817
920
  }
818
921
 
819
922
  if (permissions.hasResourceAccess(PermissionsResourceType.RecordCategories, category.id, level)) {
820
- recordCategories.push(category)
923
+ recordCategories.push(category);
821
924
  }
822
925
  }
823
926
  }
824
927
 
825
928
  // Platform data
826
- const platformPermissions = this.platformPermissions
929
+ const platformPermissions = this.platformPermissions;
827
930
  if (platformPermissions || isUserManager) {
828
931
  for (const category of this.platform.config.recordsConfiguration.recordCategories) {
829
932
  if (recordCategories.find(c => c.id === category.id)) {
@@ -832,19 +935,19 @@ export class AdminPermissionChecker {
832
935
  }
833
936
 
834
937
  if (isUserManager || platformPermissions?.hasResourceAccess(PermissionsResourceType.RecordCategories, category.id, level)) {
835
- recordCategories.push(category)
938
+ recordCategories.push(category);
836
939
  }
837
940
  }
838
941
  }
839
942
 
840
- return recordCategories
943
+ return recordCategories;
841
944
  }
842
945
 
843
946
  /**
844
947
  * Return a list of RecordSettings the current user can view or edit
845
948
  */
846
949
  async hasFinancialMemberAccess(member: MemberWithRegistrations, level: PermissionLevel = PermissionLevel.Read): Promise<boolean> {
847
- const isUserManager = this.isUserManager(member)
950
+ const isUserManager = this.isUserManager(member);
848
951
 
849
952
  if (isUserManager && level === PermissionLevel.Read) {
850
953
  return true;
@@ -859,21 +962,21 @@ export class AdminPermissionChecker {
859
962
 
860
963
  if (member.organizationId) {
861
964
  if (this.checkScope(member.organizationId)) {
862
- organizations.push(await this.getOrganization(member.organizationId))
965
+ organizations.push(await this.getOrganization(member.organizationId));
863
966
  }
864
967
  }
865
968
 
866
969
  for (const registration of member.registrations) {
867
970
  if (this.checkScope(registration.organizationId)) {
868
971
  if (!organizations.find(o => o.id === registration.organizationId)) {
869
- organizations.push(await this.getOrganization(registration.organizationId))
972
+ organizations.push(await this.getOrganization(registration.organizationId));
870
973
  }
871
974
  }
872
975
  }
873
976
 
874
977
  // Loop all organizations.
875
978
  for (const organization of organizations) {
876
- const permissions = await this.getOrganizationPermissions(organization)
979
+ const permissions = await this.getOrganizationPermissions(organization);
877
980
  if (!permissions) {
878
981
  continue;
879
982
  }
@@ -893,7 +996,7 @@ export class AdminPermissionChecker {
893
996
  }
894
997
 
895
998
  // Platform data
896
- const platformPermissions = this.platformPermissions
999
+ const platformPermissions = this.platformPermissions;
897
1000
  if (platformPermissions) {
898
1001
  if (isUserManager) {
899
1002
  // Requirements are higher: you need financial access to write your own financial
@@ -901,55 +1004,54 @@ export class AdminPermissionChecker {
901
1004
  if (platformPermissions.hasAccessRight(AccessRight.OrganizationManagePayments)) {
902
1005
  return true;
903
1006
  }
904
- } else {
1007
+ }
1008
+ else {
905
1009
  if (platformPermissions.hasAccessRight(level === PermissionLevel.Read ? AccessRight.MemberReadFinancialData : AccessRight.MemberWriteFinancialData)) {
906
1010
  return true;
907
1011
  }
908
1012
  }
909
1013
  }
910
1014
 
911
- return false
1015
+ return false;
912
1016
  }
913
1017
 
914
1018
  /**
915
1019
  * Return a list of RecordSettings the current user can view or edit
916
1020
  */
917
1021
  async getAccessibleRecordSet(member: MemberWithRegistrations, level: PermissionLevel = PermissionLevel.Read): Promise<Set<string>> {
918
- const categories = await this.getAccessibleRecordCategories(member, level)
919
- const set = new Set<string>()
1022
+ const categories = await this.getAccessibleRecordCategories(member, level);
1023
+ const set = new Set<string>();
920
1024
 
921
1025
  for (const category of categories) {
922
1026
  for (const record of category.getAllRecords()) {
923
- set.add(record.id)
1027
+ set.add(record.id);
924
1028
  }
925
1029
  }
926
1030
 
927
- return set
1031
+ return set;
928
1032
  }
929
1033
 
930
-
931
1034
  async getAccessibleGroups(organizationId: string, level: PermissionLevel = PermissionLevel.Read): Promise<string[] | 'all'> {
932
1035
  if (await this.hasFullAccess(organizationId)) {
933
- return 'all'
1036
+ return 'all';
934
1037
  }
935
1038
 
936
- const groups = await this.getOrganizationGroups(organizationId)
937
- const accessibleGroups: string[] = []
1039
+ const groups = await this.getOrganizationGroups(organizationId);
1040
+ const accessibleGroups: string[] = [];
938
1041
 
939
1042
  for (const group of groups) {
940
1043
  if (await this.canAccessGroup(group, level)) {
941
- accessibleGroups.push(group.id)
1044
+ accessibleGroups.push(group.id);
942
1045
  }
943
1046
  }
944
- return accessibleGroups
1047
+ return accessibleGroups;
945
1048
  }
946
1049
 
947
-
948
1050
  /**
949
1051
  * Changes data inline
950
1052
  */
951
1053
  async filterMemberData(member: MemberWithRegistrations, data: MemberWithRegistrationsBlob): Promise<MemberWithRegistrationsBlob> {
952
- const isUserManager = this.isUserManager(member)
1054
+ const isUserManager = this.isUserManager(member);
953
1055
  if (isUserManager) {
954
1056
  // For the user manager, we don't delete data, because when registering a new member, it doesn't have any organizations yet...
955
1057
  if (!(await this.canAccessMember(member, PermissionLevel.Full))) {
@@ -960,31 +1062,31 @@ export class AdminPermissionChecker {
960
1062
  return data;
961
1063
  }
962
1064
 
963
- const records = await this.getAccessibleRecordSet(member, PermissionLevel.Read)
1065
+ const records = await this.getAccessibleRecordSet(member, PermissionLevel.Read);
964
1066
 
965
- const cloned = data.clone()
1067
+ const cloned = data.clone();
966
1068
 
967
1069
  for (const [key, value] of cloned.details.recordAnswers.entries()) {
968
1070
  if (!records.has(value.settings.id)) {
969
- cloned.details.recordAnswers.delete(key)
1071
+ cloned.details.recordAnswers.delete(key);
970
1072
  }
971
1073
  }
972
1074
 
973
1075
  // Has financial read access?
974
1076
  if (!await this.hasFinancialMemberAccess(member, PermissionLevel.Read)) {
975
- cloned.details.requiresFinancialSupport = null
976
- cloned.details.uitpasNumber = null
977
- cloned.outstandingBalance = 0
1077
+ cloned.details.requiresFinancialSupport = null;
1078
+ cloned.details.uitpasNumber = null;
1079
+ cloned.outstandingBalance = 0;
978
1080
 
979
1081
  for (const registration of cloned.registrations) {
980
- registration.price = 0
981
- registration.pricePaid = 0
1082
+ registration.price = 0;
1083
+ registration.pricePaid = 0;
982
1084
  }
983
1085
  }
984
1086
 
985
1087
  // At least write permissions is required for now to obtain the security code
986
1088
  if (!(await this.canAccessMember(member, PermissionLevel.Write))) {
987
- cloned.details.securityCode = null
1089
+ cloned.details.securityCode = null;
988
1090
  }
989
1091
 
990
1092
  return cloned;
@@ -998,8 +1100,8 @@ export class AdminPermissionChecker {
998
1100
  throw new SimpleError({
999
1101
  code: 'invalid_request',
1000
1102
  message: 'Cannot PUT a full member details object',
1001
- statusCode: 400
1002
- })
1103
+ statusCode: 400,
1104
+ });
1003
1105
  }
1004
1106
 
1005
1107
  const hasRecordAnswers = !!data.details.recordAnswers;
@@ -1011,9 +1113,9 @@ export class AdminPermissionChecker {
1011
1113
  const hasFullAccess = await this.canAccessMember(member, PermissionLevel.Full);
1012
1114
 
1013
1115
  // can only be set to null, and only if can access member with full access
1014
- if(!hasFullAccess || data.details.securityCode !== null) {
1116
+ if (!hasFullAccess || data.details.securityCode !== null) {
1015
1117
  // Unset silently
1016
- data.details.securityCode = undefined
1118
+ data.details.securityCode = undefined;
1017
1119
  }
1018
1120
  }
1019
1121
 
@@ -1022,42 +1124,42 @@ export class AdminPermissionChecker {
1022
1124
  throw new SimpleError({
1023
1125
  code: 'invalid_request',
1024
1126
  message: 'Cannot PUT recordAnswers',
1025
- statusCode: 400
1026
- })
1127
+ statusCode: 400,
1128
+ });
1027
1129
  }
1028
-
1029
- const records = isUserManager ? new Set() : await this.getAccessibleRecordSet(member, PermissionLevel.Write)
1130
+
1131
+ const records = isUserManager ? new Set() : await this.getAccessibleRecordSet(member, PermissionLevel.Write);
1030
1132
 
1031
1133
  for (const [key, value] of data.details.recordAnswers.entries()) {
1032
- let name: string | undefined = undefined
1134
+ let name: string | undefined = undefined;
1033
1135
  if (value) {
1034
1136
  if (value.isPatch()) {
1035
1137
  throw new SimpleError({
1036
1138
  code: 'invalid_request',
1037
1139
  message: 'Cannot PATCH a record answer object',
1038
- statusCode: 400
1039
- })
1140
+ statusCode: 400,
1141
+ });
1040
1142
  }
1041
1143
 
1042
- const id = value.settings.id
1144
+ const id = value.settings.id;
1043
1145
 
1044
1146
  if (id !== key) {
1045
1147
  throw new SimpleError({
1046
1148
  code: 'invalid_request',
1047
1149
  message: 'Record answer key does not match record id',
1048
- statusCode: 400
1049
- })
1150
+ statusCode: 400,
1151
+ });
1050
1152
  }
1051
1153
 
1052
- name = value.settings.name
1154
+ name = value.settings.name;
1053
1155
  }
1054
1156
 
1055
1157
  if (!isUserManager && !records.has(key)) {
1056
1158
  throw new SimpleError({
1057
1159
  code: 'permission_denied',
1058
1160
  message: `Je hebt geen toegangsrechten om het antwoord op ${name ?? 'deze vraag'} aan te passen voor dit lid`,
1059
- statusCode: 400
1060
- })
1161
+ statusCode: 400,
1162
+ });
1061
1163
  }
1062
1164
  }
1063
1165
  }
@@ -1066,8 +1168,8 @@ export class AdminPermissionChecker {
1066
1168
  throw new SimpleError({
1067
1169
  code: 'permission_denied',
1068
1170
  message: 'Cannot edit notes',
1069
- statusCode: 400
1070
- })
1171
+ statusCode: 400,
1172
+ });
1071
1173
  }
1072
1174
 
1073
1175
  // Has financial write access?
@@ -1075,26 +1177,27 @@ export class AdminPermissionChecker {
1075
1177
  if (isUserManager && isSetFinancialSupportTrue) {
1076
1178
  const financialSupportSettings = this.platform.config.financialSupport;
1077
1179
  const preventSelfAssignment = financialSupportSettings?.preventSelfAssignment === true;
1078
-
1079
- if(preventSelfAssignment) {
1180
+
1181
+ if (preventSelfAssignment) {
1080
1182
  throw new SimpleError({
1081
1183
  code: 'permission_denied',
1082
1184
  message: 'No permissions to enable financial support for your own members',
1083
1185
  human: financialSupportSettings.preventSelfAssignmentText ?? FinancialSupportSettings.defaultPreventSelfAssignmentText,
1084
- statusCode: 400
1186
+ statusCode: 400,
1085
1187
  });
1086
1188
  }
1087
1189
  }
1088
-
1190
+
1089
1191
  if (data.details.requiresFinancialSupport) {
1090
1192
  if (isUserManager) {
1091
1193
  // Already handled
1092
- } else {
1194
+ }
1195
+ else {
1093
1196
  throw new SimpleError({
1094
1197
  code: 'permission_denied',
1095
1198
  message: 'Je hebt geen toegangsrechten om de financiële status van dit lid aan te passen',
1096
- statusCode: 400
1097
- })
1199
+ statusCode: 400,
1200
+ });
1098
1201
  }
1099
1202
  }
1100
1203
 
@@ -1103,8 +1206,8 @@ export class AdminPermissionChecker {
1103
1206
  throw new SimpleError({
1104
1207
  code: 'permission_denied',
1105
1208
  message: 'Je hebt geen toegangsrechten om het UiTPAS-nummer van dit lid aan te passen',
1106
- statusCode: 400
1107
- })
1209
+ statusCode: 400,
1210
+ });
1108
1211
  }
1109
1212
  }
1110
1213
 
@@ -1112,38 +1215,38 @@ export class AdminPermissionChecker {
1112
1215
  throw new SimpleError({
1113
1216
  code: 'permission_denied',
1114
1217
  message: 'Je hebt geen toegangsrechten om het openstaande saldo van dit lid aan te passen',
1115
- statusCode: 400
1116
- })
1218
+ statusCode: 400,
1219
+ });
1117
1220
  }
1118
1221
 
1119
- for (const {put: registration} of data.registrations.getPuts()) {
1222
+ for (const { put: registration } of data.registrations.getPuts()) {
1120
1223
  if (registration.price) {
1121
1224
  throw new SimpleError({
1122
1225
  code: 'permission_denied',
1123
1226
  message: 'Je hebt geen toegangsrechten om de prijs van een inschrijving te bepalen',
1124
- statusCode: 400
1125
- })
1227
+ statusCode: 400,
1228
+ });
1126
1229
  }
1127
1230
 
1128
1231
  if (registration.pricePaid) {
1129
1232
  throw new SimpleError({
1130
1233
  code: 'permission_denied',
1131
1234
  message: 'Je hebt geen toegangsrechten om het betaalde bedrag van een inschrijving te bepalen',
1132
- statusCode: 400
1133
- })
1235
+ statusCode: 400,
1236
+ });
1134
1237
  }
1135
1238
  }
1136
1239
  }
1137
1240
 
1138
- return data
1241
+ return data;
1139
1242
  }
1140
1243
 
1141
1244
  canAccessAllPlatformMembers(): boolean {
1142
- return !!this.platformPermissions && !!this.platformPermissions.hasAccessRight(AccessRight.PlatformLoginAs)
1245
+ return !!this.platformPermissions && !!this.platformPermissions.hasAccessRight(AccessRight.PlatformLoginAs);
1143
1246
  }
1144
1247
 
1145
1248
  hasPlatformFullAccess(): boolean {
1146
- return !!this.platformPermissions && !!this.platformPermissions.hasFullAccess()
1249
+ return !!this.platformPermissions && !!this.platformPermissions.hasFullAccess();
1147
1250
  }
1148
1251
 
1149
1252
  getPlatformAccessibleOrganizationTags(level: PermissionLevel): string[] | 'all' {
@@ -1152,34 +1255,34 @@ export class AdminPermissionChecker {
1152
1255
  }
1153
1256
 
1154
1257
  if (this.hasPlatformFullAccess()) {
1155
- return 'all'
1258
+ return 'all';
1156
1259
  }
1157
1260
 
1158
1261
  if (this.platformPermissions?.hasResourceAccess(PermissionsResourceType.OrganizationTags, '', level)) {
1159
- return 'all'
1262
+ return 'all';
1160
1263
  }
1161
1264
 
1162
- const allTags = this.platform.config.tags
1163
- const tags: string[] = []
1265
+ const allTags = this.platform.config.tags;
1266
+ const tags: string[] = [];
1164
1267
 
1165
1268
  for (const tag of allTags) {
1166
1269
  if (this.platformPermissions?.hasResourceAccess(PermissionsResourceType.OrganizationTags, tag.id, level)) {
1167
- tags.push(tag.id)
1270
+ tags.push(tag.id);
1168
1271
  }
1169
1272
  }
1170
1273
 
1171
1274
  if (tags.length === allTags.length) {
1172
- return 'all'
1275
+ return 'all';
1173
1276
  }
1174
1277
 
1175
- return tags
1278
+ return tags;
1176
1279
  }
1177
1280
 
1178
1281
  hasSomePlatformAccess(): boolean {
1179
- return !!this.platformPermissions
1282
+ return !!this.platformPermissions;
1180
1283
  }
1181
1284
 
1182
1285
  canManagePlatformAdmins() {
1183
- return this.hasPlatformFullAccess()
1286
+ return this.hasPlatformFullAccess();
1184
1287
  }
1185
1288
  }