@stamhoofd/backend 1.0.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/.env.template.json +63 -0
- package/.eslintrc.js +61 -0
- package/README.md +40 -0
- package/index.ts +172 -0
- package/jest.config.js +11 -0
- package/migrations.ts +33 -0
- package/package.json +48 -0
- package/src/crons.ts +845 -0
- package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +42 -0
- package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +320 -0
- package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +171 -0
- package/src/endpoints/auth/CreateAdminEndpoint.ts +137 -0
- package/src/endpoints/auth/CreateTokenEndpoint.test.ts +68 -0
- package/src/endpoints/auth/CreateTokenEndpoint.ts +200 -0
- package/src/endpoints/auth/DeleteTokenEndpoint.ts +31 -0
- package/src/endpoints/auth/ForgotPasswordEndpoint.ts +70 -0
- package/src/endpoints/auth/GetUserEndpoint.test.ts +64 -0
- package/src/endpoints/auth/GetUserEndpoint.ts +57 -0
- package/src/endpoints/auth/PatchApiUserEndpoint.ts +90 -0
- package/src/endpoints/auth/PatchUserEndpoint.ts +122 -0
- package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +37 -0
- package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +41 -0
- package/src/endpoints/auth/SignupEndpoint.ts +107 -0
- package/src/endpoints/auth/VerifyEmailEndpoint.ts +89 -0
- package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +95 -0
- package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +31 -0
- package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +101 -0
- package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +53 -0
- package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +57 -0
- package/src/endpoints/global/files/UploadFile.ts +147 -0
- package/src/endpoints/global/files/UploadImage.ts +119 -0
- package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +76 -0
- package/src/endpoints/global/members/GetMembersCountEndpoint.ts +43 -0
- package/src/endpoints/global/members/GetMembersEndpoint.ts +429 -0
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +734 -0
- package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +45 -0
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +105 -0
- package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +146 -0
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +52 -0
- package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +80 -0
- package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +49 -0
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +58 -0
- package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +62 -0
- package/src/endpoints/global/payments/ExchangeSTPaymentEndpoint.ts +153 -0
- package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +134 -0
- package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +44 -0
- package/src/endpoints/global/platform/GetPlatformEnpoint.ts +39 -0
- package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +63 -0
- package/src/endpoints/global/registration/GetPaymentRegistrations.ts +68 -0
- package/src/endpoints/global/registration/GetUserBalanceEndpoint.ts +39 -0
- package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +80 -0
- package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +41 -0
- package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +134 -0
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +521 -0
- package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +37 -0
- package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +115 -0
- package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +187 -0
- package/src/endpoints/organization/dashboard/billing/ActivatePackagesEndpoint.ts +424 -0
- package/src/endpoints/organization/dashboard/billing/DeactivatePackageEndpoint.ts +67 -0
- package/src/endpoints/organization/dashboard/billing/GetBillingStatusEndpoint.ts +39 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +57 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +129 -0
- package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +114 -0
- package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +234 -0
- package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +62 -0
- package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +85 -0
- package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +80 -0
- package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +54 -0
- package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +49 -0
- package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +63 -0
- package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +61 -0
- package/src/endpoints/organization/dashboard/organization/ApplyRegisterCodeEndpoint.test.ts +64 -0
- package/src/endpoints/organization/dashboard/organization/ApplyRegisterCodeEndpoint.ts +84 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +43 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +42 -0
- package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +43 -0
- package/src/endpoints/organization/dashboard/organization/GetRegisterCodeEndpoint.ts +65 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +281 -0
- package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +338 -0
- package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +196 -0
- package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +50 -0
- package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +48 -0
- package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +207 -0
- package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +202 -0
- package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +233 -0
- package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +66 -0
- package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +210 -0
- package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +93 -0
- package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +59 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +78 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +40 -0
- package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +69 -0
- package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +52 -0
- package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +73 -0
- package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +60 -0
- package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +47 -0
- package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +41 -0
- package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +217 -0
- package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +51 -0
- package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +47 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +83 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +68 -0
- package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +69 -0
- package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +125 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +204 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +278 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +80 -0
- package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +60 -0
- package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +379 -0
- package/src/endpoints/organization/shared/GetDocumentHtml.ts +54 -0
- package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +45 -0
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +78 -0
- package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +34 -0
- package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +44 -0
- package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +82 -0
- package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +59 -0
- package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +51 -0
- package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +40 -0
- package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +124 -0
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +130 -0
- package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +50 -0
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +450 -0
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +335 -0
- package/src/helpers/AddressValidator.test.ts +40 -0
- package/src/helpers/AddressValidator.ts +256 -0
- package/src/helpers/AdminPermissionChecker.ts +1031 -0
- package/src/helpers/AuthenticatedStructures.ts +158 -0
- package/src/helpers/BuckarooHelper.ts +279 -0
- package/src/helpers/CheckSettlements.ts +215 -0
- package/src/helpers/Context.ts +202 -0
- package/src/helpers/CookieHelper.ts +45 -0
- package/src/helpers/ForwardHandler.test.ts +216 -0
- package/src/helpers/ForwardHandler.ts +140 -0
- package/src/helpers/OpenIDConnectHelper.ts +284 -0
- package/src/helpers/StripeHelper.ts +293 -0
- package/src/helpers/StripePayoutChecker.ts +188 -0
- package/src/middleware/ContextMiddleware.ts +16 -0
- package/src/migrations/1646578856-validate-addresses.ts +60 -0
- package/src/seeds/0000000000-example.ts +13 -0
- package/src/seeds/1715028563-user-permissions.ts +52 -0
- package/tests/e2e/stock.test.ts +2120 -0
- package/tests/e2e/tickets.test.ts +926 -0
- package/tests/helpers/StripeMocker.ts +362 -0
- package/tests/helpers/TestServer.ts +21 -0
- package/tests/jest.global.setup.ts +29 -0
- package/tests/jest.setup.ts +59 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
|
|
2
|
+
import { Organization, RegisterCode, STCredit, UsedRegisterCode } from '@stamhoofd/models';
|
|
3
|
+
import { RegisterCodeStatus, UsedRegisterCode as UsedRegisterCodeStruct } from '@stamhoofd/structures';
|
|
4
|
+
|
|
5
|
+
import { Context } from '../../../../helpers/Context';
|
|
6
|
+
|
|
7
|
+
type Params = Record<string, never>;
|
|
8
|
+
type Query = undefined;
|
|
9
|
+
type Body = undefined;
|
|
10
|
+
type ResponseBody = RegisterCodeStatus;
|
|
11
|
+
|
|
12
|
+
export class GetRegisterCodeEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
13
|
+
|
|
14
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
15
|
+
if (request.method != "GET") {
|
|
16
|
+
return [false];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const params = Endpoint.parseParameters(request.url, "/register-code", {});
|
|
20
|
+
|
|
21
|
+
if (params) {
|
|
22
|
+
return [true, params as Params];
|
|
23
|
+
}
|
|
24
|
+
return [false];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async handle(_: DecodedRequest<Params, Query, Body>) {
|
|
28
|
+
const organization = await Context.setOrganizationScope();
|
|
29
|
+
await Context.authenticate()
|
|
30
|
+
|
|
31
|
+
if (!await Context.auth.hasSomeAccess(organization.id)) {
|
|
32
|
+
throw Context.auth.error()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const codes = await RegisterCode.where({ organizationId: organization.id })
|
|
36
|
+
let code = codes[0]
|
|
37
|
+
|
|
38
|
+
if (codes.length == 0) {
|
|
39
|
+
code = new RegisterCode()
|
|
40
|
+
code.organizationId = organization.id
|
|
41
|
+
code.description = "Doorverwezen door "+ organization.name
|
|
42
|
+
code.value = 2500
|
|
43
|
+
await code.generateCode()
|
|
44
|
+
await code.save()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const usedCodes = await UsedRegisterCode.getAll(code.code)
|
|
48
|
+
const allOrganizations = await Organization.getByIDs(...usedCodes.flatMap(u => u.organizationId ? [u.organizationId] : []))
|
|
49
|
+
const allCredits = await STCredit.getByIDs(...usedCodes.flatMap(u => u.creditId ? [u.creditId] : []))
|
|
50
|
+
|
|
51
|
+
return new Response(RegisterCodeStatus.create({
|
|
52
|
+
code: code.code,
|
|
53
|
+
value: code.value,
|
|
54
|
+
invoiceValue: code.invoiceValue,
|
|
55
|
+
usedCodes: usedCodes.map(c => {
|
|
56
|
+
return UsedRegisterCodeStruct.create({
|
|
57
|
+
id: c.id,
|
|
58
|
+
organizationName: allOrganizations.find(o => o.id === c.organizationId)?.name ?? "Onbekend",
|
|
59
|
+
createdAt: c.createdAt,
|
|
60
|
+
creditValue: (c.creditId ? allCredits.find(credit => credit.id === c.creditId)?.change : null) ?? null
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
}))
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { AutoEncoderPatchType, PatchableArray } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { Request } from "@simonbackx/simple-endpoints";
|
|
3
|
+
import { GroupFactory, OrganizationFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
4
|
+
import { Group, GroupGenderType, GroupPatch, GroupPrivateSettings, GroupSettings, GroupSettingsPatch, Organization, PermissionLevel, PermissionRole, PermissionRoleDetailed, Permissions, PermissionsResourceType, ResourcePermissions } from '@stamhoofd/structures';
|
|
5
|
+
|
|
6
|
+
import { testServer } from '../../../../../tests/helpers/TestServer';
|
|
7
|
+
import { PatchOrganizationEndpoint } from './PatchOrganizationEndpoint';
|
|
8
|
+
|
|
9
|
+
describe("Endpoint.PatchOrganization", () => {
|
|
10
|
+
// Test endpoint
|
|
11
|
+
const endpoint = new PatchOrganizationEndpoint();
|
|
12
|
+
|
|
13
|
+
test("Change the name of the organization", async () => {
|
|
14
|
+
const organization = await new OrganizationFactory({}).create()
|
|
15
|
+
const user = await new UserFactory({ organization, permissions: Permissions.create({ level: PermissionLevel.Full }) }).create()
|
|
16
|
+
//const groups = await new GroupFactory({ organization }).createMultiple(2)
|
|
17
|
+
const token = await Token.createToken(user)
|
|
18
|
+
|
|
19
|
+
const r = Request.buildJson("PATCH", "/v2/organization", organization.getApiHost(), {
|
|
20
|
+
id: organization.id,
|
|
21
|
+
name: "My crazy name"
|
|
22
|
+
});
|
|
23
|
+
r.headers.authorization = "Bearer "+token.accessToken
|
|
24
|
+
|
|
25
|
+
const response = await testServer.test(endpoint, r);
|
|
26
|
+
expect(response.body).toBeDefined();
|
|
27
|
+
|
|
28
|
+
if (!(response.body instanceof Organization)) {
|
|
29
|
+
throw new Error("Expected Organization")
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
expect(response.body.id).toEqual(organization.id)
|
|
33
|
+
expect(response.body.name).toEqual("My crazy name")
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("Can't change organization as a normal user", async () => {
|
|
37
|
+
const organization = await new OrganizationFactory({}).create()
|
|
38
|
+
const user = await new UserFactory({ organization }).create()
|
|
39
|
+
const token = await Token.createToken(user)
|
|
40
|
+
|
|
41
|
+
const r = Request.buildJson("PATCH", "/organization", organization.getApiHost(), {
|
|
42
|
+
id: organization.id,
|
|
43
|
+
name: "My crazy name"
|
|
44
|
+
});
|
|
45
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
46
|
+
|
|
47
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow(/permissions/i);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("Can't change organization as a user with read access", async () => {
|
|
51
|
+
const organization = await new OrganizationFactory({}).create()
|
|
52
|
+
const user = await new UserFactory({ organization, permissions: Permissions.create({ level: PermissionLevel.Read }) }).create()
|
|
53
|
+
const token = await Token.createToken(user)
|
|
54
|
+
|
|
55
|
+
const r = Request.buildJson("PATCH", "/organization", organization.getApiHost(), {
|
|
56
|
+
id: organization.id,
|
|
57
|
+
name: "My crazy name"
|
|
58
|
+
});
|
|
59
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
60
|
+
|
|
61
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow(/permissions/i);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("Change the name of a group with access", async () => {
|
|
65
|
+
const organization = await new OrganizationFactory({}).create()
|
|
66
|
+
const role = PermissionRoleDetailed.create({
|
|
67
|
+
name: "Role"
|
|
68
|
+
})
|
|
69
|
+
organization.privateMeta.roles.push(
|
|
70
|
+
role
|
|
71
|
+
)
|
|
72
|
+
const groups = await new GroupFactory({ organization }).createMultiple(2)
|
|
73
|
+
|
|
74
|
+
role.resources.set(PermissionsResourceType.Groups, new Map())
|
|
75
|
+
role.resources.get(PermissionsResourceType.Groups)!.set(groups[0].id, ResourcePermissions.create({
|
|
76
|
+
level: PermissionLevel.Full
|
|
77
|
+
}))
|
|
78
|
+
|
|
79
|
+
await organization.save()
|
|
80
|
+
|
|
81
|
+
const validPermissions = [
|
|
82
|
+
Permissions.create({
|
|
83
|
+
level: PermissionLevel.None,
|
|
84
|
+
roles: [PermissionRole.create(role)]
|
|
85
|
+
}),
|
|
86
|
+
Permissions.create({
|
|
87
|
+
level: PermissionLevel.Full
|
|
88
|
+
}),
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
for (const permission of validPermissions) {
|
|
92
|
+
const user = await new UserFactory({ organization,
|
|
93
|
+
permissions: permission
|
|
94
|
+
}).create()
|
|
95
|
+
const token = await Token.createToken(user)
|
|
96
|
+
|
|
97
|
+
const changes = new PatchableArray<string, Group, AutoEncoderPatchType<Group>>()
|
|
98
|
+
changes.addPatch(GroupPatch.create({
|
|
99
|
+
id: groups[0].id,
|
|
100
|
+
settings: GroupSettingsPatch.create({
|
|
101
|
+
name: "My crazy group name",
|
|
102
|
+
})
|
|
103
|
+
}))
|
|
104
|
+
|
|
105
|
+
const r = Request.buildJson("PATCH", "/organization", organization.getApiHost(), {
|
|
106
|
+
id: organization.id,
|
|
107
|
+
groups: changes.encode({ version: 2 }),
|
|
108
|
+
});
|
|
109
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
110
|
+
|
|
111
|
+
const response = await testServer.test(endpoint, r);
|
|
112
|
+
expect(response.body).toBeDefined();
|
|
113
|
+
|
|
114
|
+
if (!(response.body instanceof Organization)) {
|
|
115
|
+
throw new Error("Expected Organization")
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
expect(response.body.id).toEqual(organization.id)
|
|
119
|
+
expect(response.body.groups.find(g => g.id == groups[0].id)!.settings.name).toEqual("My crazy group name")
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("Can't change name of group without access", async () => {
|
|
124
|
+
const organization = await new OrganizationFactory({}).create()
|
|
125
|
+
const role = PermissionRoleDetailed.create({
|
|
126
|
+
name: "Role"
|
|
127
|
+
})
|
|
128
|
+
const role2 = PermissionRoleDetailed.create({
|
|
129
|
+
name: "Role2"
|
|
130
|
+
})
|
|
131
|
+
organization.privateMeta.roles.push(
|
|
132
|
+
role,
|
|
133
|
+
role2
|
|
134
|
+
)
|
|
135
|
+
await organization.save()
|
|
136
|
+
const groups = await new GroupFactory({ organization }).createMultiple(2)
|
|
137
|
+
|
|
138
|
+
groups[0].privateSettings.permissions.write.push(PermissionRole.create(role))
|
|
139
|
+
await groups[0].save()
|
|
140
|
+
|
|
141
|
+
groups[0].privateSettings.permissions.read.push(PermissionRole.create(role2))
|
|
142
|
+
await groups[0].save()
|
|
143
|
+
|
|
144
|
+
const invalidPermissions = [
|
|
145
|
+
Permissions.create({
|
|
146
|
+
level: PermissionLevel.Read,
|
|
147
|
+
roles: [PermissionRole.create(role)]
|
|
148
|
+
}),
|
|
149
|
+
Permissions.create({
|
|
150
|
+
level: PermissionLevel.None,
|
|
151
|
+
roles: [PermissionRole.create(role2)]
|
|
152
|
+
}),
|
|
153
|
+
Permissions.create({
|
|
154
|
+
level: PermissionLevel.Write,
|
|
155
|
+
roles: [PermissionRole.create(role2), PermissionRole.create(role)]
|
|
156
|
+
}),
|
|
157
|
+
Permissions.create({
|
|
158
|
+
level: PermissionLevel.Write
|
|
159
|
+
}),
|
|
160
|
+
Permissions.create({
|
|
161
|
+
level: PermissionLevel.Read
|
|
162
|
+
}),
|
|
163
|
+
null
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
for (const permission of invalidPermissions) {
|
|
167
|
+
const user = await new UserFactory({
|
|
168
|
+
organization,
|
|
169
|
+
permissions: permission
|
|
170
|
+
}).create()
|
|
171
|
+
const token = await Token.createToken(user)
|
|
172
|
+
|
|
173
|
+
const changes = new PatchableArray<string, Group, AutoEncoderPatchType<Group>>()
|
|
174
|
+
changes.addPatch(GroupPatch.create({
|
|
175
|
+
id: groups[0].id,
|
|
176
|
+
settings: GroupSettingsPatch.create({
|
|
177
|
+
name: "My crazy group name",
|
|
178
|
+
})
|
|
179
|
+
}))
|
|
180
|
+
const r = Request.buildJson("PATCH", "/organization", organization.getApiHost(), {
|
|
181
|
+
id: organization.id,
|
|
182
|
+
groups: changes.encode({ version: 2 }),
|
|
183
|
+
});
|
|
184
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
185
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow(/permissions/i);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
test("Create a group with access", async () => {
|
|
193
|
+
const organization = await new OrganizationFactory({}).create()
|
|
194
|
+
const groups = await new GroupFactory({ organization }).createMultiple(2)
|
|
195
|
+
|
|
196
|
+
const validPermissions = [
|
|
197
|
+
Permissions.create({
|
|
198
|
+
level: PermissionLevel.Full
|
|
199
|
+
}),
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
const invalidPermissions = [
|
|
203
|
+
Permissions.create({
|
|
204
|
+
level: PermissionLevel.Write
|
|
205
|
+
}),
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
for (const permission of validPermissions) {
|
|
209
|
+
const user = await new UserFactory({
|
|
210
|
+
organization,
|
|
211
|
+
permissions: permission
|
|
212
|
+
}).create()
|
|
213
|
+
const token = await Token.createToken(user)
|
|
214
|
+
|
|
215
|
+
const changes = new PatchableArray<string, Group, AutoEncoderPatchType<Group>>()
|
|
216
|
+
const put = Group.create({
|
|
217
|
+
cycle: 0,
|
|
218
|
+
organizationId: organization.id,
|
|
219
|
+
periodId: organization.periodId,
|
|
220
|
+
settings: GroupSettings.create({
|
|
221
|
+
name: "My crazy group name",
|
|
222
|
+
startDate: new Date(),
|
|
223
|
+
endDate: new Date(),
|
|
224
|
+
registrationStartDate: new Date(),
|
|
225
|
+
registrationEndDate: new Date(),
|
|
226
|
+
genderType: GroupGenderType.Mixed,
|
|
227
|
+
}),
|
|
228
|
+
privateSettings: GroupPrivateSettings.create({})
|
|
229
|
+
})
|
|
230
|
+
changes.addPut(put)
|
|
231
|
+
|
|
232
|
+
const r = Request.buildJson("PATCH", "/v140/organization", organization.getApiHost(), {
|
|
233
|
+
id: organization.id,
|
|
234
|
+
groups: changes.encode({ version: 140 }),
|
|
235
|
+
});
|
|
236
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
237
|
+
|
|
238
|
+
const response = await testServer.test(endpoint, r);
|
|
239
|
+
expect(response.body).toBeDefined();
|
|
240
|
+
|
|
241
|
+
if (!(response.body instanceof Organization)) {
|
|
242
|
+
throw new Error("Expected Organization")
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
expect(response.body.id).toEqual(organization.id)
|
|
246
|
+
expect(response.body.groups.map(g => g.id)).toContainEqual(put.id)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
for (const permission of invalidPermissions) {
|
|
250
|
+
const user = await new UserFactory({
|
|
251
|
+
organization,
|
|
252
|
+
permissions: permission
|
|
253
|
+
}).create()
|
|
254
|
+
const token = await Token.createToken(user)
|
|
255
|
+
|
|
256
|
+
const changes = new PatchableArray<string, Group, AutoEncoderPatchType<Group>>()
|
|
257
|
+
const put = Group.create({
|
|
258
|
+
cycle: 0,
|
|
259
|
+
organizationId: organization.id,
|
|
260
|
+
periodId: organization.periodId,
|
|
261
|
+
settings: GroupSettings.create({
|
|
262
|
+
name: "My crazy group name",
|
|
263
|
+
startDate: new Date(),
|
|
264
|
+
endDate: new Date(),
|
|
265
|
+
registrationStartDate: new Date(),
|
|
266
|
+
registrationEndDate: new Date(),
|
|
267
|
+
genderType: GroupGenderType.Mixed,
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
changes.addPut(put)
|
|
271
|
+
|
|
272
|
+
const r = Request.buildJson("PATCH", "/v2/organization", organization.getApiHost(), {
|
|
273
|
+
id: organization.id,
|
|
274
|
+
groups: changes.encode({ version: 2 }),
|
|
275
|
+
});
|
|
276
|
+
r.headers.authorization = "Bearer " + token.accessToken
|
|
277
|
+
await expect(testServer.test(endpoint, r)).rejects.toThrow(/permissions/i);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
});
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import { AutoEncoderPatchType, Decoder, ObjectData, patchObject } from '@simonbackx/simple-encoding';
|
|
2
|
+
import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
|
|
3
|
+
import { SimpleError, SimpleErrors } from '@simonbackx/simple-errors';
|
|
4
|
+
import { Group, Organization,PayconiqPayment, Platform, StripeAccount, Token, User, Webshop } from '@stamhoofd/models';
|
|
5
|
+
import { BuckarooSettings, GroupPrivateSettings, Organization as OrganizationStruct, OrganizationPatch, PayconiqAccount, PaymentMethod, PaymentMethodHelper, PermissionLevel, Permissions, PermissionsResourceType,ResourcePermissions, UserPermissions, Version, OrganizationMetaData } from "@stamhoofd/structures";
|
|
6
|
+
import { Formatter } from '@stamhoofd/utility';
|
|
7
|
+
|
|
8
|
+
import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
|
|
9
|
+
import { BuckarooHelper } from '../../../../helpers/BuckarooHelper';
|
|
10
|
+
import { Context } from '../../../../helpers/Context';
|
|
11
|
+
|
|
12
|
+
type Params = Record<string, never>;
|
|
13
|
+
type Query = undefined;
|
|
14
|
+
type Body = AutoEncoderPatchType<OrganizationStruct>;
|
|
15
|
+
type ResponseBody = OrganizationStruct;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One endpoint to create, patch and delete groups. Usefull because on organization setup, we need to create multiple groups at once. Also, sometimes we need to link values and update multiple groups at once
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
|
|
22
|
+
bodyDecoder = OrganizationPatch as Decoder<AutoEncoderPatchType<OrganizationStruct>>
|
|
23
|
+
|
|
24
|
+
protected doesMatch(request: Request): [true, Params] | [false] {
|
|
25
|
+
if (request.method != "PATCH") {
|
|
26
|
+
return [false];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const params = Endpoint.parseParameters(request.url, "/organization", {});
|
|
30
|
+
|
|
31
|
+
if (params) {
|
|
32
|
+
return [true, params as Params];
|
|
33
|
+
}
|
|
34
|
+
return [false];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async handle(request: DecodedRequest<Params, Query, Body>) {
|
|
38
|
+
const organization = await Context.setOrganizationScope();
|
|
39
|
+
const {user} = await Context.authenticate()
|
|
40
|
+
|
|
41
|
+
if (!await Context.auth.hasSomeAccess(organization.id)) {
|
|
42
|
+
throw Context.auth.error()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// check if organization ID matches
|
|
46
|
+
if (request.body.id !== organization.id) {
|
|
47
|
+
throw new SimpleError({
|
|
48
|
+
code: "invalid_id",
|
|
49
|
+
message: "You cannot modify an organization with a different ID than the organization you are signed in for",
|
|
50
|
+
statusCode: 403
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const errors = new SimpleErrors()
|
|
55
|
+
|
|
56
|
+
if (await Context.auth.hasFullAccess(organization.id)) {
|
|
57
|
+
organization.name = request.body.name ?? organization.name
|
|
58
|
+
if (request.body.website !== undefined) {
|
|
59
|
+
organization.website = request.body.website;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (request.body.address) {
|
|
63
|
+
organization.address = organization.address.patch(request.body.address)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (request.body.uri && request.body.uri !== organization.uri) {
|
|
67
|
+
const slugified = Formatter.slug(request.body.uri);
|
|
68
|
+
if (slugified.length > 100) {
|
|
69
|
+
throw new SimpleError({
|
|
70
|
+
code: "invalid_field",
|
|
71
|
+
message: "Field is too long",
|
|
72
|
+
human: "De URI van de vereniging is te lang",
|
|
73
|
+
field: "uri"
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (slugified.length < 3) {
|
|
78
|
+
throw new SimpleError({
|
|
79
|
+
code: "invalid_field",
|
|
80
|
+
message: "Field is too short",
|
|
81
|
+
human: "De URI van de vereniging is te kort",
|
|
82
|
+
field: "uri"
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
const alreadyExists = await Organization.getByURI(slugified);
|
|
86
|
+
|
|
87
|
+
if (alreadyExists) {
|
|
88
|
+
throw new SimpleError({
|
|
89
|
+
code: "name_taken",
|
|
90
|
+
message: "An organization with the same URI already exists",
|
|
91
|
+
human: "Er bestaat al een vereniging met dezelfde URI. Voeg bijvoorbeeld de naam van je gemeente toe.",
|
|
92
|
+
field: "uri",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
organization.uri = slugified
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (request.body.privateMeta && request.body.privateMeta.isPatch()) {
|
|
100
|
+
organization.privateMeta.emails = request.body.privateMeta.emails.applyTo(organization.privateMeta.emails)
|
|
101
|
+
organization.privateMeta.roles = request.body.privateMeta.roles.applyTo(organization.privateMeta.roles)
|
|
102
|
+
organization.privateMeta.privateKey = request.body.privateMeta.privateKey ?? organization.privateMeta.privateKey
|
|
103
|
+
organization.privateMeta.featureFlags = patchObject(organization.privateMeta.featureFlags, request.body.privateMeta.featureFlags);
|
|
104
|
+
|
|
105
|
+
if (request.body.privateMeta.mollieProfile !== undefined) {
|
|
106
|
+
organization.privateMeta.mollieProfile = patchObject(organization.privateMeta.mollieProfile, request.body.privateMeta.mollieProfile)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (request.body.privateMeta.useTestPayments !== undefined) {
|
|
110
|
+
organization.privateMeta.useTestPayments = request.body.privateMeta.useTestPayments
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Apply payconiq patch
|
|
114
|
+
if (request.body.privateMeta.payconiqAccounts !== undefined) {
|
|
115
|
+
organization.privateMeta.payconiqAccounts = patchObject(organization.privateMeta.payconiqAccounts, request.body.privateMeta.payconiqAccounts)
|
|
116
|
+
|
|
117
|
+
for (const account of organization.privateMeta.payconiqAccounts) {
|
|
118
|
+
if (account.merchantId === null) {
|
|
119
|
+
const payment = await PayconiqPayment.createTest(organization, account)
|
|
120
|
+
|
|
121
|
+
if (!payment) {
|
|
122
|
+
throw new SimpleError({
|
|
123
|
+
code: "invalid_field",
|
|
124
|
+
message: "De API-key voor Payconiq is niet geldig. Kijk eens na of je wel de juiste key hebt ingevuld.",
|
|
125
|
+
field: "payconiqAccounts"
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Save merchant id
|
|
130
|
+
const decoded = PayconiqAccount.decode(
|
|
131
|
+
new ObjectData({
|
|
132
|
+
...(payment as any).creditor,
|
|
133
|
+
id: account.id,
|
|
134
|
+
apiKey: account.apiKey,
|
|
135
|
+
}, {version: 0})
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
account.merchantId = decoded.merchantId
|
|
139
|
+
account.callbackUrl = decoded.callbackUrl
|
|
140
|
+
account.profileId = decoded.profileId
|
|
141
|
+
account.name = decoded.name
|
|
142
|
+
account.iban = decoded.iban
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (request.body.privateMeta.buckarooSettings !== undefined) {
|
|
148
|
+
if (request.body.privateMeta.buckarooSettings === null) {
|
|
149
|
+
organization.privateMeta.buckarooSettings = null;
|
|
150
|
+
} else {
|
|
151
|
+
organization.privateMeta.buckarooSettings = organization.privateMeta.buckarooSettings ?? BuckarooSettings.create({})
|
|
152
|
+
organization.privateMeta.buckarooSettings.patchOrPut(request.body.privateMeta.buckarooSettings)
|
|
153
|
+
|
|
154
|
+
// Validate buckaroo settings
|
|
155
|
+
const buckaroo = new BuckarooHelper(organization.privateMeta.buckarooSettings.key, organization.privateMeta.buckarooSettings.secret, organization.privateMeta.useTestPayments ?? STAMHOOFD.environment != 'production')
|
|
156
|
+
|
|
157
|
+
if (!(await buckaroo.createTest())) {
|
|
158
|
+
throw new SimpleError({
|
|
159
|
+
code: "invalid_field",
|
|
160
|
+
message: "De key of secret voor Buckaroo is niet geldig. Kijk eens na of je wel de juiste key hebt ingevuld.",
|
|
161
|
+
field: "buckarooSettings"
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (request.body.privateMeta.registrationPaymentConfiguration?.stripeAccountId !== undefined) {
|
|
168
|
+
if (request.body.privateMeta.registrationPaymentConfiguration.stripeAccountId !== null) {
|
|
169
|
+
const account = await StripeAccount.getByID(request.body.privateMeta.registrationPaymentConfiguration.stripeAccountId)
|
|
170
|
+
if (!account || account.organizationId !== organization.id) {
|
|
171
|
+
throw new SimpleError({
|
|
172
|
+
code: "invalid_field",
|
|
173
|
+
message: "Het Stripe account dat je hebt gekozen bestaat niet (meer)",
|
|
174
|
+
field: "registrationPaymentConfiguration.stripeAccountId"
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
organization.privateMeta.registrationPaymentConfiguration.stripeAccountId = request.body.privateMeta.registrationPaymentConfiguration.stripeAccountId
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Allow admin patches (permissions only atm). No put atm
|
|
183
|
+
if (request.body.admins) {
|
|
184
|
+
for (const patch of request.body.admins.getPatches()) {
|
|
185
|
+
if (patch.permissions) {
|
|
186
|
+
const admin = await User.getByID(patch.id)
|
|
187
|
+
if (!admin || !await Context.auth.canAccessUser(admin, PermissionLevel.Full)) {
|
|
188
|
+
throw new SimpleError({
|
|
189
|
+
code: "invalid_field",
|
|
190
|
+
message: "De beheerder die je wilt wijzigen bestaat niet (meer)",
|
|
191
|
+
field: "admins"
|
|
192
|
+
})
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
admin.permissions = UserPermissions.limitedPatch(admin.permissions, patch.permissions, organization.id)
|
|
196
|
+
|
|
197
|
+
if (admin.id === user.id && (!admin.permissions || !admin.permissions.forOrganization(organization)?.hasFullAccess())) {
|
|
198
|
+
throw new SimpleError({
|
|
199
|
+
code: "permission_denied",
|
|
200
|
+
message: "Je kan jezelf niet verwijderen als hoofdbeheerder"
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
await admin.save()
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (request.body.meta) {
|
|
209
|
+
const savedPackages = organization.meta.packages
|
|
210
|
+
organization.meta.patchOrPut(request.body.meta)
|
|
211
|
+
organization.meta.packages = savedPackages
|
|
212
|
+
|
|
213
|
+
// check payconiq + mollie
|
|
214
|
+
if (request.body.meta.registrationPaymentConfiguration) {
|
|
215
|
+
if (!organization.privateMeta.payconiqApiKey && !organization.privateMeta.buckarooSettings?.paymentMethods.includes(PaymentMethod.Payconiq)) {
|
|
216
|
+
const i = organization.meta.paymentMethods.findIndex(p => p == PaymentMethod.Payconiq)
|
|
217
|
+
if (i != -1) {
|
|
218
|
+
throw new SimpleError({
|
|
219
|
+
code: "invalid_field",
|
|
220
|
+
message: "Je kan Payconiq niet activeren omdat je geen Payconiq API Key hebt ingesteld. Schakel Payconiq uit voor je verder gaat.",
|
|
221
|
+
field: "paymentMethods"
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// check payconiq + mollie
|
|
227
|
+
if (!organization.privateMeta.mollieOnboarding || !organization.privateMeta.mollieOnboarding.canReceivePayments) {
|
|
228
|
+
let stripe: StripeAccount | undefined = undefined
|
|
229
|
+
if (organization.privateMeta.registrationPaymentConfiguration.stripeAccountId) {
|
|
230
|
+
stripe = await StripeAccount.getByID(organization.privateMeta.registrationPaymentConfiguration.stripeAccountId)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const i = organization.meta.paymentMethods.findIndex(p => {
|
|
234
|
+
if (p === PaymentMethod.Payconiq) return
|
|
235
|
+
if (p === PaymentMethod.Transfer) return
|
|
236
|
+
if (p === PaymentMethod.PointOfSale) return
|
|
237
|
+
|
|
238
|
+
if (!organization.privateMeta.buckarooSettings?.paymentMethods.includes(p)) {
|
|
239
|
+
if (!stripe?.meta.paymentMethods.includes(p)) {
|
|
240
|
+
return true
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
if (i != -1) {
|
|
245
|
+
throw new SimpleError({
|
|
246
|
+
code: "invalid_field",
|
|
247
|
+
message: "Je kan "+PaymentMethodHelper.getName(organization.meta.paymentMethods[i])+" niet activeren omdat je daarvoor nog niet aangesloten bent bij een betaalpartner. Schakel "+PaymentMethodHelper.getName(organization.meta.paymentMethods[i])+" uit voor je verder gaat.",
|
|
248
|
+
field: "paymentMethods"
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (request.body.meta?.tags) {
|
|
255
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
256
|
+
throw Context.auth.error()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const cleanedPatch = OrganizationMetaData.patch({
|
|
260
|
+
tags: request.body.meta.tags as any
|
|
261
|
+
})
|
|
262
|
+
const platform = await Platform.getShared()
|
|
263
|
+
const patchedMeta = organization.meta.patch(cleanedPatch);
|
|
264
|
+
for (const tag of patchedMeta.tags) {
|
|
265
|
+
if (!platform.config.tags.find(t => t.id === tag)) {
|
|
266
|
+
throw new SimpleError({ code: "invalid_tag", message: "Invalid tag", statusCode: 400 });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Sort tags based on platform config order
|
|
271
|
+
patchedMeta.tags.sort((a, b) => {
|
|
272
|
+
const aIndex = platform.config.tags.findIndex(t => t.id === a);
|
|
273
|
+
const bIndex = platform.config.tags.findIndex(t => t.id === b);
|
|
274
|
+
return aIndex - bIndex;
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
organization.meta.tags = patchedMeta.tags;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (request.body.uri) {
|
|
282
|
+
if (!Context.auth.hasPlatformFullAccess()) {
|
|
283
|
+
throw Context.auth.error()
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const uriExists = await Organization.getByURI(request.body.uri);
|
|
287
|
+
|
|
288
|
+
if (uriExists && uriExists.id !== organization.id) {
|
|
289
|
+
throw new SimpleError({
|
|
290
|
+
code: "name_taken",
|
|
291
|
+
message: "An organization with the same name already exists",
|
|
292
|
+
human: "Er bestaat al een vereniging met dezelfde URI. Pas deze aan zodat deze uniek is, en controleer of deze vereniging niet al bestaat.",
|
|
293
|
+
field: "name",
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
organization.uri = request.body.uri
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Save the organization
|
|
301
|
+
await organization.save()
|
|
302
|
+
} else {
|
|
303
|
+
if (request.body.name || request.body.address) {
|
|
304
|
+
throw new SimpleError({
|
|
305
|
+
code: "permission_denied",
|
|
306
|
+
message: "You do not have permissions to edit the organization settings",
|
|
307
|
+
statusCode: 403
|
|
308
|
+
})
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Only needed for permissions atm, so no put or delete here
|
|
313
|
+
for (const struct of request.body.webshops.getPatches()) {
|
|
314
|
+
const model = await Webshop.getByID(struct.id)
|
|
315
|
+
|
|
316
|
+
if (!model || !await Context.auth.canAccessWebshop(model, PermissionLevel.Full)) {
|
|
317
|
+
errors.addError(
|
|
318
|
+
Context.auth.error('Je hebt geen toegangsrechten om deze webshop te wijzigen')
|
|
319
|
+
)
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (struct.meta) {
|
|
324
|
+
model.meta.patchOrPut(struct.meta)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (struct.privateMeta) {
|
|
328
|
+
model.privateMeta.patchOrPut(struct.privateMeta)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
await model.save();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
errors.throwIfNotEmpty()
|
|
335
|
+
return new Response(await AuthenticatedStructures.organization(organization));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|