@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.
- package/package.json +10 -10
- package/src/audit-logs/GroupLogger.ts +3 -3
- package/src/audit-logs/MemberResponsibilityRecordLogger.ts +1 -1
- package/src/audit-logs/OrderLogger.ts +1 -1
- package/src/audit-logs/RegistrationLogger.ts +1 -1
- package/src/endpoints/admin/members/ChargeMembersEndpoint.ts +4 -4
- package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/ChargeOrganizationsEndpoint.ts +5 -5
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +1 -1
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +8 -8
- package/src/endpoints/auth/CreateAdminEndpoint.ts +2 -2
- package/src/endpoints/auth/CreateTokenEndpoint.ts +10 -10
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +2 -2
- package/src/endpoints/auth/PatchUserEndpoint.ts +9 -9
- package/src/endpoints/auth/SignupEndpoint.ts +2 -2
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +3 -3
- package/src/endpoints/global/audit-logs/GetAuditLogsEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +1 -1
- package/src/endpoints/global/email/GetEmailEndpoint.ts +1 -1
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +1 -1
- package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +139 -0
- package/src/endpoints/global/email/PatchEmailEndpoint.ts +30 -7
- package/src/endpoints/global/events/GetEventNotificationsEndpoint.ts +1 -1
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +16 -35
- package/src/endpoints/global/events/PatchEventNotificationsEndpoint.ts +1 -1
- package/src/endpoints/global/events/PatchEventsEndpoint.ts +22 -16
- package/src/endpoints/global/files/ExportToExcelEndpoint.ts +1 -1
- package/src/endpoints/global/files/UploadFile.ts +14 -2
- package/src/endpoints/global/files/UploadImage.ts +2 -2
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +2 -2
- package/src/endpoints/global/members/GetMembersEndpoint.ts +1 -1
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +19 -19
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +34 -34
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +1 -1
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +5 -5
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +5 -1
- package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +68 -0
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +1 -1
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +2 -2
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +15 -17
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +4 -4
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +37 -37
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +2 -2
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +1 -1
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +5 -5
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +3 -3
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +9 -9
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +4 -4
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +11 -11
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +13 -13
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +16 -16
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +1 -1
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +106 -0
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +16 -3
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +247 -0
- package/src/endpoints/{auth → organization/dashboard/users}/PatchApiUserEndpoint.ts +25 -6
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +4 -4
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +2 -2
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +8 -8
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +2 -2
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +1 -1
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +5 -0
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +2 -2
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +3 -3
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +6 -1
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +1 -1
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +10 -8
- package/src/excel-loaders/event-notifications.ts +11 -11
- package/src/excel-loaders/members.ts +34 -34
- package/src/excel-loaders/organizations.ts +23 -23
- package/src/excel-loaders/payments.ts +39 -39
- package/src/excel-loaders/receivable-balances.ts +21 -21
- package/src/helpers/AddressValidator.ts +6 -6
- package/src/helpers/AdminPermissionChecker.ts +7 -4
- package/src/helpers/AuthenticatedStructures.ts +16 -8
- package/src/helpers/BuckarooHelper.ts +1 -1
- package/src/helpers/CheckSettlements.ts +1 -1
- package/src/helpers/Context.ts +31 -15
- package/src/helpers/FileCache.ts +7 -7
- package/src/helpers/ForwardHandler.ts +1 -1
- package/src/helpers/GlobalHelper.ts +6 -4
- package/src/helpers/MembershipCharger.ts +2 -2
- package/src/helpers/SetupStepUpdater.ts +1 -1
- package/src/helpers/StripeHelper.ts +18 -7
- package/src/helpers/XlsxTransformerColumnHelper.ts +18 -18
- package/src/services/DocumentService.ts +1 -1
- package/src/services/EventNotificationService.ts +1 -1
- package/src/services/MemberNumberService.ts +3 -3
- package/src/services/SSOService.ts +5 -5
- package/src/sql-filters/members.ts +1 -1
- package/tests/e2e/api-rate-limits.test.ts +188 -0
- package/tests/e2e/private-files.test.ts +3 -3
- package/tests/helpers/StripeMocker.ts +7 -1
- /package/src/endpoints/global/platform/{GetPlatformEnpoint.ts → GetPlatformEndpoint.ts} +0 -0
|
@@ -2,15 +2,15 @@ import { Decoder } from '@simonbackx/simple-encoding';
|
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { Token, User } from '@stamhoofd/models';
|
|
5
|
-
import { ApiUser, ApiUserWithToken, UserPermissions } from '@stamhoofd/structures';
|
|
5
|
+
import { ApiUser, ApiUserWithToken, UserMeta, UserPermissions } from '@stamhoofd/structures';
|
|
6
6
|
|
|
7
7
|
import { Context } from '../../../../helpers/Context';
|
|
8
8
|
type Params = Record<string, never>;
|
|
9
9
|
type Query = undefined;
|
|
10
10
|
type Body = ApiUser;
|
|
11
|
-
type ResponseBody =
|
|
11
|
+
type ResponseBody = ApiUserWithToken;
|
|
12
12
|
|
|
13
|
-
export class
|
|
13
|
+
export class CreateApiUserEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
14
14
|
bodyDecoder = ApiUser as Decoder<ApiUser>;
|
|
15
15
|
|
|
16
16
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
@@ -56,6 +56,19 @@ export class CreateAdminEndpoint extends Endpoint<Params, Query, Body, ResponseB
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
admin.permissions = UserPermissions.limitedAdd(null, request.body.permissions, organization.id);
|
|
59
|
+
|
|
60
|
+
if (request.body.meta) {
|
|
61
|
+
const rateLimits = request.body.meta.rateLimits;
|
|
62
|
+
if (rateLimits) {
|
|
63
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
64
|
+
throw Context.auth.error($t('370932fc-6614-44be-bb8f-ff921305fadd'));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
admin.meta = admin.meta ?? UserMeta.create({});
|
|
68
|
+
admin.meta.rateLimits = rateLimits;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
59
72
|
await admin.save();
|
|
60
73
|
|
|
61
74
|
// Set id
|
|
@@ -41,7 +41,7 @@ export class DeleteUserEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
41
41
|
if (user.id == request.params.id) {
|
|
42
42
|
throw new SimpleError({
|
|
43
43
|
code: 'permission_denied',
|
|
44
|
-
message:
|
|
44
|
+
message: $t(`253f8a6e-bef8-4a3a-8a7c-2580d8bad49c`),
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -49,7 +49,7 @@ export class DeleteUserEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
49
49
|
if (!editUser || !Context.auth.checkScope(editUser.organizationId)) {
|
|
50
50
|
throw new SimpleError({
|
|
51
51
|
code: 'permission_denied',
|
|
52
|
-
message:
|
|
52
|
+
message: $t(`dd63dfe1-1a13-476b-b729-10b9df944e88`),
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -0,0 +1,247 @@
|
|
|
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, PermissionsResourceType, ResourcePermissions, UserMeta, UserPermissions } from '@stamhoofd/structures';
|
|
8
|
+
import { CreateApiUserEndpoint } from './CreateApiUserEndpoint';
|
|
9
|
+
import { PatchMap } from '@simonbackx/simple-encoding';
|
|
10
|
+
|
|
11
|
+
describe('Endpoint.PatchApiUserEndpoint', () => {
|
|
12
|
+
// Test endpoint
|
|
13
|
+
const createEndpoint = new CreateApiUserEndpoint();
|
|
14
|
+
const endpoint = new PatchApiUserEndpoint();
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
TestUtils.setEnvironment('userMode', 'platform');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('Only a platform admin can alter the rate limits of a key', async () => {
|
|
21
|
+
const organization = await new OrganizationFactory({}).create();
|
|
22
|
+
const user = await new UserFactory({
|
|
23
|
+
globalPermissions: Permissions.create({
|
|
24
|
+
level: PermissionLevel.Full,
|
|
25
|
+
}),
|
|
26
|
+
}).create();
|
|
27
|
+
const token = await Token.createToken(user);
|
|
28
|
+
|
|
29
|
+
const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
|
|
30
|
+
permissions: UserPermissions.create({
|
|
31
|
+
organizationPermissions: new Map([
|
|
32
|
+
[organization.id, Permissions.create({ level: PermissionLevel.Read })],
|
|
33
|
+
]),
|
|
34
|
+
}),
|
|
35
|
+
}));
|
|
36
|
+
createRequest.headers.authorization = 'Bearer ' + token.accessToken;
|
|
37
|
+
const createResponse = await testServer.test(createEndpoint, createRequest);
|
|
38
|
+
|
|
39
|
+
const apiUserId = createResponse.body.id;
|
|
40
|
+
|
|
41
|
+
const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
|
|
42
|
+
id: apiUserId,
|
|
43
|
+
meta: UserMeta.patch({
|
|
44
|
+
rateLimits: ApiUserRateLimits.High,
|
|
45
|
+
}),
|
|
46
|
+
}));
|
|
47
|
+
r.headers.authorization = 'Bearer ' + token.accessToken;
|
|
48
|
+
|
|
49
|
+
const response = await testServer.test(endpoint, r);
|
|
50
|
+
expect(response.body).toBeDefined();
|
|
51
|
+
expect(response.body.id).toEqual(apiUserId);
|
|
52
|
+
expect(response.body.meta?.rateLimits).toEqual(ApiUserRateLimits.High);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('An organization admin cannot alter rate limits', async () => {
|
|
56
|
+
const organization = await new OrganizationFactory({}).create();
|
|
57
|
+
const user = await new UserFactory({
|
|
58
|
+
permissions: Permissions.create({
|
|
59
|
+
level: PermissionLevel.Full,
|
|
60
|
+
}),
|
|
61
|
+
organization,
|
|
62
|
+
}).create();
|
|
63
|
+
const token = await Token.createToken(user);
|
|
64
|
+
|
|
65
|
+
const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
|
|
66
|
+
permissions: UserPermissions.create({
|
|
67
|
+
organizationPermissions: new Map([
|
|
68
|
+
[organization.id, Permissions.create({ level: PermissionLevel.Read })],
|
|
69
|
+
]),
|
|
70
|
+
}),
|
|
71
|
+
}));
|
|
72
|
+
createRequest.headers.authorization = 'Bearer ' + token.accessToken;
|
|
73
|
+
const createResponse = await testServer.test(createEndpoint, createRequest);
|
|
74
|
+
|
|
75
|
+
const apiUserId = createResponse.body.id;
|
|
76
|
+
|
|
77
|
+
const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
|
|
78
|
+
id: apiUserId,
|
|
79
|
+
meta: UserMeta.patch({
|
|
80
|
+
rateLimits: ApiUserRateLimits.High,
|
|
81
|
+
}),
|
|
82
|
+
}));
|
|
83
|
+
r.headers.authorization = 'Bearer ' + token.accessToken;
|
|
84
|
+
|
|
85
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow(SHExpect.simpleError({
|
|
86
|
+
code: 'permission_denied',
|
|
87
|
+
}));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('You cannot grant an API-key permissions outside its organization using a put', async () => {
|
|
91
|
+
const organization = await new OrganizationFactory({}).create();
|
|
92
|
+
const user = await new UserFactory({
|
|
93
|
+
permissions: Permissions.create({
|
|
94
|
+
level: PermissionLevel.Full,
|
|
95
|
+
}),
|
|
96
|
+
organization,
|
|
97
|
+
}).create();
|
|
98
|
+
const token = await Token.createToken(user);
|
|
99
|
+
|
|
100
|
+
const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
|
|
101
|
+
permissions: UserPermissions.create({
|
|
102
|
+
organizationPermissions: new Map([
|
|
103
|
+
[organization.id, Permissions.create({ level: PermissionLevel.Read })],
|
|
104
|
+
]),
|
|
105
|
+
}),
|
|
106
|
+
}));
|
|
107
|
+
createRequest.headers.authorization = 'Bearer ' + token.accessToken;
|
|
108
|
+
const createResponse = await testServer.test(createEndpoint, createRequest);
|
|
109
|
+
const apiUserId = createResponse.body.id;
|
|
110
|
+
|
|
111
|
+
const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
|
|
112
|
+
id: apiUserId,
|
|
113
|
+
permissions: UserPermissions.create({
|
|
114
|
+
organizationPermissions: new Map([
|
|
115
|
+
[organization.id, Permissions.create({ level: PermissionLevel.Read })],
|
|
116
|
+
['other', Permissions.create({ level: PermissionLevel.Full })],
|
|
117
|
+
]),
|
|
118
|
+
globalPermissions: Permissions.create({
|
|
119
|
+
level: PermissionLevel.Full,
|
|
120
|
+
}),
|
|
121
|
+
}),
|
|
122
|
+
}));
|
|
123
|
+
r.headers.authorization = 'Bearer ' + token.accessToken;
|
|
124
|
+
|
|
125
|
+
const response = await testServer.test(endpoint, r);
|
|
126
|
+
expect(response.body).toBeDefined();
|
|
127
|
+
expect(response.body.id).toEqual(apiUserId);
|
|
128
|
+
expect(response.body.permissions).toEqual(
|
|
129
|
+
UserPermissions.create({
|
|
130
|
+
organizationPermissions: new Map([
|
|
131
|
+
[organization.id, Permissions.create({ level: PermissionLevel.Read })],
|
|
132
|
+
]),
|
|
133
|
+
globalPermissions: null,
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('You cannot grant an API-key permissions outside its organization using a patch', async () => {
|
|
139
|
+
const organization = await new OrganizationFactory({}).create();
|
|
140
|
+
const user = await new UserFactory({
|
|
141
|
+
permissions: Permissions.create({
|
|
142
|
+
level: PermissionLevel.Full,
|
|
143
|
+
}),
|
|
144
|
+
organization,
|
|
145
|
+
}).create();
|
|
146
|
+
const token = await Token.createToken(user);
|
|
147
|
+
|
|
148
|
+
const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
|
|
149
|
+
permissions: UserPermissions.create({
|
|
150
|
+
organizationPermissions: new Map([
|
|
151
|
+
[organization.id, Permissions.create({ level: PermissionLevel.Read })],
|
|
152
|
+
]),
|
|
153
|
+
}),
|
|
154
|
+
}));
|
|
155
|
+
createRequest.headers.authorization = 'Bearer ' + token.accessToken;
|
|
156
|
+
const createResponse = await testServer.test(createEndpoint, createRequest);
|
|
157
|
+
const apiUserId = createResponse.body.id;
|
|
158
|
+
|
|
159
|
+
const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
|
|
160
|
+
id: apiUserId,
|
|
161
|
+
permissions: UserPermissions.patch({
|
|
162
|
+
organizationPermissions: new PatchMap([
|
|
163
|
+
['other', Permissions.create({ level: PermissionLevel.Full })],
|
|
164
|
+
]),
|
|
165
|
+
globalPermissions: Permissions.create({
|
|
166
|
+
level: PermissionLevel.Full,
|
|
167
|
+
}),
|
|
168
|
+
}),
|
|
169
|
+
}));
|
|
170
|
+
r.headers.authorization = 'Bearer ' + token.accessToken;
|
|
171
|
+
|
|
172
|
+
const response = await testServer.test(endpoint, r);
|
|
173
|
+
expect(response.body).toBeDefined();
|
|
174
|
+
expect(response.body.id).toEqual(apiUserId);
|
|
175
|
+
expect(response.body.permissions).toEqual(
|
|
176
|
+
UserPermissions.create({
|
|
177
|
+
organizationPermissions: new Map([
|
|
178
|
+
[organization.id, Permissions.create({ level: PermissionLevel.Read })],
|
|
179
|
+
]),
|
|
180
|
+
globalPermissions: null,
|
|
181
|
+
}),
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('You can grant an API-key permissions using a patch', async () => {
|
|
186
|
+
const organization = await new OrganizationFactory({}).create();
|
|
187
|
+
const user = await new UserFactory({
|
|
188
|
+
permissions: Permissions.create({
|
|
189
|
+
level: PermissionLevel.Full,
|
|
190
|
+
}),
|
|
191
|
+
organization,
|
|
192
|
+
}).create();
|
|
193
|
+
const token = await Token.createToken(user);
|
|
194
|
+
|
|
195
|
+
const createRequest = Request.buildJson('POST', '/api-keys', organization.getApiHost(), ApiUser.create({
|
|
196
|
+
permissions: UserPermissions.create({
|
|
197
|
+
organizationPermissions: new Map([
|
|
198
|
+
[organization.id, Permissions.create({ level: PermissionLevel.Read })],
|
|
199
|
+
]),
|
|
200
|
+
}),
|
|
201
|
+
}));
|
|
202
|
+
createRequest.headers.authorization = 'Bearer ' + token.accessToken;
|
|
203
|
+
const createResponse = await testServer.test(createEndpoint, createRequest);
|
|
204
|
+
const apiUserId = createResponse.body.id;
|
|
205
|
+
|
|
206
|
+
const r = Request.buildJson('PATCH', '/api-keys/' + apiUserId, organization.getApiHost(), ApiUser.patch({
|
|
207
|
+
id: apiUserId,
|
|
208
|
+
permissions: UserPermissions.patch({
|
|
209
|
+
organizationPermissions: new PatchMap([
|
|
210
|
+
[organization.id, Permissions.patch({
|
|
211
|
+
resources: new PatchMap([[
|
|
212
|
+
PermissionsResourceType.Groups,
|
|
213
|
+
new Map([
|
|
214
|
+
['group-id', ResourcePermissions.create({
|
|
215
|
+
level: PermissionLevel.Full,
|
|
216
|
+
})],
|
|
217
|
+
]),
|
|
218
|
+
]]),
|
|
219
|
+
})],
|
|
220
|
+
]),
|
|
221
|
+
}),
|
|
222
|
+
}));
|
|
223
|
+
r.headers.authorization = 'Bearer ' + token.accessToken;
|
|
224
|
+
|
|
225
|
+
const response = await testServer.test(endpoint, r);
|
|
226
|
+
expect(response.body).toBeDefined();
|
|
227
|
+
expect(response.body.id).toEqual(apiUserId);
|
|
228
|
+
expect(response.body.permissions).toEqual(
|
|
229
|
+
UserPermissions.create({
|
|
230
|
+
organizationPermissions: new Map([
|
|
231
|
+
[organization.id, Permissions.create({
|
|
232
|
+
level: PermissionLevel.Read,
|
|
233
|
+
resources: new Map([[
|
|
234
|
+
PermissionsResourceType.Groups,
|
|
235
|
+
new Map([
|
|
236
|
+
['group-id', ResourcePermissions.create({
|
|
237
|
+
level: PermissionLevel.Full,
|
|
238
|
+
})],
|
|
239
|
+
]),
|
|
240
|
+
]]),
|
|
241
|
+
})],
|
|
242
|
+
]),
|
|
243
|
+
globalPermissions: null,
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import { AutoEncoderPatchType, Decoder } from '@simonbackx/simple-encoding';
|
|
1
|
+
import { AutoEncoderPatchType, Decoder, isPatch } from '@simonbackx/simple-encoding';
|
|
2
2
|
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
3
3
|
import { SimpleError } from '@simonbackx/simple-errors';
|
|
4
4
|
import { Token, User } from '@stamhoofd/models';
|
|
5
|
-
import { ApiUser, PermissionLevel, UserPermissions } from '@stamhoofd/structures';
|
|
6
|
-
|
|
7
|
-
import { Context } from '../../helpers/Context';
|
|
5
|
+
import { ApiUser, PermissionLevel, UserMeta, UserPermissions } from '@stamhoofd/structures';
|
|
6
|
+
import { Context } from '../../../../helpers/Context';
|
|
8
7
|
|
|
9
8
|
type Params = { id: string };
|
|
10
9
|
type Query = undefined;
|
|
11
10
|
type Body = AutoEncoderPatchType<ApiUser>;
|
|
12
11
|
type ResponseBody = ApiUser;
|
|
13
12
|
|
|
14
|
-
export class
|
|
13
|
+
export class PatchApiUserEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
15
14
|
bodyDecoder = ApiUser.patchType() as Decoder<AutoEncoderPatchType<ApiUser>>;
|
|
16
15
|
|
|
17
16
|
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
@@ -42,7 +41,7 @@ export class PatchUserEndpoint extends Endpoint<Params, Query, Body, ResponseBod
|
|
|
42
41
|
const editUser = request.body.id === user.id ? user : await User.getByID(request.body.id);
|
|
43
42
|
|
|
44
43
|
if (!editUser || !await Context.auth.canAccessUser(editUser, PermissionLevel.Write) || !editUser.isApiUser) {
|
|
45
|
-
throw Context.auth.notFoundOrNoAccess(
|
|
44
|
+
throw Context.auth.notFoundOrNoAccess($t(`cd099f48-7a5e-4993-854d-697104201871`));
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
editUser.firstName = request.body.name ?? editUser.name;
|
|
@@ -85,6 +84,26 @@ export class PatchUserEndpoint extends Endpoint<Params, Query, Body, ResponseBod
|
|
|
85
84
|
}
|
|
86
85
|
}
|
|
87
86
|
|
|
87
|
+
if (request.body.meta) {
|
|
88
|
+
if (!isPatch(request.body.meta)) {
|
|
89
|
+
throw new SimpleError({
|
|
90
|
+
code: 'invalid_request',
|
|
91
|
+
message: 'Invalid request: meta is not a patch',
|
|
92
|
+
statusCode: 400,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const rateLimits = request.body.meta.rateLimits;
|
|
97
|
+
if (rateLimits && rateLimits !== editUser.meta?.rateLimits) {
|
|
98
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
99
|
+
throw Context.auth.error($t('370932fc-6614-44be-bb8f-ff921305fadd'));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
editUser.meta = editUser.meta ?? UserMeta.create({});
|
|
103
|
+
editUser.meta.rateLimits = rateLimits;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
88
107
|
await editUser.save();
|
|
89
108
|
|
|
90
109
|
return new Response(await Token.getAPIUserWithToken(editUser));
|
|
@@ -38,7 +38,7 @@ export class CreateWebshopEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
38
38
|
|
|
39
39
|
// Fast throw first (more in depth checking for patches later)
|
|
40
40
|
if (!await Context.auth.canCreateWebshops(organization.id)) {
|
|
41
|
-
throw Context.auth.error(
|
|
41
|
+
throw Context.auth.error($t(`e97dabba-7c7f-4705-b5be-fa4ea9f701ed`));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const errors = new SimpleErrors();
|
|
@@ -141,7 +141,7 @@ export class CreateWebshopEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
141
141
|
throw new SimpleError({
|
|
142
142
|
code: 'failed_to_generate_unique_uri',
|
|
143
143
|
message: 'Failed to generate unique uri',
|
|
144
|
-
human:
|
|
144
|
+
human: $t(`a159e71b-e975-48c0-a33b-fdb52ad22299`),
|
|
145
145
|
statusCode: 500,
|
|
146
146
|
});
|
|
147
147
|
}
|
|
@@ -168,7 +168,7 @@ export class CreateWebshopEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
168
168
|
throw new SimpleError({
|
|
169
169
|
code: 'missing_permissions',
|
|
170
170
|
message: 'You cannot create a webshop without having full permissions on the created webshop',
|
|
171
|
-
human:
|
|
171
|
+
human: $t(`28f1afe6-b40d-495c-a58e-2b665c20a294`),
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
174
|
|
|
@@ -211,7 +211,7 @@ export class CreateWebshopEndpoint extends Endpoint<Params, Query, Body, Respons
|
|
|
211
211
|
throw new SimpleError({
|
|
212
212
|
code: 'failed_to_generate_unique_domainUri',
|
|
213
213
|
message: 'Failed to generate unique domainUri',
|
|
214
|
-
human:
|
|
214
|
+
human: $t(`a159e71b-e975-48c0-a33b-fdb52ad22299`),
|
|
215
215
|
statusCode: 500,
|
|
216
216
|
});
|
|
217
217
|
}
|
|
@@ -66,7 +66,7 @@ export class PatchWebshopDiscountCodesEndpoint extends Endpoint<Params, Query, B
|
|
|
66
66
|
throw new SimpleError({
|
|
67
67
|
code: 'used_code',
|
|
68
68
|
message: 'Discount code already in use',
|
|
69
|
-
human:
|
|
69
|
+
human: $t(`d8cf1267-28dd-4d70-9f5f-1ed800729f6d`) + ' ' + struct.code + $t(`9145b0bf-8539-4ddc-8867-859f8716f026`),
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
throw e;
|
|
@@ -98,7 +98,7 @@ export class PatchWebshopDiscountCodesEndpoint extends Endpoint<Params, Query, B
|
|
|
98
98
|
throw new SimpleError({
|
|
99
99
|
code: 'used_code',
|
|
100
100
|
message: 'Discount code already in use',
|
|
101
|
-
human:
|
|
101
|
+
human: $t(`d8cf1267-28dd-4d70-9f5f-1ed800729f6d`) + ' ' + model.code + $t(`9145b0bf-8539-4ddc-8867-859f8716f026`),
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
104
|
throw e;
|
|
@@ -91,7 +91,7 @@ export class PatchWebshopEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
91
91
|
throw new SimpleError({
|
|
92
92
|
code: 'domain_already_used',
|
|
93
93
|
message: 'This domain is already used by another organization',
|
|
94
|
-
human:
|
|
94
|
+
human: $t(`91e43562-956f-41ac-b5b6-a8bcb63edd66`),
|
|
95
95
|
statusCode: 400,
|
|
96
96
|
});
|
|
97
97
|
}
|
|
@@ -107,7 +107,7 @@ export class PatchWebshopEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
107
107
|
throw new SimpleError({
|
|
108
108
|
code: 'invalid_field',
|
|
109
109
|
message: 'Invalid domain',
|
|
110
|
-
human:
|
|
110
|
+
human: $t(`0aa565ac-c12a-4b38-9612-0a615e6a67e0`),
|
|
111
111
|
field: 'customUrl',
|
|
112
112
|
});
|
|
113
113
|
}
|
|
@@ -128,7 +128,7 @@ export class PatchWebshopEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
128
128
|
throw new SimpleError({
|
|
129
129
|
code: 'invalid_field',
|
|
130
130
|
message: 'domainUri contains invalid characters',
|
|
131
|
-
human:
|
|
131
|
+
human: $t(`4e842c04-da6c-4690-a187-665fa4a60bae`),
|
|
132
132
|
field: 'customUrl',
|
|
133
133
|
});
|
|
134
134
|
}
|
|
@@ -139,7 +139,7 @@ export class PatchWebshopEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
139
139
|
throw new SimpleError({
|
|
140
140
|
code: 'invalid_domain',
|
|
141
141
|
message: 'This domain is already in use',
|
|
142
|
-
human:
|
|
142
|
+
human: $t(`11064157-89fd-4c12-b56a-1f5f236a010d`) + ' ' + existing.meta.name + $t(`42dccb0d-88b1-4a18-9250-04aa969b1299`),
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
145
|
}
|
|
@@ -159,7 +159,7 @@ export class PatchWebshopEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
159
159
|
throw new SimpleError({
|
|
160
160
|
code: 'invalid_field',
|
|
161
161
|
message: 'Uri cannot be empty',
|
|
162
|
-
human:
|
|
162
|
+
human: $t(`6af872f3-4547-4fd2-8949-0163e6473c04`),
|
|
163
163
|
field: 'uri',
|
|
164
164
|
});
|
|
165
165
|
}
|
|
@@ -168,7 +168,7 @@ export class PatchWebshopEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
168
168
|
throw new SimpleError({
|
|
169
169
|
code: 'invalid_field',
|
|
170
170
|
message: 'Uri contains invalid characters',
|
|
171
|
-
human:
|
|
171
|
+
human: $t(`4e842c04-da6c-4690-a187-665fa4a60bae`),
|
|
172
172
|
field: 'uri',
|
|
173
173
|
});
|
|
174
174
|
}
|
|
@@ -181,7 +181,7 @@ export class PatchWebshopEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
181
181
|
throw new SimpleError({
|
|
182
182
|
code: 'missing_permissions',
|
|
183
183
|
message: 'You cannot restrict your own permissions',
|
|
184
|
-
human:
|
|
184
|
+
human: $t(`0b70b194-296a-457e-804b-abe800bd5b4c`),
|
|
185
185
|
});
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -194,7 +194,7 @@ export class PatchWebshopEndpoint extends Endpoint<Params, Query, Body, Response
|
|
|
194
194
|
throw new SimpleError({
|
|
195
195
|
code: 'invalid_field',
|
|
196
196
|
message: 'Uri already in use',
|
|
197
|
-
human:
|
|
197
|
+
human: $t(`17267b0d-0be9-4d04-b837-7687da20347f`),
|
|
198
198
|
field: 'uri',
|
|
199
199
|
});
|
|
200
200
|
}
|
|
@@ -42,7 +42,7 @@ export class PatchWebshopTicketsEndpoint extends Endpoint<Params, Query, Body, R
|
|
|
42
42
|
|
|
43
43
|
const webshop = await Webshop.getByID(request.params.id);
|
|
44
44
|
if (!webshop || !await Context.auth.canAccessWebshopTickets(webshop, PermissionLevel.Write)) {
|
|
45
|
-
throw Context.auth.notFoundOrNoAccess(
|
|
45
|
+
throw Context.auth.notFoundOrNoAccess($t(`b4969b2f-c128-44ed-a8de-279c1dd55382`));
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const tickets: Ticket[] = [];
|
|
@@ -31,7 +31,7 @@ export class GetDocumentHtml extends Endpoint<Params, Query, Body, ResponseBody>
|
|
|
31
31
|
if (!document || !(await Context.auth.canAccessDocument(document))) {
|
|
32
32
|
throw new SimpleError({
|
|
33
33
|
code: 'not_found',
|
|
34
|
-
message: '
|
|
34
|
+
message: $t('a9860203-6a6d-4612-b4d4-7851e47d4dad'),
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -48,7 +48,7 @@ export class GetDocumentHtml extends Endpoint<Params, Query, Body, ResponseBody>
|
|
|
48
48
|
if (!html) {
|
|
49
49
|
throw new SimpleError({
|
|
50
50
|
code: 'failed_generating',
|
|
51
|
-
message: '
|
|
51
|
+
message: $t('74ba486a-60b2-4d94-a6fd-159a3adfca8b'),
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -34,7 +34,7 @@ export class GetPaymentEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
34
34
|
throw new SimpleError({
|
|
35
35
|
code: 'not_found',
|
|
36
36
|
message: 'Payment not found',
|
|
37
|
-
human:
|
|
37
|
+
human: $t(`6024d1b5-f0a0-42f4-b972-636286edf929`),
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -2,6 +2,7 @@ import { Request } from '@simonbackx/simple-endpoints';
|
|
|
2
2
|
import { OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
3
3
|
import { Organization, PermissionLevel, Permissions } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
|
+
import { TestUtils } from '@stamhoofd/test-utils';
|
|
5
6
|
import { testServer } from '../../../../../tests/helpers/TestServer';
|
|
6
7
|
import { GetOrganizationEndpoint } from './GetOrganizationEndpoint';
|
|
7
8
|
|
|
@@ -9,6 +10,10 @@ describe('Endpoint.GetOrganization', () => {
|
|
|
9
10
|
// Test endpoint
|
|
10
11
|
const endpoint = new GetOrganizationEndpoint();
|
|
11
12
|
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
TestUtils.setEnvironment('userMode', 'platform');
|
|
15
|
+
});
|
|
16
|
+
|
|
12
17
|
test('Get organization as signed in user', async () => {
|
|
13
18
|
const organization = await new OrganizationFactory({}).create();
|
|
14
19
|
const user = await new UserFactory({ organization }).create();
|
|
@@ -34,7 +34,7 @@ export class CheckWebshopDiscountCodesEndpoint extends Endpoint<Params, Query, B
|
|
|
34
34
|
throw new SimpleError({
|
|
35
35
|
code: 'not_found',
|
|
36
36
|
message: 'Webshop not found',
|
|
37
|
-
human:
|
|
37
|
+
human: $t(`45c039cd-e937-42cd-934b-a2bb4ee0abdd`),
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -33,7 +33,7 @@ export class GetOrderByPaymentEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
33
33
|
throw new SimpleError({
|
|
34
34
|
code: 'not_found',
|
|
35
35
|
message: 'Order not found',
|
|
36
|
-
human:
|
|
36
|
+
human: $t(`1aced6e8-6b32-4da5-b176-906df0a1cc0a`),
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
const [order] = await Order.where({ paymentId: payment.id }, { limit: 1 });
|
|
@@ -41,7 +41,7 @@ export class GetOrderByPaymentEndpoint extends Endpoint<Params, Query, Body, Res
|
|
|
41
41
|
throw new SimpleError({
|
|
42
42
|
code: 'not_found',
|
|
43
43
|
message: 'Order not found',
|
|
44
|
-
human:
|
|
44
|
+
human: $t(`1aced6e8-6b32-4da5-b176-906df0a1cc0a`),
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -31,7 +31,7 @@ export class GetOrderEndpoint extends Endpoint<Params, Query, Body, ResponseBody
|
|
|
31
31
|
throw new SimpleError({
|
|
32
32
|
code: 'not_found',
|
|
33
33
|
message: 'Order not found',
|
|
34
|
-
human:
|
|
34
|
+
human: $t(`1aced6e8-6b32-4da5-b176-906df0a1cc0a`),
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -55,7 +55,7 @@ export class GetTicketsEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
55
55
|
throw new SimpleError({
|
|
56
56
|
code: 'not_found',
|
|
57
57
|
message: 'Ticket not found',
|
|
58
|
-
human:
|
|
58
|
+
human: $t(`d7f095ca-0f05-4c8a-9a65-bbbf69b28af0`),
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -67,7 +67,7 @@ export class GetTicketsEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
67
67
|
throw new SimpleError({
|
|
68
68
|
code: 'not_found',
|
|
69
69
|
message: 'Ticket not found',
|
|
70
|
-
human:
|
|
70
|
+
human: $t(`d7f095ca-0f05-4c8a-9a65-bbbf69b28af0`),
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -79,7 +79,7 @@ export class GetTicketsEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
79
79
|
throw new SimpleError({
|
|
80
80
|
code: 'not_found',
|
|
81
81
|
message: 'Ticket not found',
|
|
82
|
-
human:
|
|
82
|
+
human: $t(`d7f095ca-0f05-4c8a-9a65-bbbf69b28af0`),
|
|
83
83
|
});
|
|
84
84
|
}
|
|
85
85
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Request } from '@simonbackx/simple-endpoints';
|
|
2
2
|
import { OrganizationFactory, Token, UserFactory, WebshopFactory } from '@stamhoofd/models';
|
|
3
|
-
import { PermissionLevel, Permissions
|
|
3
|
+
import { PermissionLevel, Permissions } from '@stamhoofd/structures';
|
|
4
4
|
|
|
5
|
+
import { TestUtils } from '@stamhoofd/test-utils';
|
|
5
6
|
import { testServer } from '../../../../tests/helpers/TestServer';
|
|
6
7
|
import { GetWebshopEndpoint } from './GetWebshopEndpoint';
|
|
7
8
|
|
|
@@ -9,6 +10,10 @@ describe('Endpoint.GetWebshop', () => {
|
|
|
9
10
|
// Test endpoint
|
|
10
11
|
const endpoint = new GetWebshopEndpoint();
|
|
11
12
|
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
TestUtils.setEnvironment('userMode', 'platform');
|
|
15
|
+
});
|
|
16
|
+
|
|
12
17
|
test('Get webshop as signed in user', async () => {
|
|
13
18
|
const organization = await new OrganizationFactory({}).create();
|
|
14
19
|
const user = await new UserFactory({ organization }).create();
|
|
@@ -40,7 +40,7 @@ export class GetWebshopEndpoint extends Endpoint<Params, Query, Body, ResponseBo
|
|
|
40
40
|
throw new SimpleError({
|
|
41
41
|
code: 'not_found',
|
|
42
42
|
message: 'Webshop not found',
|
|
43
|
-
human:
|
|
43
|
+
human: $t(`45c039cd-e937-42cd-934b-a2bb4ee0abdd`),
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
|