wip: vault sync filter

This commit is contained in:
Mo
2023-05-30 08:18:01 -05:00
parent 5bdbf54495
commit c752775a1c
16 changed files with 401 additions and 164 deletions
@@ -20,6 +20,7 @@ import { SyncItems } from '../Domain/UseCase/SyncItems'
import { ItemConflictProjector } from '../Projection/ItemConflictProjector'
import { ItemSaveValidatorInterface } from '../Domain/Item/SaveValidator/ItemSaveValidatorInterface'
import { ItemSaveValidator } from '../Domain/Item/SaveValidator/ItemSaveValidator'
import { VaultFilter } from '../Domain/Item/SaveRule/VaultFilter'
import { OwnershipFilter } from '../Domain/Item/SaveRule/OwnershipFilter'
import { TimeDifferenceFilter } from '../Domain/Item/SaveRule/TimeDifferenceFilter'
import { ItemFactoryInterface } from '../Domain/Item/ItemFactoryInterface'
@@ -46,7 +47,7 @@ import { VaultInviteFactory } from '../Domain/VaultInvite/Factory/VaultInviteFac
import { ContactServiceInterface } from '../Domain/Contact/Service/ContactServiceInterface'
import { ContactService } from '../Domain/Contact/Service/ContactService'
import { ContactFactoryInterface } from '../Domain/Contact/Factory/ContactFactoryInterface'
import { SnjsVersionFilter } from '../Domain/Item/SaveRule/SnjsVersionFilter'
import { VaultSnjsVersionFilter } from '../Domain/Item/SaveRule/VaultSnjsVersionFilter'
import { CreateVaultFileValetToken } from '../Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetToken'
export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
@@ -226,11 +227,12 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
return new ContactFactory(context.container.get(TYPES.Timer))
})
container.bind<OwnershipFilter>(TYPES.OwnershipFilter).toDynamicValue(() => new OwnershipFilter())
container
.bind<OwnershipFilter>(TYPES.OwnershipFilter)
.bind<VaultFilter>(TYPES.VaultFilter)
.toDynamicValue(
(context: interfaces.Context) =>
new OwnershipFilter(context.container.get(TYPES.VaultService), context.container.get(TYPES.VaultUserService)),
new VaultFilter(context.container.get(TYPES.VaultService), context.container.get(TYPES.VaultUserService)),
)
container
.bind<TimeDifferenceFilter>(TYPES.TimeDifferenceFilter)
@@ -238,14 +240,17 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
container.bind<UuidFilter>(TYPES.UuidFilter).toDynamicValue(() => new UuidFilter())
container.bind<ContentTypeFilter>(TYPES.ContentTypeFilter).toDynamicValue(() => new ContentTypeFilter())
container.bind<ContentFilter>(TYPES.ContentFilter).toDynamicValue(() => new ContentFilter())
container.bind<SnjsVersionFilter>(TYPES.SnjsVersionFilter).toDynamicValue(() => new SnjsVersionFilter())
container
.bind<VaultSnjsVersionFilter>(TYPES.VaultSnjsVersionFilter)
.toDynamicValue(() => new VaultSnjsVersionFilter())
container
.bind<ItemSaveValidatorInterface>(TYPES.ItemSaveValidator)
.toDynamicValue((context: interfaces.Context) => {
return new ItemSaveValidator([
context.container.get(TYPES.SnjsVersionFilter),
context.container.get(TYPES.OwnershipFilter),
context.container.get(TYPES.VaultFilter),
context.container.get(TYPES.VaultSnjsVersionFilter),
context.container.get(TYPES.TimeDifferenceFilter),
context.container.get(TYPES.UuidFilter),
context.container.get(TYPES.ContentTypeFilter),
@@ -97,7 +97,8 @@ const TYPES = {
ItemBackupService: Symbol.for('ItemBackupService'),
ItemSaveValidator: Symbol.for('ItemSaveValidator'),
OwnershipFilter: Symbol.for('OwnershipFilter'),
SnjsVersionFilter: Symbol.for('SnjsVersionFilter'),
VaultFilter: Symbol.for('VaultFilter'),
VaultSnjsVersionFilter: Symbol.for('VaultSnjsVersionFilter'),
TimeDifferenceFilter: Symbol.for('TimeDifferenceFilter'),
UuidFilter: Symbol.for('UuidFilter'),
ContentTypeFilter: Symbol.for('ContentTypeFilter'),
@@ -1,4 +1,4 @@
import { ConflictType } from '@standardnotes/responses'
import { ConflictType } from '../../Tmp/ConflictType'
import { Item } from './Item'
import { ItemHash } from './ItemHash'
@@ -16,11 +16,11 @@ import { ItemServiceInterface } from './ItemServiceInterface'
import { SaveItemsDTO } from './SaveItemsDTO'
import { SaveItemsResult } from './SaveItemsResult'
import { ItemSaveValidatorInterface } from './SaveValidator/ItemSaveValidatorInterface'
import { ConflictType } from '@standardnotes/responses'
import { ItemTransferCalculatorInterface } from './ItemTransferCalculatorInterface'
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
import { ItemProjection } from '../../Projection/ItemProjection'
import { VaultUserRepositoryInterface } from '../VaultUser/Repository/VaultUserRepositoryInterface'
import { ConflictType } from '../../Tmp/ConflictType'
export class ItemService implements ItemServiceInterface {
private readonly DEFAULT_ITEMS_LIMIT = 150
@@ -100,16 +100,18 @@ export class ItemService implements ItemServiceInterface {
const lastUpdatedTimestamp = this.timer.getTimestampInMicroseconds()
for (const itemHash of dto.itemHashes) {
const existingItem = await this.itemRepository.findByUuid(itemHash.uuid)
if (dto.readOnlyAccess) {
conflicts.push({
unsavedItem: itemHash,
serverItem: existingItem ?? undefined,
type: ConflictType.ReadOnlyError,
})
continue
}
const existingItem = await this.itemRepository.findByUuid(itemHash.uuid)
const processingResult = await this.itemSaveValidator.validate({
userUuid: dto.userUuid,
apiVersion: dto.apiVersion,
@@ -1,7 +1,7 @@
import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO'
import { ItemSaveRuleResult } from './ItemSaveRuleResult'
import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
import { ConflictType } from '@standardnotes/responses'
import { ConflictType } from '../../../Tmp/ConflictType'
export class ContentFilter implements ItemSaveRuleInterface {
async check(dto: ItemSaveValidationDTO): Promise<ItemSaveRuleResult> {
@@ -3,7 +3,7 @@ import { ContentType } from '@standardnotes/common'
import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO'
import { ItemSaveRuleResult } from './ItemSaveRuleResult'
import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
import { ConflictType } from '@standardnotes/responses'
import { ConflictType } from '../../../Tmp/ConflictType'
export class ContentTypeFilter implements ItemSaveRuleInterface {
async check(dto: ItemSaveValidationDTO): Promise<ItemSaveRuleResult> {
@@ -1,152 +1,30 @@
import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO'
import { ItemSaveRuleResult } from './ItemSaveRuleResult'
import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
import { ConflictType } from '@standardnotes/responses'
import { VaultUserServiceInterface } from '../../VaultUser/Service/VaultUserServiceInterface'
import { ItemHash } from '../ItemHash'
import { VaultUserPermission } from '../../VaultUser/Model/VaultUserPermission'
import { VaultServiceInterface } from '../../Vault/Service/VaultServiceInterface'
import { ContentType } from '@standardnotes/common'
import { ConflictType } from '../../../Tmp/ConflictType'
export class OwnershipFilter implements ItemSaveRuleInterface {
constructor(private vaultService: VaultServiceInterface, private vaultUserService: VaultUserServiceInterface) {}
async check(dto: ItemSaveValidationDTO): Promise<ItemSaveRuleResult> {
const successValue = {
const deferToVaultFilter = dto.existingItem?.vaultUuid || dto.itemHash.vault_uuid
if (deferToVaultFilter) {
return {
passed: true,
}
}
const itemBelongsToADifferentUser = dto.existingItem !== null && dto.existingItem.userUuid !== dto.userUuid
if (itemBelongsToADifferentUser) {
return {
passed: false,
conflict: {
unsavedItem: dto.itemHash,
type: ConflictType.UuidConflict,
},
}
}
return {
passed: true,
}
const vaultReadonlyFail = {
passed: false,
conflict: {
unsavedItem: dto.itemHash,
type: ConflictType.ReadOnlyError,
},
}
const ownershipFail = {
passed: false,
conflict: {
unsavedItem: dto.itemHash,
type: ConflictType.UuidConflict,
},
}
const contentFail = {
passed: false,
conflict: {
unsavedItem: dto.itemHash,
type: ConflictType.ContentError,
},
}
const vaultUuidInvolved = dto.existingItem?.vaultUuid || dto.itemHash.vault_uuid
if (vaultUuidInvolved) {
const result = await this.authorizationToSaveVaultInvolvedItem(dto)
if (result === 'ownership-fail') {
return ownershipFail
} else if (result === 'readonly-fail') {
return vaultReadonlyFail
} else if (result === 'content-fail') {
return contentFail
} else if (result === 'success') {
return successValue
} else {
throw new Error(`Unexpected vault authorization result: ${result}`)
}
}
const itemBelongsToADifferentUser =
dto.existingItem != null && dto.existingItem.userUuid && dto.existingItem.userUuid !== dto.userUuid
if (itemBelongsToADifferentUser) {
return ownershipFail
}
return successValue
}
private async authorizationToSaveVaultInvolvedItem(
dto: ItemSaveValidationDTO,
): Promise<'ownership-fail' | 'readonly-fail' | 'content-fail' | 'success'> {
const existingItemVaultUuid = dto.existingItem?.vaultUuid
const targetItemVaultUuid = dto.itemHash.vault_uuid
const vaultUuidInvolved = existingItemVaultUuid || targetItemVaultUuid
const isMovingVaults = existingItemVaultUuid && targetItemVaultUuid && existingItemVaultUuid !== targetItemVaultUuid
if (isMovingVaults) {
const existingVaultAuthorization = await this.vaultAuthorizationForItem(dto.userUuid, existingItemVaultUuid)
const targetVaultAuthorization = await this.vaultAuthorizationForItem(dto.userUuid, targetItemVaultUuid)
if (!existingVaultAuthorization || !targetVaultAuthorization) {
return 'ownership-fail'
}
if (existingVaultAuthorization === 'read' || targetVaultAuthorization === 'read') {
return 'readonly-fail'
}
}
const vaultAuthorization = await this.vaultAuthorizationForItem(dto.userUuid, vaultUuidInvolved as string)
if (!vaultAuthorization) {
return 'ownership-fail'
}
if (vaultAuthorization === 'read') {
return 'readonly-fail'
}
const isItemBeingRemovedFromVault =
dto.existingItem != null && dto.existingItem.vaultUuid != null && dto.itemHash.vault_uuid == null
const isItemBeingDeleted = dto.itemHash.deleted === true
if (isItemBeingRemovedFromVault || isItemBeingDeleted) {
const doesHavePermissionToRemoveItemFromVault =
vaultAuthorization === 'admin' || dto.existingItem?.createdByUuid === dto.userUuid
return doesHavePermissionToRemoveItemFromVault ? 'success' : 'readonly-fail'
}
if (dto.itemHash.content_type === ContentType.VaultItemsKey && vaultAuthorization !== 'admin') {
return 'readonly-fail'
}
const usingValidKey = await this.vaultItemIsBeingSavedWithValidItemsKey(dto.itemHash)
if (!usingValidKey) {
return 'content-fail'
}
return 'success'
}
private async vaultItemIsBeingSavedWithValidItemsKey(itemHash: ItemHash): Promise<boolean> {
const isItemNotEncryptedByItemsKey = itemHash.content_type === ContentType.VaultItemsKey
if (isItemNotEncryptedByItemsKey) {
return true
}
const vault = await this.vaultService.getVault({ vaultUuid: itemHash.vault_uuid as string })
if (!vault) {
return false
}
return itemHash.items_key_id === vault.specifiedItemsKeyUuid
}
private async vaultAuthorizationForItem(
userUuid: string,
vaultUuid: string,
): Promise<VaultUserPermission | undefined> {
const vaultUser = await this.vaultUserService.getUserForVault({
userUuid,
vaultUuid: vaultUuid,
})
if (vaultUser) {
return vaultUser.permissions
}
return undefined
}
}
@@ -5,7 +5,7 @@ import { ItemHash } from '../ItemHash'
import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO'
import { ItemSaveRuleResult } from './ItemSaveRuleResult'
import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
import { ConflictType } from '@standardnotes/responses'
import { ConflictType } from '../../../Tmp/ConflictType'
export class TimeDifferenceFilter implements ItemSaveRuleInterface {
constructor(private timer: TimerInterface) {}
@@ -2,7 +2,7 @@ import { validate } from 'uuid'
import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO'
import { ItemSaveRuleResult } from './ItemSaveRuleResult'
import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
import { ConflictType } from '@standardnotes/responses'
import { ConflictType } from '../../../Tmp/ConflictType'
export class UuidFilter implements ItemSaveRuleInterface {
async check(dto: ItemSaveValidationDTO): Promise<ItemSaveRuleResult> {
@@ -0,0 +1,295 @@
import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO'
import { ItemSaveRuleResult } from './ItemSaveRuleResult'
import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
import { VaultUserServiceInterface } from '../../VaultUser/Service/VaultUserServiceInterface'
import { ItemHash } from '../ItemHash'
import { VaultUserPermission } from '../../VaultUser/Model/VaultUserPermission'
import { VaultServiceInterface } from '../../Vault/Service/VaultServiceInterface'
import { ContentType } from '@standardnotes/common'
import { ConflictType } from '../../../Tmp/ConflictType'
import {
VaultSaveOperation,
AddToVaultSaveOperation,
RemoveFromVaultSaveOperation,
MoveToOtherVaultSaveOperation,
SaveToVaultSaveOperation,
CreateToVaultSaveOperation,
} from './VaultSaveOperation'
export class VaultFilter implements ItemSaveRuleInterface {
constructor(private vaultService: VaultServiceInterface, private vaultUserService: VaultUserServiceInterface) {}
async check(dto: ItemSaveValidationDTO): Promise<ItemSaveRuleResult> {
if (!dto.existingItem?.vaultUuid && !dto.itemHash.vault_uuid) {
return {
passed: true,
}
}
const operation = this.getSaveOperationType(dto)
return this.getResultForOperation(operation)
}
private async getResultForOperation(operation: VaultSaveOperation): Promise<ItemSaveRuleResult> {
if (operation.type === 'add-to-vault') {
return this.handleAddToVaultOperation(operation)
} else if (operation.type === 'remove-from-vault') {
return this.handleRemoveFromVaultOperation(operation)
} else if (operation.type === 'move-to-other-vault') {
return this.handleMoveToOtherVaultOperation(operation)
} else if (operation.type === 'save-to-vault') {
return this.handleSaveToVaultOperation(operation)
} else if (operation.type === 'create-to-vault') {
return this.handleCreateToVaultOperation(operation)
}
throw new Error(`Unsupported vault operation: ${operation}`)
}
private getSaveOperationType(dto: ItemSaveValidationDTO): VaultSaveOperation {
const existingItemVaultUuid = dto.existingItem?.vaultUuid
const targetItemVaultUuid = dto.itemHash.vault_uuid
const common = {
incomingItem: dto.itemHash,
userUuid: dto.userUuid,
}
if (
dto.existingItem &&
existingItemVaultUuid &&
targetItemVaultUuid &&
existingItemVaultUuid !== targetItemVaultUuid
) {
return {
type: 'move-to-other-vault',
sourceVaultUuid: existingItemVaultUuid,
targetVaultUuid: targetItemVaultUuid,
existingItem: dto.existingItem,
...common,
}
}
if (dto.existingItem && existingItemVaultUuid && !targetItemVaultUuid) {
return {
type: 'remove-from-vault',
vaultUuid: existingItemVaultUuid,
existingItem: dto.existingItem,
...common,
}
}
if (dto.existingItem && !existingItemVaultUuid && targetItemVaultUuid) {
return {
type: 'add-to-vault',
vaultUuid: targetItemVaultUuid,
existingItem: dto.existingItem,
...common,
}
}
if (dto.existingItem && existingItemVaultUuid && existingItemVaultUuid === targetItemVaultUuid) {
return {
type: 'save-to-vault',
vaultUuid: existingItemVaultUuid,
existingItem: dto.existingItem,
...common,
}
}
if (!dto.existingItem && targetItemVaultUuid) {
return {
type: 'create-to-vault',
vaultUuid: targetItemVaultUuid,
...common,
}
}
throw new Error('Invalid save operation')
}
private isAuthorizedToSaveContentType(contentType: ContentType, permissions: VaultUserPermission): boolean {
if (contentType === ContentType.VaultItemsKey) {
return permissions === 'admin'
}
return true
}
private buildFailResult(operation: VaultSaveOperation, type: ConflictType) {
return {
passed: false,
conflict: {
unsavedItem: operation.incomingItem,
type,
},
}
}
private buildSuccessValue(): ItemSaveRuleResult {
return {
passed: true,
}
}
private async handleAddToVaultOperation(operation: AddToVaultSaveOperation): Promise<ItemSaveRuleResult> {
const vaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid)
if (!vaultPermissions) {
return this.buildFailResult(operation, ConflictType.VaultNotMemberError)
}
if (operation.existingItem.deleted || operation.incomingItem.deleted) {
return this.buildFailResult(operation, ConflictType.VaultInvalidState)
}
if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, vaultPermissions)) {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
if (vaultPermissions === 'read') {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
if (operation.existingItem.userUuid !== operation.userUuid) {
return this.buildFailResult(operation, ConflictType.UuidConflict)
}
const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem)
if (!usesValidKey) {
return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey)
}
return this.buildSuccessValue()
}
private async handleRemoveFromVaultOperation(operation: RemoveFromVaultSaveOperation): Promise<ItemSaveRuleResult> {
const vaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid)
if (!vaultPermissions) {
return this.buildFailResult(operation, ConflictType.VaultNotMemberError)
}
if (operation.existingItem.deleted || operation.incomingItem.deleted) {
return this.buildFailResult(operation, ConflictType.VaultInvalidState)
}
if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, vaultPermissions)) {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
if (vaultPermissions === 'read') {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem)
if (!usesValidKey) {
return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey)
}
return this.buildSuccessValue()
}
private async handleMoveToOtherVaultOperation(operation: MoveToOtherVaultSaveOperation): Promise<ItemSaveRuleResult> {
const sourceVaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.sourceVaultUuid)
const targetVaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.targetVaultUuid)
if (!sourceVaultPermissions || !targetVaultPermissions) {
return this.buildFailResult(operation, ConflictType.VaultNotMemberError)
}
if (operation.existingItem.deleted || operation.incomingItem.deleted) {
return this.buildFailResult(operation, ConflictType.VaultInvalidState)
}
if (sourceVaultPermissions === 'read' || targetVaultPermissions === 'read') {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
if (
!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, sourceVaultPermissions) ||
!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, targetVaultPermissions)
) {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem)
if (!usesValidKey) {
return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey)
}
return this.buildSuccessValue()
}
private async handleSaveToVaultOperation(operation: SaveToVaultSaveOperation): Promise<ItemSaveRuleResult> {
const vaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid)
if (!vaultPermissions) {
return this.buildFailResult(operation, ConflictType.VaultNotMemberError)
}
if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, vaultPermissions)) {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
if (vaultPermissions === 'read') {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem)
if (!usesValidKey) {
return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey)
}
return this.buildSuccessValue()
}
private async handleCreateToVaultOperation(operation: CreateToVaultSaveOperation): Promise<ItemSaveRuleResult> {
const vaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid)
if (!vaultPermissions) {
return this.buildFailResult(operation, ConflictType.VaultNotMemberError)
}
if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, vaultPermissions)) {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
if (vaultPermissions === 'read') {
return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError)
}
const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem)
if (!usesValidKey) {
return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey)
}
return this.buildSuccessValue()
}
private async incomingItemUsesValidItemsKey(itemHash: ItemHash): Promise<boolean> {
const isItemNotEncryptedByItemsKey = itemHash.content_type === ContentType.VaultItemsKey
if (isItemNotEncryptedByItemsKey) {
return true
}
const vault = await this.vaultService.getVault({ vaultUuid: itemHash.vault_uuid as string })
if (!vault) {
return false
}
return itemHash.items_key_id === vault.specifiedItemsKeyUuid
}
private async getVaultPermissions(userUuid: string, vaultUuid: string): Promise<VaultUserPermission | undefined> {
const vaultUser = await this.vaultUserService.getUserForVault({
userUuid,
vaultUuid: vaultUuid,
})
if (vaultUser) {
return vaultUser.permissions
}
return undefined
}
}
@@ -0,0 +1,44 @@
import { ItemHash } from '../ItemHash'
import { Item } from '../Item'
type SaveOperationCommon = {
incomingItem: ItemHash
userUuid: string
}
export type AddToVaultSaveOperation = SaveOperationCommon & {
type: 'add-to-vault'
vaultUuid: string
existingItem: Item
}
export type RemoveFromVaultSaveOperation = SaveOperationCommon & {
type: 'remove-from-vault'
vaultUuid: string
existingItem: Item
}
export type MoveToOtherVaultSaveOperation = SaveOperationCommon & {
type: 'move-to-other-vault'
sourceVaultUuid: string
targetVaultUuid: string
existingItem: Item
}
export type SaveToVaultSaveOperation = SaveOperationCommon & {
type: 'save-to-vault'
vaultUuid: string
existingItem: Item
}
export type CreateToVaultSaveOperation = SaveOperationCommon & {
type: 'create-to-vault'
vaultUuid: string
}
export type VaultSaveOperation =
| AddToVaultSaveOperation
| RemoveFromVaultSaveOperation
| MoveToOtherVaultSaveOperation
| SaveToVaultSaveOperation
| CreateToVaultSaveOperation
@@ -1,12 +1,12 @@
import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO'
import { ItemSaveRuleResult } from './ItemSaveRuleResult'
import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
import { ConflictType } from '@standardnotes/responses'
import { lt } from 'semver'
import { ConflictType } from '../../../Tmp/ConflictType'
const MINIMUM_SNJS_VERSION_FOR_VAULT_OPERATIONS = '2.200.0'
export class SnjsVersionFilter implements ItemSaveRuleInterface {
export class VaultSnjsVersionFilter implements ItemSaveRuleInterface {
async check(dto: ItemSaveValidationDTO): Promise<ItemSaveRuleResult> {
const successValue = {
passed: true,
@@ -18,13 +18,13 @@ export class SnjsVersionFilter implements ItemSaveRuleInterface {
}
const isClientSnjsVersionLessThanMinimum = lt(dto.snjsVersion, MINIMUM_SNJS_VERSION_FOR_VAULT_OPERATIONS)
if (isClientSnjsVersionLessThanMinimum) {
return {
passed: false,
conflict: {
unsavedItem: dto.itemHash,
type: ConflictType.ContentError,
serverItem: dto.existingItem ?? undefined,
type: ConflictType.SnjsVersionError,
},
}
}
@@ -1,7 +1,6 @@
import { ConflictType } from '@standardnotes/responses'
import { ItemHash } from '../ItemHash'
import { ItemProjection } from '../../../Projection/ItemProjection'
import { ConflictType } from '../../../Tmp/ConflictType'
export type SyncResponse20161215 = {
retrieved_items: Array<ItemProjection>
@@ -6,7 +6,7 @@ import { ItemHash } from '../ItemHash'
import { ItemProjection } from '../../../Projection/ItemProjection'
import { SyncResponse20161215 } from './SyncResponse20161215'
import { SyncResponseFactoryInterface } from './SyncResponseFactoryInterface'
import { ConflictType } from '@standardnotes/responses'
import { ConflictType } from '../../../Tmp/ConflictType'
export class SyncResponseFactory20161215 implements SyncResponseFactoryInterface {
private readonly LEGACY_MIN_CONFLICT_INTERVAL = 20_000_000
@@ -1,6 +1,5 @@
import { ConflictType } from '@standardnotes/responses'
import { ItemHash } from '../Domain/Item/ItemHash'
import { ConflictType } from '../Tmp/ConflictType'
import { ItemProjection } from './ItemProjection'
export type ItemConflictProjection = {
@@ -0,0 +1,14 @@
export enum ConflictType {
ConflictingData = 'sync_conflict',
UuidConflict = 'uuid_conflict',
ContentTypeError = 'content_type_error',
ContentError = 'content_error',
ReadOnlyError = 'readonly_error',
UuidError = 'uuid_error',
SnjsVersionError = 'snjs_version_error',
VaultInsufficientPermissionsError = 'vault_insufficient_permissions_error',
VaultNotMemberError = 'vault_not_member_error',
VaultNotFoundError = 'vault_not_found_error',
VaultInvalidState = 'vault_invalid_state',
VaultInvalidItemsKey = 'vault_invalid_items_key',
}