@stamhoofd/backend 2.83.0 → 2.83.1

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.
@@ -1,17 +1,17 @@
1
- import { AutoEncoderPatchType, cloneObject, Decoder, isPatchableArray, ObjectData, PatchableArrayAutoEncoder, patchObject } from '@simonbackx/simple-encoding';
1
+ import { AutoEncoderPatchType, cloneObject, Decoder, isPatchableArray, isPatchMap, ObjectData, PatchableArray, PatchableArrayAutoEncoder, PatchMap, patchObject } from '@simonbackx/simple-encoding';
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError, SimpleErrors } from '@simonbackx/simple-errors';
4
4
  import { Organization, OrganizationRegistrationPeriod, PayconiqPayment, Platform, RegistrationPeriod, StripeAccount, Webshop } from '@stamhoofd/models';
5
- import { BuckarooSettings, Company, OrganizationMetaData, OrganizationPatch, Organization as OrganizationStruct, PayconiqAccount, PaymentMethod, PaymentMethodHelper, PermissionLevel, PlatformConfig } from '@stamhoofd/structures';
5
+ import { BuckarooSettings, Company, MemberResponsibility, OrganizationMetaData, OrganizationPatch, Organization as OrganizationStruct, PayconiqAccount, PaymentMethod, PaymentMethodHelper, PermissionLevel, PermissionRoleDetailed, PermissionRoleForResponsibility, PermissionsResourceType, ResourcePermissions } from '@stamhoofd/structures';
6
6
  import { Formatter } from '@stamhoofd/utility';
7
7
 
8
8
  import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
9
9
  import { BuckarooHelper } from '../../../../helpers/BuckarooHelper';
10
10
  import { Context } from '../../../../helpers/Context';
11
+ import { MemberUserSyncer } from '../../../../helpers/MemberUserSyncer';
11
12
  import { SetupStepUpdater } from '../../../../helpers/SetupStepUpdater';
12
13
  import { TagHelper } from '../../../../helpers/TagHelper';
13
14
  import { ViesHelper } from '../../../../helpers/ViesHelper';
14
- import { MemberUserSyncer } from '../../../../helpers/MemberUserSyncer';
15
15
 
16
16
  type Params = Record<string, never>;
17
17
  type Query = undefined;
@@ -376,6 +376,130 @@ export class PatchOrganizationEndpoint extends Endpoint<Params, Query, Body, Res
376
376
  statusCode: 403,
377
377
  });
378
378
  }
379
+
380
+ // Give users without full access permission to alter responsibilities in order to give other users permissions to resources they also have full permissions to
381
+ if (request.body.privateMeta && request.body.privateMeta.isPatch()) {
382
+ if (request.body.privateMeta.inheritedResponsibilityRoles) {
383
+ const patchableArray: PatchableArrayAutoEncoder<PermissionRoleForResponsibility> = new PatchableArray();
384
+
385
+ for (const patch of request.body.privateMeta.inheritedResponsibilityRoles.getPatches()) {
386
+ const resources: Map<PermissionsResourceType, Map<string, ResourcePermissions>> = patch.resources.applyTo(new Map<PermissionsResourceType, Map<string, ResourcePermissions>>());
387
+
388
+ if (!await Context.auth.hasFullAccessForOrganizationResources(request.body.id, resources)) {
389
+ throw new SimpleError({
390
+ code: 'permission_denied',
391
+ message: 'You do not have permissions to edit inherited responsibility roles',
392
+ statusCode: 403,
393
+ });
394
+ }
395
+
396
+ patchableArray.addPatch(PermissionRoleForResponsibility.patch({
397
+ id: patch.id,
398
+ resources: patch.resources,
399
+ }));
400
+ }
401
+
402
+ for (const { put, afterId } of request.body.privateMeta.inheritedResponsibilityRoles.getPuts()) {
403
+ const resources: Map<PermissionsResourceType, Map<string, ResourcePermissions>> = put.resources;
404
+
405
+ if (!await Context.auth.hasFullAccessForOrganizationResources(request.body.id, resources)) {
406
+ throw new SimpleError({
407
+ code: 'permission_denied',
408
+ message: 'You do not have permissions to add inherited responsibility roles',
409
+ statusCode: 403,
410
+ });
411
+ }
412
+
413
+ const limitedPut = PermissionRoleForResponsibility.create({
414
+ id: put.id,
415
+ name: put.name,
416
+ responsibilityId: put.responsibilityId,
417
+ responsibilityGroupId: put.responsibilityGroupId,
418
+ resources: put.resources,
419
+ });
420
+
421
+ patchableArray.addPut(limitedPut, afterId);
422
+ }
423
+
424
+ organization.privateMeta = organization.privateMeta.patch({
425
+ inheritedResponsibilityRoles: patchableArray,
426
+ });
427
+ }
428
+
429
+ if (request.body.privateMeta.responsibilities) {
430
+ const patchableArray: PatchableArrayAutoEncoder<MemberResponsibility> = new PatchableArray();
431
+
432
+ for (const patch of request.body.privateMeta.responsibilities.getPatches()) {
433
+ if (!patch.permissions) {
434
+ continue;
435
+ };
436
+
437
+ const resources: Map<PermissionsResourceType, Map<string, ResourcePermissions>> = isPatchMap(patch.permissions.resources) ? patch.permissions.resources.applyTo(new Map<PermissionsResourceType, Map<string, ResourcePermissions>>()) : patch.permissions.resources;
438
+
439
+ if (!await Context.auth.hasFullAccessForOrganizationResources(request.body.id, resources)) {
440
+ throw new SimpleError({
441
+ code: 'permission_denied',
442
+ message: 'You do not have permissions to edit responsibilities',
443
+ statusCode: 403,
444
+ });
445
+ }
446
+
447
+ patchableArray.addPatch(MemberResponsibility.patch({
448
+ id: patch.id,
449
+ permissions: PermissionRoleForResponsibility.patch({
450
+ resources: isPatchMap(patch.permissions.resources) ? patch.permissions.resources : new PatchMap(patch.permissions.resources),
451
+ }),
452
+ }));
453
+ }
454
+
455
+ if (request.body.privateMeta.responsibilities.getPuts().length > 0) {
456
+ throw new SimpleError({
457
+ code: 'permission_denied',
458
+ message: 'You do not have permissions to add responsibilities',
459
+ statusCode: 403,
460
+ });
461
+ }
462
+
463
+ organization.privateMeta = organization.privateMeta.patch({
464
+ responsibilities: patchableArray,
465
+ });
466
+ }
467
+
468
+ if (request.body.privateMeta.roles) {
469
+ const patchableArray: PatchableArrayAutoEncoder<PermissionRoleDetailed> = new PatchableArray();
470
+
471
+ for (const patch of request.body.privateMeta.roles.getPatches()) {
472
+ const resources: Map<PermissionsResourceType, Map<string, ResourcePermissions>> = patch.resources.applyTo(new Map<PermissionsResourceType, Map<string, ResourcePermissions>>());
473
+
474
+ if (!await Context.auth.hasFullAccessForOrganizationResources(request.body.id, resources)) {
475
+ throw new SimpleError({
476
+ code: 'permission_denied',
477
+ message: 'You do not have permissions to edit roles',
478
+ statusCode: 403,
479
+ });
480
+ }
481
+
482
+ patchableArray.addPatch(PermissionRoleDetailed.patch({
483
+ id: patch.id,
484
+ resources: patch.resources,
485
+ }));
486
+ }
487
+
488
+ if (request.body.privateMeta.roles.getPuts().length > 0) {
489
+ throw new SimpleError({
490
+ code: 'permission_denied',
491
+ message: 'You do not have permissions to add roles',
492
+ statusCode: 403,
493
+ });
494
+ }
495
+
496
+ organization.privateMeta = organization.privateMeta.patch({
497
+ roles: patchableArray,
498
+ });
499
+ }
500
+
501
+ await organization.save();
502
+ }
379
503
  }
380
504
 
381
505
  // Only needed for permissions atm, so no put or delete here
@@ -1,7 +1,7 @@
1
1
  import { AutoEncoderPatchType, PatchMap } from '@simonbackx/simple-encoding';
2
2
  import { isSimpleError, isSimpleErrors, SimpleError } from '@simonbackx/simple-errors';
3
3
  import { BalanceItem, CachedBalance, Document, EmailTemplate, Event, EventNotification, Group, Member, MemberPlatformMembership, MemberWithRegistrations, Order, Organization, OrganizationRegistrationPeriod, Payment, Registration, User, Webshop } from '@stamhoofd/models';
4
- import { AccessRight, EventPermissionChecker, FinancialSupportSettings, GroupCategory, GroupStatus, GroupType, MemberWithRegistrationsBlob, PermissionLevel, PermissionsResourceType, Platform as PlatformStruct, RecordSettings } from '@stamhoofd/structures';
4
+ import { AccessRight, EventPermissionChecker, FinancialSupportSettings, GroupCategory, GroupStatus, GroupType, MemberWithRegistrationsBlob, PermissionLevel, PermissionsResourceType, Platform as PlatformStruct, RecordSettings, ResourcePermissions } from '@stamhoofd/structures';
5
5
  import { Formatter } from '@stamhoofd/utility';
6
6
  import { MemberRecordStore } from '../services/MemberRecordStore';
7
7
  import { addTemporaryMemberAccess, hasTemporaryMemberAccess } from './TemporaryMemberAccess';
@@ -373,6 +373,27 @@ export class AdminPermissionChecker {
373
373
  return false;
374
374
  }
375
375
 
376
+ /**
377
+ Returns true if the user has full access to all resource ids in the provided resources map. The resource permissions in the map are ignored for now.
378
+ */
379
+ async hasFullAccessForOrganizationResources(organizationId: string, resources: Map<PermissionsResourceType, Map<string, ResourcePermissions>>): Promise<boolean> {
380
+ const organizationPermissions = await this.getOrganizationPermissions(organizationId);
381
+
382
+ if (!organizationPermissions) {
383
+ return false;
384
+ }
385
+
386
+ for (const [resourceType, mapForType] of resources.entries()) {
387
+ for (const resourceId of mapForType.keys()) {
388
+ if (!organizationPermissions.hasResourceAccess(resourceType, resourceId, PermissionLevel.Full)) {
389
+ return false;
390
+ }
391
+ }
392
+ }
393
+
394
+ return true;
395
+ }
396
+
376
397
  async canAccessWebshop(webshop: { id: string; organizationId: string }, permissionLevel: PermissionLevel = PermissionLevel.Read) {
377
398
  const organizationPermissions = await this.getOrganizationPermissions(webshop.organizationId);
378
399