mirror of
https://github.com/standardnotes/server
synced 2026-07-14 00:01:54 -04:00
feat: add creating shared vault file valet tokens. (#635)
Co-authored-by: Mo <[email protected]>
This commit is contained in:
@@ -3,6 +3,12 @@ import { Result, ValueObject } from '@standardnotes/domain-core'
|
||||
import { SharedVaultUserPermissionProps } from './SharedVaultUserPermissionProps'
|
||||
|
||||
export class SharedVaultUserPermission extends ValueObject<SharedVaultUserPermissionProps> {
|
||||
static readonly PERMISSIONS = {
|
||||
Read: 'read',
|
||||
Write: 'write',
|
||||
Admin: 'admin',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
@@ -12,7 +18,8 @@ export class SharedVaultUserPermission extends ValueObject<SharedVaultUserPermis
|
||||
}
|
||||
|
||||
static create(sharedVaultUserPermission: string): Result<SharedVaultUserPermission> {
|
||||
if (!['read', 'write', 'admin'].includes(sharedVaultUserPermission)) {
|
||||
const isValidPermission = Object.values(this.PERMISSIONS).includes(sharedVaultUserPermission)
|
||||
if (!isValidPermission) {
|
||||
return Result.fail<SharedVaultUserPermission>(`Invalid shared vault user permission ${sharedVaultUserPermission}`)
|
||||
} else {
|
||||
return Result.ok<SharedVaultUserPermission>(new SharedVaultUserPermission({ value: sharedVaultUserPermission }))
|
||||
|
||||
+1
@@ -6,4 +6,5 @@ export interface SharedVaultUserRepositoryInterface {
|
||||
findByUuid(sharedVaultUserUuid: Uuid): Promise<SharedVaultUser | null>
|
||||
save(sharedVaultUser: SharedVaultUser): Promise<void>
|
||||
remove(sharedVault: SharedVaultUser): Promise<void>
|
||||
findByUserUuidAndSharedVaultUuid(dto: { userUuid: Uuid; sharedVaultUuid: Uuid }): Promise<SharedVaultUser | null>
|
||||
}
|
||||
|
||||
+363
@@ -0,0 +1,363 @@
|
||||
import { SharedVaultValetTokenData, TokenEncoderInterface, ValetTokenOperation } from '@standardnotes/security'
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { CreateSharedVaultFileValetToken } from './CreateSharedVaultFileValetToken'
|
||||
import { SharedVault } from '../../SharedVault/SharedVault'
|
||||
import { SharedVaultUser } from '../../SharedVault/User/SharedVaultUser'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
import { Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
describe('CreateSharedVaultFileValetToken', () => {
|
||||
let sharedVaultRepository: SharedVaultRepositoryInterface
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
let tokenEncoder: TokenEncoderInterface<SharedVaultValetTokenData>
|
||||
const valetTokenTTL = 3600
|
||||
let sharedVault: SharedVault
|
||||
let sharedVaultUser: SharedVaultUser
|
||||
|
||||
const createUseCase = () =>
|
||||
new CreateSharedVaultFileValetToken(sharedVaultRepository, sharedVaultUserRepository, tokenEncoder, valetTokenTTL)
|
||||
|
||||
beforeEach(() => {
|
||||
sharedVault = SharedVault.create({
|
||||
fileUploadBytesLimit: 100,
|
||||
fileUploadBytesUsed: 2,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
sharedVaultRepository = {} as jest.Mocked<SharedVaultRepositoryInterface>
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(sharedVault)
|
||||
|
||||
sharedVaultUser = SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockResolvedValue(sharedVaultUser)
|
||||
|
||||
tokenEncoder = {} as jest.Mocked<TokenEncoderInterface<SharedVaultValetTokenData>>
|
||||
tokenEncoder.encodeExpirableToken = jest.fn().mockReturnValue('encoded-token')
|
||||
})
|
||||
|
||||
it('should return error when shared vault uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: 'invalid-uuid',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Read,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid-uuid')
|
||||
})
|
||||
|
||||
it('should return error when user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: 'invalid-uuid',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Read,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid-uuid')
|
||||
})
|
||||
|
||||
it('should return error when shared vault is not found', async () => {
|
||||
sharedVaultRepository.findByUuid = jest.fn().mockResolvedValue(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Read,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Shared vault not found')
|
||||
})
|
||||
|
||||
it('should return error when shared vault user is not found', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockResolvedValue(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Read,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Shared vault user not found')
|
||||
})
|
||||
|
||||
it('should return error when shared vault user does not have permission', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Write,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('User does not have permission to perform this operation')
|
||||
})
|
||||
|
||||
it('should create a shared vault file valet token', async () => {
|
||||
sharedVaultUser = SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest.fn().mockResolvedValue(sharedVaultUser)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Write,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(result.getValue()).toBe('encoded-token')
|
||||
})
|
||||
|
||||
describe('move operation', () => {
|
||||
beforeEach(() => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
})
|
||||
|
||||
it('should return error when move operation type is not specified', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Move,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Move operation type is required')
|
||||
})
|
||||
|
||||
it('should return error when target uuid is missing on a shared-vault-to-shared-vault move operation', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Move,
|
||||
moveOperationType: 'shared-vault-to-shared-vault',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Shared vault to shared vault move target uuid is required')
|
||||
})
|
||||
|
||||
it('should return error when target uuid is invalid on a shared-vault-to-shared-vault move operation', async () => {
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Move,
|
||||
moveOperationType: 'shared-vault-to-shared-vault',
|
||||
sharedVaultToSharedVaultMoveTargetUuid: 'invalid-uuid',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Given value is not a valid uuid: invalid-uuid')
|
||||
})
|
||||
|
||||
it('should return error when target shared vault user is not found on a shared-vault-to-shared-vault move operation', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
.mockReturnValueOnce(null)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Move,
|
||||
moveOperationType: 'shared-vault-to-shared-vault',
|
||||
sharedVaultToSharedVaultMoveTargetUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Shared vault target user not found')
|
||||
})
|
||||
|
||||
it('should return error when target shared vault user does not have permission on a shared-vault-to-shared-vault move operation', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Read).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Move,
|
||||
moveOperationType: 'shared-vault-to-shared-vault',
|
||||
sharedVaultToSharedVaultMoveTargetUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('User does not have permission to perform this operation')
|
||||
})
|
||||
|
||||
it('should create move valet token for shared-vault-to-shared-vault operation', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Move,
|
||||
moveOperationType: 'shared-vault-to-shared-vault',
|
||||
sharedVaultToSharedVaultMoveTargetUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(result.getValue()).toBe('encoded-token')
|
||||
})
|
||||
|
||||
it('should create move valet token for shared-vault-to-user operation', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Move,
|
||||
moveOperationType: 'shared-vault-to-user',
|
||||
sharedVaultToSharedVaultMoveTargetUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(result.getValue()).toBe('encoded-token')
|
||||
})
|
||||
|
||||
it('should create move valet token for user-to-shared-vault operation', async () => {
|
||||
sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
.mockReturnValueOnce(
|
||||
SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create(SharedVaultUserPermission.PERMISSIONS.Write).getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue(),
|
||||
)
|
||||
|
||||
const useCase = createUseCase()
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
sharedVaultUuid: '00000000-0000-0000-0000-000000000000',
|
||||
remoteIdentifier: 'remote-identifier',
|
||||
operation: ValetTokenOperation.Move,
|
||||
moveOperationType: 'user-to-shared-vault',
|
||||
sharedVaultToSharedVaultMoveTargetUuid: '00000000-0000-0000-0000-000000000000',
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(result.getValue()).toBe('encoded-token')
|
||||
})
|
||||
})
|
||||
})
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
import { SharedVaultValetTokenData, TokenEncoderInterface, ValetTokenOperation } from '@standardnotes/security'
|
||||
|
||||
import { SharedVaultRepositoryInterface } from '../../SharedVault/SharedVaultRepositoryInterface'
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/User/SharedVaultUserRepositoryInterface'
|
||||
import { CreateSharedVaultFileValetTokenDTO } from './CreateSharedVaultFileValetTokenDTO'
|
||||
import { SharedVaultUserPermission } from '../../SharedVault/User/SharedVaultUserPermission'
|
||||
|
||||
export class CreateSharedVaultFileValetToken implements UseCaseInterface<string> {
|
||||
constructor(
|
||||
private sharedVaultRepository: SharedVaultRepositoryInterface,
|
||||
private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||
private tokenEncoder: TokenEncoderInterface<SharedVaultValetTokenData>,
|
||||
private valetTokenTTL: number,
|
||||
) {}
|
||||
|
||||
async execute(dto: CreateSharedVaultFileValetTokenDTO): Promise<Result<string>> {
|
||||
const sharedVaultUuidOrError = Uuid.create(dto.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const sharedVault = await this.sharedVaultRepository.findByUuid(sharedVaultUuid)
|
||||
if (!sharedVault) {
|
||||
return Result.fail('Shared vault not found')
|
||||
}
|
||||
|
||||
const sharedVaultUser = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
|
||||
userUuid: userUuid,
|
||||
sharedVaultUuid: sharedVaultUuid,
|
||||
})
|
||||
if (!sharedVaultUser) {
|
||||
return Result.fail('Shared vault user not found')
|
||||
}
|
||||
|
||||
if (
|
||||
sharedVaultUser.props.permission.value === SharedVaultUserPermission.PERMISSIONS.Read &&
|
||||
dto.operation !== ValetTokenOperation.Read
|
||||
) {
|
||||
return Result.fail('User does not have permission to perform this operation')
|
||||
}
|
||||
|
||||
if (dto.operation === ValetTokenOperation.Move) {
|
||||
if (!dto.moveOperationType) {
|
||||
return Result.fail('Move operation type is required')
|
||||
}
|
||||
|
||||
if (dto.moveOperationType === 'shared-vault-to-shared-vault') {
|
||||
if (!dto.sharedVaultToSharedVaultMoveTargetUuid) {
|
||||
return Result.fail('Shared vault to shared vault move target uuid is required')
|
||||
}
|
||||
|
||||
const sharedVaultTargetUuidOrError = Uuid.create(dto.sharedVaultToSharedVaultMoveTargetUuid)
|
||||
if (sharedVaultTargetUuidOrError.isFailed()) {
|
||||
return Result.fail(sharedVaultTargetUuidOrError.getError())
|
||||
}
|
||||
const sharedVaultTargetUuid = sharedVaultTargetUuidOrError.getValue()
|
||||
|
||||
const toSharedVaultUser = await this.sharedVaultUserRepository.findByUserUuidAndSharedVaultUuid({
|
||||
userUuid: userUuid,
|
||||
sharedVaultUuid: sharedVaultTargetUuid,
|
||||
})
|
||||
|
||||
if (!toSharedVaultUser) {
|
||||
return Result.fail('Shared vault target user not found')
|
||||
}
|
||||
|
||||
if (toSharedVaultUser.props.permission.value === SharedVaultUserPermission.PERMISSIONS.Read) {
|
||||
return Result.fail('User does not have permission to perform this operation')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tokenData: SharedVaultValetTokenData = {
|
||||
sharedVaultUuid: dto.sharedVaultUuid,
|
||||
permittedOperation: dto.operation,
|
||||
remoteIdentifier: dto.remoteIdentifier,
|
||||
uploadBytesUsed: sharedVault.props.fileUploadBytesUsed,
|
||||
uploadBytesLimit: sharedVault.props.fileUploadBytesLimit,
|
||||
unencryptedFileSize: dto.unencryptedFileSize,
|
||||
moveOperation: this.createMoveOperationData(dto),
|
||||
}
|
||||
|
||||
const valetToken = this.tokenEncoder.encodeExpirableToken(tokenData, this.valetTokenTTL)
|
||||
|
||||
return Result.ok(valetToken)
|
||||
}
|
||||
|
||||
private createMoveOperationData(dto: CreateSharedVaultFileValetTokenDTO): SharedVaultValetTokenData['moveOperation'] {
|
||||
if (!dto.moveOperationType) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
let fromUuid: string
|
||||
let toUuid: string
|
||||
switch (dto.moveOperationType) {
|
||||
case 'shared-vault-to-user':
|
||||
fromUuid = dto.sharedVaultUuid
|
||||
toUuid = dto.userUuid
|
||||
break
|
||||
case 'user-to-shared-vault':
|
||||
fromUuid = dto.userUuid
|
||||
toUuid = dto.sharedVaultUuid
|
||||
break
|
||||
case 'shared-vault-to-shared-vault':
|
||||
fromUuid = dto.sharedVaultUuid
|
||||
toUuid = dto.sharedVaultToSharedVaultMoveTargetUuid as string
|
||||
break
|
||||
}
|
||||
|
||||
return {
|
||||
type: dto.moveOperationType,
|
||||
fromUuid,
|
||||
toUuid,
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { SharedVaultMoveType, ValetTokenOperation } from '@standardnotes/security'
|
||||
|
||||
export interface CreateSharedVaultFileValetTokenDTO {
|
||||
userUuid: string
|
||||
sharedVaultUuid: string
|
||||
fileUuid?: string
|
||||
remoteIdentifier: string
|
||||
operation: ValetTokenOperation
|
||||
unencryptedFileSize?: number
|
||||
moveOperationType?: SharedVaultMoveType
|
||||
sharedVaultToSharedVaultMoveTargetUuid?: string
|
||||
}
|
||||
@@ -21,7 +21,7 @@ export class TypeORMSharedVaultRepository implements SharedVaultRepositoryInterf
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault')
|
||||
.where('shared_vault.uuid = :uuid', {
|
||||
uuid: uuid.toString(),
|
||||
uuid: uuid.value,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
|
||||
@@ -11,6 +11,27 @@ export class TypeORMSharedVaultUserRepository implements SharedVaultUserReposito
|
||||
private mapper: MapperInterface<SharedVaultUser, TypeORMSharedVaultUser>,
|
||||
) {}
|
||||
|
||||
async findByUserUuidAndSharedVaultUuid(dto: {
|
||||
userUuid: Uuid
|
||||
sharedVaultUuid: Uuid
|
||||
}): Promise<SharedVaultUser | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_user')
|
||||
.where('shared_vault_user.user_uuid = :userUuid', {
|
||||
userUuid: dto.userUuid.value,
|
||||
})
|
||||
.andWhere('shared_vault_user.shared_vault_uuid = :sharedVaultUuid', {
|
||||
sharedVaultUuid: dto.sharedVaultUuid.value,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.mapper.toDomain(persistence)
|
||||
}
|
||||
|
||||
async save(sharedVaultUser: SharedVaultUser): Promise<void> {
|
||||
const persistence = this.mapper.toProjection(sharedVaultUser)
|
||||
|
||||
@@ -21,7 +42,7 @@ export class TypeORMSharedVaultUserRepository implements SharedVaultUserReposito
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('shared_vault_user')
|
||||
.where('shared_vault_user.uuid = :uuid', {
|
||||
uuid: uuid.toString(),
|
||||
uuid: uuid.value,
|
||||
})
|
||||
.getOne()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user