فهرست منبع

fix(syncing-server): race condition when adding admin user to newly created shared vault (#688)

Karol Sójko 1 سال پیش
والد
کامیت
3bd1547ce3

+ 2 - 1
package.json

@@ -19,7 +19,8 @@
     "publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
     "postversion": "./scripts/push-tags-one-by-one.sh",
     "upgrade:snjs": "yarn workspaces foreach --verbose run upgrade:snjs",
-    "e2e": "yarn build packages/home-server && PORT=3123 yarn workspace @standardnotes/home-server start"
+    "e2e": "yarn build packages/home-server && PORT=3123 yarn workspace @standardnotes/home-server start",
+    "start": "yarn build packages/home-server && yarn workspace @standardnotes/home-server start"
   },
   "devDependencies": {
     "@commitlint/cli": "^17.0.2",

+ 16 - 0
packages/syncing-server/src/Domain/UseCase/SharedVaults/AddUserToSharedVault/AddUserToSharedVault.spec.ts

@@ -115,4 +115,20 @@ describe('AddUserToSharedVault', () => {
     expect(result.isFailed()).toBe(false)
     expect(sharedVaultUserRepository.save).toHaveBeenCalled()
   })
+
+  it('should add a user to a shared vault and skip checking if shared vault exists to avoid race conditions', async () => {
+    sharedVaultRepository.findByUuid = jest.fn().mockResolvedValueOnce(null)
+
+    const useCase = createUseCase()
+
+    const result = await useCase.execute({
+      sharedVaultUuid: validUuid,
+      userUuid: validUuid,
+      permission: 'read',
+      skipSharedVaultExistenceCheck: true,
+    })
+
+    expect(result.isFailed()).toBe(false)
+    expect(sharedVaultUserRepository.save).toHaveBeenCalled()
+  })
 })

+ 5 - 3
packages/syncing-server/src/Domain/UseCase/SharedVaults/AddUserToSharedVault/AddUserToSharedVault.ts

@@ -20,9 +20,11 @@ export class AddUserToSharedVault implements UseCaseInterface<SharedVaultUser> {
     }
     const sharedVaultUuid = sharedVaultUuidOrError.getValue()
 
-    const sharedVault = await this.sharedVaultRepository.findByUuid(sharedVaultUuid)
-    if (!sharedVault) {
-      return Result.fail('Attempting to add a shared vault user to a non-existent shared vault')
+    if (!dto.skipSharedVaultExistenceCheck) {
+      const sharedVault = await this.sharedVaultRepository.findByUuid(sharedVaultUuid)
+      if (!sharedVault) {
+        return Result.fail('Attempting to add a shared vault user to a non-existent shared vault')
+      }
     }
 
     const userUuidOrError = Uuid.create(dto.userUuid)

+ 1 - 0
packages/syncing-server/src/Domain/UseCase/SharedVaults/AddUserToSharedVault/AddUserToSharedVaultDTO.ts

@@ -2,4 +2,5 @@ export interface AddUserToSharedVaultDTO {
   sharedVaultUuid: string
   userUuid: string
   permission: string
+  skipSharedVaultExistenceCheck?: boolean
 }

+ 1 - 0
packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVault/CreateSharedVault.spec.ts

@@ -93,6 +93,7 @@ describe('CreateSharedVault', () => {
       sharedVaultUuid: expect.any(String),
       userUuid: '00000000-0000-0000-0000-000000000000',
       permission: 'admin',
+      skipSharedVaultExistenceCheck: true,
     })
     expect(sharedVaultRepository.save).toHaveBeenCalled()
   })

+ 11 - 2
packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVault/CreateSharedVault.ts

@@ -1,4 +1,12 @@
-import { Result, RoleName, Timestamps, UseCaseInterface, Uuid, Validator } from '@standardnotes/domain-core'
+import {
+  Result,
+  RoleName,
+  SharedVaultUserPermission,
+  Timestamps,
+  UseCaseInterface,
+  Uuid,
+  Validator,
+} from '@standardnotes/domain-core'
 import { CreateSharedVaultResult } from './CreateSharedVaultResult'
 import { CreateSharedVaultDTO } from './CreateSharedVaultDTO'
 import { TimerInterface } from '@standardnotes/time'
@@ -56,7 +64,8 @@ export class CreateSharedVault implements UseCaseInterface<CreateSharedVaultResu
     const sharedVaultUserOrError = await this.addUserToSharedVault.execute({
       sharedVaultUuid: sharedVault.id.toString(),
       userUuid: dto.userUuid,
-      permission: 'admin',
+      permission: SharedVaultUserPermission.PERMISSIONS.Admin,
+      skipSharedVaultExistenceCheck: true,
     })
     if (sharedVaultUserOrError.isFailed()) {
       return Result.fail(sharedVaultUserOrError.getError())