mirror of
https://github.com/standardnotes/app
synced 2026-09-13 09:46:02 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acc41edb02 | ||
|
|
804a39dabc | ||
|
|
bfd2b14264 | ||
|
|
eb062220d6 | ||
|
|
0eb552ddc7 | ||
|
|
14bae5e895 |
BIN
Binary file not shown.
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.26.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.26.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.26.32",
|
||||
"version": "1.26.34",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -36,7 +36,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/domain-core": "^1.24.0",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/utils": "workspace:*",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { SharedVaultPermission } from '@standardnotes/responses'
|
||||
import { SharedVaultUserPermission } from '@standardnotes/domain-core'
|
||||
|
||||
export type CreateSharedVaultInviteParams = {
|
||||
sharedVaultUuid: string
|
||||
recipientUuid: string
|
||||
encryptedMessage: string
|
||||
permissions: SharedVaultPermission
|
||||
permission: SharedVaultUserPermission
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { SharedVaultPermission } from '@standardnotes/responses'
|
||||
import { SharedVaultUserPermission } from '@standardnotes/domain-core'
|
||||
|
||||
export type UpdateSharedVaultInviteParams = {
|
||||
sharedVaultUuid: string
|
||||
inviteUuid: string
|
||||
encryptedMessage: string
|
||||
permissions?: SharedVaultPermission
|
||||
permission?: SharedVaultUserPermission
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export const AsymmetricMessagesPaths = {
|
||||
createMessage: '/v1/asymmetric-messages',
|
||||
getMessages: '/v1/asymmetric-messages',
|
||||
updateMessage: (messageUuid: string) => `/v1/asymmetric-messages/${messageUuid}`,
|
||||
getInboundUserMessages: () => '/v1/asymmetric-messages',
|
||||
getOutboundUserMessages: () => '/v1/asymmetric-messages/outbound',
|
||||
deleteMessage: (messageUuid: string) => `/v1/asymmetric-messages/${messageUuid}`,
|
||||
deleteAllInboundMessages: '/v1/asymmetric-messages/inbound',
|
||||
createMessage: '/v1/messages',
|
||||
getMessages: '/v1/messages',
|
||||
updateMessage: (messageUuid: string) => `/v1/messages/${messageUuid}`,
|
||||
getInboundUserMessages: () => '/v1/messages',
|
||||
getOutboundUserMessages: () => '/v1/messages/outbound',
|
||||
deleteMessage: (messageUuid: string) => `/v1/messages/${messageUuid}`,
|
||||
deleteAllInboundMessages: '/v1/messages/inbound',
|
||||
}
|
||||
|
||||
@@ -26,14 +26,14 @@ export class SharedVaultInvitesServer implements SharedVaultInvitesServerInterfa
|
||||
return this.httpService.post(SharedVaultInvitesPaths.createInvite(params.sharedVaultUuid), {
|
||||
recipient_uuid: params.recipientUuid,
|
||||
encrypted_message: params.encryptedMessage,
|
||||
permissions: params.permissions,
|
||||
permission: params.permission.value,
|
||||
})
|
||||
}
|
||||
|
||||
updateInvite(params: UpdateSharedVaultInviteParams): Promise<HttpResponse<UpdateSharedVaultInviteResponse>> {
|
||||
return this.httpService.patch(SharedVaultInvitesPaths.updateInvite(params.sharedVaultUuid, params.inviteUuid), {
|
||||
encrypted_message: params.encryptedMessage,
|
||||
permissions: params.permissions,
|
||||
permission: params.permission?.value,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.119](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.118](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.117](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.116](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/clipper",
|
||||
"description": "Web clipper browser extension for Standard Notes",
|
||||
"version": "1.1.116",
|
||||
"version": "1.1.119",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.108.50](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle bad access when accessing paths ([#2375](https://github.com/standardnotes/app/issues/2375)) ([804a39d](https://github.com/standardnotes/app/commit/804a39dabc595579aba3643ef6d6ef0c10263f9c))
|
||||
|
||||
## [3.108.49](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.48](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.47](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { MessageType } from '../test/TestIpcMessage'
|
||||
import { Store } from './javascripts/Main/Store/Store'
|
||||
import { StoreKeys } from './javascripts/Main/Store/StoreKeys'
|
||||
import { Paths, Urls } from './javascripts/Main/Types/Paths'
|
||||
import { UpdateState } from './javascripts/Main/UpdateManager'
|
||||
import { handleTestMessage } from './javascripts/Main/Utils/Testing'
|
||||
import { isTesting } from './javascripts/Main/Utils/Utils'
|
||||
import { WindowState } from './javascripts/Main/Window'
|
||||
|
||||
export class AppState {
|
||||
@@ -27,12 +24,6 @@ export class AppState {
|
||||
this.store.set(StoreKeys.LastRunVersion, this.version)
|
||||
|
||||
this.updates = new UpdateState(this)
|
||||
|
||||
if (isTesting()) {
|
||||
handleTestMessage(MessageType.AppStateCall, (method, ...args) => {
|
||||
;(this as any)[method](...args)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public isRunningVersionForFirstTime(): boolean {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-console */
|
||||
import { app, ipcMain, shell } from 'electron'
|
||||
import log from 'electron-log'
|
||||
import fs from 'fs-extra'
|
||||
@@ -9,8 +10,6 @@ import { Store } from './javascripts/Main/Store/Store'
|
||||
import { StoreKeys } from './javascripts/Main/Store/StoreKeys'
|
||||
import { isSnap } from './javascripts/Main/Types/Constants'
|
||||
import { Paths } from './javascripts/Main/Types/Paths'
|
||||
import { setupTesting } from './javascripts/Main/Utils/Testing'
|
||||
import { isTesting } from './javascripts/Main/Utils/Utils'
|
||||
import { CommandLineArgs } from './javascripts/Shared/CommandLineArgs'
|
||||
|
||||
enableExperimentalFeaturesForFileAccessFix()
|
||||
@@ -39,10 +38,6 @@ if (userDataPathIndex > 0) {
|
||||
migrateSnapStorage()
|
||||
}
|
||||
|
||||
if (isTesting()) {
|
||||
setupTesting()
|
||||
}
|
||||
|
||||
log.transports.file.level = 'info'
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
@@ -96,8 +91,12 @@ function migrateSnapStorage() {
|
||||
fs.moveSync(fullFilePath, path.join(dest, fileName), {
|
||||
overwrite: false,
|
||||
})
|
||||
} catch (error: any) {
|
||||
console.error(`Migration: error occured while moving ${fullFilePath} to ${dest}:`, error?.message ?? error)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Migration: error occured while moving ${fullFilePath} to ${dest}:`,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(error as any)?.message ?? error,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,18 +109,22 @@ function migrateSnapStorage() {
|
||||
* Backups location has not been altered by the user. Move it to the
|
||||
* user documents directory
|
||||
*/
|
||||
console.log(`Migration: moving ${store.data.backupsLocation} to ${Paths.documentsDir}`)
|
||||
const newLocation = path.join(Paths.documentsDir, path.basename(store.data.backupsLocation))
|
||||
try {
|
||||
fs.copySync(store.data.backupsLocation, newLocation)
|
||||
} catch (error: any) {
|
||||
console.error(
|
||||
`Migration: error occured while moving ${store.data.backupsLocation} to ${Paths.documentsDir}:`,
|
||||
error?.message ?? error,
|
||||
)
|
||||
const documentsDir = Paths.documentsDir
|
||||
console.log(`Migration: moving ${store.data.backupsLocation} to ${documentsDir}`)
|
||||
if (documentsDir) {
|
||||
const newLocation = path.join(documentsDir, path.basename(store.data.backupsLocation))
|
||||
try {
|
||||
fs.copySync(store.data.backupsLocation, newLocation)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Migration: error occured while moving ${store.data.backupsLocation} to ${documentsDir}:`,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(error as any)?.message ?? error,
|
||||
)
|
||||
}
|
||||
store.set(StoreKeys.LegacyTextBackupsLocation, newLocation)
|
||||
console.log('Migration: finished moving backups directory.')
|
||||
}
|
||||
store.set(StoreKeys.LegacyTextBackupsLocation, newLocation)
|
||||
console.log('Migration: finished moving backups directory.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ export class FilesBackupManager implements FileBackupsDevice {
|
||||
return value === true
|
||||
}
|
||||
|
||||
async getUserDocumentsDirectory(): Promise<string> {
|
||||
async getUserDocumentsDirectory(): Promise<string | undefined> {
|
||||
return Paths.documentsDir
|
||||
}
|
||||
|
||||
@@ -103,7 +103,12 @@ export class FilesBackupManager implements FileBackupsDevice {
|
||||
}
|
||||
|
||||
const LegacyTextBackupsDirectory = 'Standard Notes Backups'
|
||||
return path.join(Paths.homeDir, LegacyTextBackupsDirectory)
|
||||
const homeDir = Paths.homeDir
|
||||
if (homeDir) {
|
||||
return path.join(homeDir, LegacyTextBackupsDirectory)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
private getFileBackupsMappingFilePath(backupsLocation: string): string {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { compareVersions } from 'compare-versions'
|
||||
import log from 'electron-log'
|
||||
import fs from 'fs'
|
||||
|
||||
@@ -247,7 +247,7 @@ export class RemoteBridge implements CrossProcessBridge {
|
||||
return this.fileBackups.migrateLegacyFileBackupsToNewStructure(newPath)
|
||||
}
|
||||
|
||||
getUserDocumentsDirectory(): Promise<string> {
|
||||
getUserDocumentsDirectory(): Promise<string | undefined> {
|
||||
return this.fileBackups.getUserDocumentsDirectory()
|
||||
}
|
||||
|
||||
|
||||
@@ -36,11 +36,19 @@ export const Paths = {
|
||||
get userDataDir(): string {
|
||||
return app.getPath('userData')
|
||||
},
|
||||
get homeDir(): string {
|
||||
return app.getPath('home')
|
||||
get homeDir(): string | undefined {
|
||||
try {
|
||||
return app.getPath('home')
|
||||
} catch (error) {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
get documentsDir(): string {
|
||||
return app.getPath('documents')
|
||||
get documentsDir(): string | undefined {
|
||||
try {
|
||||
return app.getPath('documents')
|
||||
} catch (error) {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
get tempDir(): string {
|
||||
return app.getPath('temp')
|
||||
|
||||
@@ -152,7 +152,7 @@ export class DesktopDevice extends WebOrDesktopDevice implements DesktopDeviceIn
|
||||
return this.remoteBridge.wasLegacyTextBackupsExplicitlyDisabled()
|
||||
}
|
||||
|
||||
getUserDocumentsDirectory(): Promise<string> {
|
||||
getUserDocumentsDirectory(): Promise<string | undefined> {
|
||||
return this.remoteBridge.getUserDocumentsDirectory()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.108.47",
|
||||
"version": "3.108.50",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.0.9",
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/domain-core": "^1.24.0",
|
||||
"@standardnotes/electron-clear-data": "1.1.1",
|
||||
"@standardnotes/web": "workspace:*",
|
||||
"axios": "^1.1.3",
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.21.57](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.21.56](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.21.55](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.21.55",
|
||||
"version": "1.21.57",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/domain-core": "^1.24.0",
|
||||
"@standardnotes/models": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "workspace:*",
|
||||
|
||||
+2
-2
@@ -14,9 +14,9 @@ export class GetPayloadAuthenticatedDataDetachedUseCase {
|
||||
execute(
|
||||
encrypted: EncryptedOutputParameters,
|
||||
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined {
|
||||
const itemKeyComponents = deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
||||
const contentKeyComponents = deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
||||
|
||||
const authenticatedDataString = itemKeyComponents.authenticatedData
|
||||
const authenticatedDataString = contentKeyComponents.authenticatedData
|
||||
|
||||
const result = this.parseStringUseCase.execute<
|
||||
RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.59.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/features
|
||||
|
||||
## [1.59.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/features",
|
||||
"version": "1.59.11",
|
||||
"version": "1.59.12",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -26,7 +26,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/domain-core": "^1.24.0",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.28.67](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.28.66](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.28.65](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.28.64](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.28.64",
|
||||
"version": "1.28.67",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.16.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle bad access when accessing paths ([#2375](https://github.com/standardnotes/app/issues/2375)) ([804a39d](https://github.com/standardnotes/app/commit/804a39dabc595579aba3643ef6d6ef0c10263f9c))
|
||||
|
||||
## [1.16.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.16.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.16.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.16.10",
|
||||
"version": "1.16.13",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -49,7 +49,7 @@ interface PlaintextBackupsMethods {
|
||||
interface TextBackupsMethods {
|
||||
getTextBackupsCount(location: string): Promise<number>
|
||||
saveTextBackupData(location: string, data: string): Promise<void>
|
||||
getUserDocumentsDirectory(): Promise<string>
|
||||
getUserDocumentsDirectory(): Promise<string | undefined>
|
||||
}
|
||||
|
||||
interface LegacyBackupsMethods {
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.56.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.28](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.27](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.56.27",
|
||||
"version": "3.56.30",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.46.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
## [1.46.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
## [1.46.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/models",
|
||||
"version": "1.46.14",
|
||||
"version": "1.46.16",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/domain-core": "^1.24.0",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
"@standardnotes/responses": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "workspace:^",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ImmutablePayloadCollection } from '../Collection/Payload/ImmutablePayloadCollection'
|
||||
import { ConflictDelta } from './Conflict'
|
||||
import { DecryptedPayloadInterface } from '../../Abstract/Payload/Interfaces/DecryptedPayload'
|
||||
import { DeletedPayloadInterface, isDecryptedPayload, PayloadEmitSource } from '../../Abstract/Payload'
|
||||
import { FullyFormedPayloadInterface, isDecryptedPayload, PayloadEmitSource } from '../../Abstract/Payload'
|
||||
import { HistoryMap } from '../History'
|
||||
import { extendSyncDelta, SourcelessSyncDeltaEmit, SyncDeltaEmit } from './Abstract/DeltaEmit'
|
||||
import { DeltaInterface } from './Abstract/DeltaInterface'
|
||||
@@ -11,7 +10,7 @@ import { getIncrementedDirtyIndex } from '../DirtyCounter/DirtyCounter'
|
||||
export class DeltaFileImport implements DeltaInterface {
|
||||
constructor(
|
||||
readonly baseCollection: ImmutablePayloadCollection,
|
||||
private readonly applyPayloads: DecryptedPayloadInterface[],
|
||||
private readonly applyPayloads: FullyFormedPayloadInterface[],
|
||||
protected readonly historyMap: HistoryMap,
|
||||
) {}
|
||||
|
||||
@@ -31,10 +30,7 @@ export class DeltaFileImport implements DeltaInterface {
|
||||
return result
|
||||
}
|
||||
|
||||
private resolvePayload(
|
||||
payload: DecryptedPayloadInterface | DeletedPayloadInterface,
|
||||
currentResults: SyncDeltaEmit,
|
||||
): SourcelessSyncDeltaEmit {
|
||||
private resolvePayload(payload: FullyFormedPayloadInterface, currentResults: SyncDeltaEmit): SourcelessSyncDeltaEmit {
|
||||
/**
|
||||
* Check to see if we've already processed a payload for this id.
|
||||
* If so, that would be the latest value, and not what's in the base collection.
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.402](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.401](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.400](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.399](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/releases",
|
||||
"version": "1.4.399",
|
||||
"version": "1.4.402",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/releases.json",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.13.33](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/responses
|
||||
|
||||
## [1.13.32](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/responses
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/responses",
|
||||
"version": "1.13.32",
|
||||
"version": "1.13.33",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export interface AsymmetricMessageServerHash {
|
||||
uuid: string
|
||||
user_uuid: string
|
||||
recipient_uuid: string
|
||||
sender_uuid: string
|
||||
replaceabilityIdentifier?: string
|
||||
encrypted_message: string
|
||||
|
||||
@@ -16,7 +16,7 @@ export type RawSyncData = {
|
||||
unsaved?: ConflictParams[]
|
||||
shared_vaults?: SharedVaultServerHash[]
|
||||
shared_vault_invites?: SharedVaultInviteServerHash[]
|
||||
user_events?: UserEventServerHash[]
|
||||
asymmetric_messages?: AsymmetricMessageServerHash[]
|
||||
notifications?: UserEventServerHash[]
|
||||
messages?: AsymmetricMessageServerHash[]
|
||||
status?: number
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { AsymmetricMessageServerHash } from '../AsymmetricMessage/AsymmetricMessageServerHash'
|
||||
import { SharedVaultPermission } from './SharedVaultPermission'
|
||||
|
||||
export interface SharedVaultInviteServerHash extends AsymmetricMessageServerHash {
|
||||
uuid: string
|
||||
@@ -7,7 +6,7 @@ export interface SharedVaultInviteServerHash extends AsymmetricMessageServerHash
|
||||
user_uuid: string
|
||||
sender_uuid: string
|
||||
encrypted_message: string
|
||||
permissions: SharedVaultPermission
|
||||
permission: string
|
||||
created_at_timestamp: number
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export enum SharedVaultPermission {
|
||||
Read = 'read',
|
||||
Write = 'write',
|
||||
Admin = 'admin',
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import { SharedVaultPermission } from './SharedVaultPermission'
|
||||
|
||||
export interface SharedVaultUserServerHash {
|
||||
uuid: string
|
||||
shared_vault_uuid: string
|
||||
user_uuid: string
|
||||
permissions: SharedVaultPermission
|
||||
permission: string
|
||||
updated_at_timestamp: number
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { UserEventType } from './UserEventType'
|
||||
|
||||
export type UserEventPayload =
|
||||
| {
|
||||
eventType: UserEventType.SharedVaultItemRemoved
|
||||
itemUuid: string
|
||||
sharedVaultUuid: string
|
||||
version: string
|
||||
}
|
||||
| {
|
||||
eventType: UserEventType.RemovedFromSharedVault
|
||||
sharedVaultUuid: string
|
||||
version: string
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
import { UserEventType } from './UserEventType'
|
||||
|
||||
export type UserEventServerHash = {
|
||||
uuid: string
|
||||
user_uuid: string
|
||||
event_type: UserEventType
|
||||
event_payload: string
|
||||
type: string
|
||||
payload: string
|
||||
created_at_timestamp?: number
|
||||
updated_at_timestamp?: number
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum UserEventType {
|
||||
SharedVaultItemRemoved = 'shared_vault_item_removed',
|
||||
RemovedFromSharedVault = 'removed_from_shared_vault',
|
||||
}
|
||||
@@ -36,7 +36,6 @@ export * from './Http'
|
||||
export * from './SharedVaults/SharedVaultInviteServerHash'
|
||||
export * from './SharedVaults/SharedVaultUserServerHash'
|
||||
export * from './SharedVaults/SharedVaultServerHash'
|
||||
export * from './SharedVaults/SharedVaultPermission'
|
||||
|
||||
export * from './AsymmetricMessage/AsymmetricMessageServerHash'
|
||||
|
||||
@@ -68,5 +67,3 @@ export * from './User/SettingData'
|
||||
export * from './User/UpdateSettingResponse'
|
||||
|
||||
export * from './UserEvent/UserEventServerHash'
|
||||
export * from './UserEvent/UserEventType'
|
||||
export * from './UserEvent/UserEventPayload'
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.63.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handle bad access when accessing paths ([#2375](https://github.com/standardnotes/app/issues/2375)) ([804a39d](https://github.com/standardnotes/app/commit/804a39dabc595579aba3643ef6d6ef0c10263f9c))
|
||||
|
||||
## [1.63.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.63.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.63.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/services",
|
||||
"version": "1.63.12",
|
||||
"version": "1.63.15",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -18,7 +18,7 @@
|
||||
"dependencies": {
|
||||
"@standardnotes/api": "workspace:^",
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/domain-core": "^1.24.0",
|
||||
"@standardnotes/encryption": "workspace:^",
|
||||
"@standardnotes/features": "workspace:^",
|
||||
"@standardnotes/files": "workspace:^",
|
||||
|
||||
@@ -80,7 +80,7 @@ describe('AsymmetricMessageService', () => {
|
||||
const messages: AsymmetricMessageServerHash[] = [
|
||||
{
|
||||
uuid: 'keypair-changed-message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
@@ -88,7 +88,7 @@ describe('AsymmetricMessageService', () => {
|
||||
},
|
||||
{
|
||||
uuid: 'misc-message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 1,
|
||||
@@ -119,7 +119,7 @@ describe('AsymmetricMessageService', () => {
|
||||
const messages: AsymmetricMessageServerHash[] = [
|
||||
{
|
||||
uuid: 'newer-message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
@@ -127,7 +127,7 @@ describe('AsymmetricMessageService', () => {
|
||||
},
|
||||
{
|
||||
uuid: 'older-message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 1,
|
||||
@@ -153,7 +153,7 @@ describe('AsymmetricMessageService', () => {
|
||||
it('should handle ContactShare message', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
@@ -181,7 +181,7 @@ describe('AsymmetricMessageService', () => {
|
||||
it('should handle SenderKeypairChanged message', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
@@ -210,7 +210,7 @@ describe('AsymmetricMessageService', () => {
|
||||
it('should handle SharedVaultRootKeyChanged message', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
@@ -238,7 +238,7 @@ describe('AsymmetricMessageService', () => {
|
||||
it('should handle SharedVaultMetadataChanged message', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
@@ -268,7 +268,7 @@ describe('AsymmetricMessageService', () => {
|
||||
it('should throw if message type is SharedVaultInvite', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
@@ -294,7 +294,7 @@ describe('AsymmetricMessageService', () => {
|
||||
it('should delete message from server after processing', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
recipient_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
|
||||
@@ -31,9 +31,9 @@ export class ResendAllMessages implements UseCaseInterface<void> {
|
||||
const errors: string[] = []
|
||||
|
||||
for (const message of messages.data.messages) {
|
||||
const recipient = this.findContact.execute({ userUuid: message.user_uuid })
|
||||
const recipient = this.findContact.execute({ userUuid: message.recipient_uuid })
|
||||
if (recipient.isFailed()) {
|
||||
errors.push(`Contact not found for invite ${message.user_uuid}`)
|
||||
errors.push(`Contact not found for invite ${message.recipient_uuid}`)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -160,14 +160,21 @@ export class FilesBackupService
|
||||
}
|
||||
|
||||
private async automaticallyEnableTextBackupsIfPreferenceNotSet(): Promise<void> {
|
||||
if (this.storage.getValue(StorageKey.TextBackupsEnabled) == undefined) {
|
||||
this.storage.setValue(StorageKey.TextBackupsEnabled, true)
|
||||
const location = await this.device.joinPaths(
|
||||
await this.device.getUserDocumentsDirectory(),
|
||||
await this.prependWorkspacePathForPath(TextBackupsDirectoryName),
|
||||
)
|
||||
this.storage.setValue(StorageKey.TextBackupsLocation, location)
|
||||
if (this.storage.getValue(StorageKey.TextBackupsEnabled) != undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
this.storage.setValue(StorageKey.TextBackupsEnabled, true)
|
||||
const documentsDir = await this.device.getUserDocumentsDirectory()
|
||||
if (!documentsDir) {
|
||||
return
|
||||
}
|
||||
|
||||
const location = await this.device.joinPaths(
|
||||
documentsDir,
|
||||
await this.prependWorkspacePathForPath(TextBackupsDirectoryName),
|
||||
)
|
||||
this.storage.setValue(StorageKey.TextBackupsLocation, location)
|
||||
}
|
||||
|
||||
openAllDirectoriesContainingBackupFiles(): void {
|
||||
|
||||
@@ -89,7 +89,6 @@ export interface EncryptionProviderInterface {
|
||||
setNewRootKeyWrapper(wrappingKey: RootKeyInterface): Promise<void>
|
||||
|
||||
createNewItemsKeyWithRollback(): Promise<() => Promise<void>>
|
||||
reencryptApplicableItemsAfterUserRootKeyChange(): Promise<void>
|
||||
getSureDefaultItemsKey(): ItemsKeyInterface
|
||||
|
||||
createRandomizedKeySystemRootKey(dto: { systemIdentifier: KeySystemIdentifier }): KeySystemRootKeyInterface
|
||||
|
||||
@@ -240,10 +240,6 @@ export class EncryptionService
|
||||
return this.itemsEncryption.repersistAllItems()
|
||||
}
|
||||
|
||||
public async reencryptApplicableItemsAfterUserRootKeyChange(): Promise<void> {
|
||||
await this.rootKeyManager.reencryptApplicableItemsAfterUserRootKeyChange()
|
||||
}
|
||||
|
||||
public async createNewItemsKeyWithRollback(): Promise<() => Promise<void>> {
|
||||
return this._createNewItemsKeyWithRollback.execute()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { MutatorClientInterface } from './../../../Mutator/MutatorClientInterface'
|
||||
import { ItemManagerInterface } from './../../../Item/ItemManagerInterface'
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { ContentTypesUsingRootKeyEncryption } from '@standardnotes/models'
|
||||
|
||||
/**
|
||||
* When the user root key changes, we must re-encrypt all relevant items with this new root key (by simply re-syncing).
|
||||
*/
|
||||
export class ReencryptTypeAItems implements UseCaseInterface<void> {
|
||||
constructor(private items: ItemManagerInterface, private mutator: MutatorClientInterface) {}
|
||||
|
||||
public async execute(): Promise<Result<void>> {
|
||||
const items = this.items.getItems(ContentTypesUsingRootKeyEncryption())
|
||||
if (items.length > 0) {
|
||||
/**
|
||||
* Do not call sync after marking dirty.
|
||||
* Re-encrypting items keys is called by consumers who have specific flows who
|
||||
* will sync on their own timing
|
||||
*/
|
||||
await this.mutator.setItemsDirty(items)
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -170,6 +170,9 @@ export class HomeServerService
|
||||
let location = await this.getHomeServerDataLocation()
|
||||
if (!location) {
|
||||
const documentsDirectory = await this.desktopDevice.getUserDocumentsDirectory()
|
||||
if (!documentsDirectory) {
|
||||
return
|
||||
}
|
||||
location = `${documentsDirectory}/${this.HOME_SERVER_DATA_DIRECTORY_NAME}`
|
||||
}
|
||||
|
||||
|
||||
@@ -76,9 +76,7 @@ export class KeySystemKeyManager
|
||||
}
|
||||
}
|
||||
|
||||
public getRootKeyFromStorageForVault(
|
||||
keySystemIdentifier: KeySystemIdentifier,
|
||||
): KeySystemRootKeyInterface | undefined {
|
||||
getRootKeyFromStorageForVault(keySystemIdentifier: KeySystemIdentifier): KeySystemRootKeyInterface | undefined {
|
||||
const payload = this.storage.getValue<DecryptedTransferPayload<KeySystemRootKeyContent>>(
|
||||
this.storageKeyForRootKey(keySystemIdentifier),
|
||||
)
|
||||
@@ -94,6 +92,10 @@ export class KeySystemKeyManager
|
||||
return key
|
||||
}
|
||||
|
||||
getMemCachedRootKey(systemIdentifier: KeySystemIdentifier): KeySystemRootKeyInterface {
|
||||
return this.rootKeyMemoryCache[systemIdentifier]
|
||||
}
|
||||
|
||||
private storageKeyForRootKey(systemIdentifier: KeySystemIdentifier): string {
|
||||
return `${RootKeyStorageKeyPrefix}${systemIdentifier}`
|
||||
}
|
||||
|
||||
@@ -9,18 +9,16 @@ import { ProtocolVersion, compareVersions } from '@standardnotes/common'
|
||||
import {
|
||||
BackupFile,
|
||||
BackupFileDecryptedContextualPayload,
|
||||
ComponentContent,
|
||||
CopyPayloadWithContentOverride,
|
||||
CreateDecryptedBackupFileContextPayload,
|
||||
CreateEncryptedBackupFileContextPayload,
|
||||
DecryptedItemInterface,
|
||||
DecryptedPayloadInterface,
|
||||
isDecryptedPayload,
|
||||
isEncryptedPayload,
|
||||
isEncryptedTransferPayload,
|
||||
} from '@standardnotes/models'
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { Challenge, ChallengePrompt, ChallengeReason, ChallengeValidation } from '../Challenge'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface'
|
||||
|
||||
const Strings = {
|
||||
@@ -57,44 +55,22 @@ export class ImportDataUseCase {
|
||||
* .affectedItems: Items that were either created or dirtied by this import
|
||||
* .errorCount: The number of items that were not imported due to failure to decrypt.
|
||||
*/
|
||||
|
||||
async execute(data: BackupFile, awaitSync = false): Promise<ImportDataReturnType> {
|
||||
if (data.version) {
|
||||
/**
|
||||
* Prior to 003 backup files did not have a version field so we cannot
|
||||
* stop importing if there is no backup file version, only if there is
|
||||
* an unsupported version.
|
||||
*/
|
||||
const version = data.version as ProtocolVersion
|
||||
|
||||
const supportedVersions = this.encryption.supportedVersions()
|
||||
if (!supportedVersions.includes(version)) {
|
||||
return { error: new ClientDisplayableError(Strings.UnsupportedBackupFileVersion) }
|
||||
}
|
||||
|
||||
const userVersion = this.encryption.getUserVersion()
|
||||
if (userVersion && compareVersions(version, userVersion) === 1) {
|
||||
/** File was made with a greater version than the user's account */
|
||||
return { error: new ClientDisplayableError(Strings.BackupFileMoreRecentThanAccount) }
|
||||
const result = this.validateVersion(data.version)
|
||||
if (result.isFailed()) {
|
||||
return { error: new ClientDisplayableError(result.getError()) }
|
||||
}
|
||||
}
|
||||
|
||||
let password: string | undefined
|
||||
|
||||
if (data.auth_params || data.keyParams) {
|
||||
/** Get import file password. */
|
||||
const challenge = new Challenge(
|
||||
[new ChallengePrompt(ChallengeValidation.None, Strings.FileAccountPassword, undefined, true)],
|
||||
ChallengeReason.DecryptEncryptedFile,
|
||||
true,
|
||||
)
|
||||
const passwordResponse = await this.challengeService.promptForChallengeResponse(challenge)
|
||||
if (passwordResponse == undefined) {
|
||||
/** Challenge was canceled */
|
||||
return { error: new ClientDisplayableError('Import aborted') }
|
||||
const passwordResult = await this.getFilePassword()
|
||||
if (passwordResult.isFailed()) {
|
||||
return { error: new ClientDisplayableError(passwordResult.getError()) }
|
||||
}
|
||||
this.challengeService.completeChallenge(challenge)
|
||||
password = passwordResponse?.values[0].value as string
|
||||
password = passwordResult.getValue()
|
||||
}
|
||||
|
||||
if (!(await this.protectionService.authorizeFileImport())) {
|
||||
@@ -110,31 +86,23 @@ export class ImportDataUseCase {
|
||||
})
|
||||
|
||||
const decryptedPayloadsOrError = await this._decryptBackFile.execute(data, password)
|
||||
|
||||
if (decryptedPayloadsOrError instanceof ClientDisplayableError) {
|
||||
return { error: decryptedPayloadsOrError }
|
||||
}
|
||||
|
||||
const validPayloads = decryptedPayloadsOrError.filter(isDecryptedPayload).map((payload) => {
|
||||
/* Don't want to activate any components during import process in
|
||||
* case of exceptions breaking up the import proccess */
|
||||
if (payload.content_type === ContentType.TYPES.Component && (payload.content as ComponentContent).active) {
|
||||
const typedContent = payload as DecryptedPayloadInterface<ComponentContent>
|
||||
return CopyPayloadWithContentOverride(typedContent, {
|
||||
active: false,
|
||||
})
|
||||
} else {
|
||||
return payload
|
||||
}
|
||||
const decryptedPayloads = decryptedPayloadsOrError.filter(isDecryptedPayload)
|
||||
const encryptedPayloads = decryptedPayloadsOrError.filter(isEncryptedPayload)
|
||||
const acceptableEncryptedPayloads = encryptedPayloads.filter((payload) => {
|
||||
return payload.key_system_identifier !== undefined
|
||||
})
|
||||
const importablePayloads = [...decryptedPayloads, ...acceptableEncryptedPayloads]
|
||||
|
||||
const affectedUuids = await this.payloadManager.importPayloads(
|
||||
validPayloads,
|
||||
importablePayloads,
|
||||
this.historyService.getHistoryMapCopy(),
|
||||
)
|
||||
|
||||
const promise = this.sync.sync()
|
||||
|
||||
if (awaitSync) {
|
||||
await promise
|
||||
}
|
||||
@@ -143,7 +111,42 @@ export class ImportDataUseCase {
|
||||
|
||||
return {
|
||||
affectedItems: affectedItems,
|
||||
errorCount: decryptedPayloadsOrError.length - validPayloads.length,
|
||||
errorCount: decryptedPayloadsOrError.length - importablePayloads.length,
|
||||
}
|
||||
}
|
||||
|
||||
private async getFilePassword(): Promise<Result<string>> {
|
||||
const challenge = new Challenge(
|
||||
[new ChallengePrompt(ChallengeValidation.None, Strings.FileAccountPassword, undefined, true)],
|
||||
ChallengeReason.DecryptEncryptedFile,
|
||||
true,
|
||||
)
|
||||
const passwordResponse = await this.challengeService.promptForChallengeResponse(challenge)
|
||||
if (passwordResponse == undefined) {
|
||||
/** Challenge was canceled */
|
||||
return Result.fail('Import aborted')
|
||||
}
|
||||
this.challengeService.completeChallenge(challenge)
|
||||
return Result.ok(passwordResponse?.values[0].value as string)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prior to 003 backup files did not have a version field so we cannot
|
||||
* stop importing if there is no backup file version, only if there is
|
||||
* an unsupported version.
|
||||
*/
|
||||
private validateVersion(version: ProtocolVersion): Result<void> {
|
||||
const supportedVersions = this.encryption.supportedVersions()
|
||||
if (!supportedVersions.includes(version)) {
|
||||
return Result.fail(Strings.UnsupportedBackupFileVersion)
|
||||
}
|
||||
|
||||
const userVersion = this.encryption.getUserVersion()
|
||||
if (userVersion && compareVersions(version, userVersion) === 1) {
|
||||
/** File was made with a greater version than the user's account */
|
||||
return Result.fail(Strings.BackupFileMoreRecentThanAccount)
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
EncryptedPayloadInterface,
|
||||
FullyFormedPayloadInterface,
|
||||
PayloadEmitSource,
|
||||
DecryptedPayloadInterface,
|
||||
HistoryMap,
|
||||
} from '@standardnotes/models'
|
||||
import { IntegrityPayload } from '@standardnotes/responses'
|
||||
@@ -24,7 +23,7 @@ export interface PayloadManagerInterface {
|
||||
*/
|
||||
get nonDeletedItems(): FullyFormedPayloadInterface[]
|
||||
|
||||
importPayloads(payloads: DecryptedPayloadInterface[], historyMap: HistoryMap): Promise<string[]>
|
||||
importPayloads(payloads: FullyFormedPayloadInterface[], historyMap: HistoryMap): Promise<string[]>
|
||||
|
||||
removePayloadLocally(payload: FullyFormedPayloadInterface | FullyFormedPayloadInterface[]): void
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
EncryptionOperatorsInterface,
|
||||
} from '@standardnotes/encryption'
|
||||
import {
|
||||
ContentTypesUsingRootKeyEncryption,
|
||||
DecryptedPayload,
|
||||
DecryptedTransferPayload,
|
||||
EncryptedPayload,
|
||||
@@ -32,12 +31,11 @@ import { StorageValueModes } from '../Storage/StorageTypes'
|
||||
import { EncryptTypeAPayload } from '../Encryption/UseCase/TypeA/EncryptPayload'
|
||||
import { DecryptTypeAPayload } from '../Encryption/UseCase/TypeA/DecryptPayload'
|
||||
import { AbstractService } from '../Service/AbstractService'
|
||||
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
|
||||
import { MutatorClientInterface } from '../Mutator/MutatorClientInterface'
|
||||
import { RootKeyManagerEvent } from './RootKeyManagerEvent'
|
||||
import { ValidatePasscodeResult } from './ValidatePasscodeResult'
|
||||
import { ValidateAccountPasswordResult } from './ValidateAccountPasswordResult'
|
||||
import { KeyMode } from './KeyMode'
|
||||
import { ReencryptTypeAItems } from '../Encryption/UseCase/TypeA/ReencryptTypeAItems'
|
||||
|
||||
export class RootKeyManager extends AbstractService<RootKeyManagerEvent> {
|
||||
private rootKey?: RootKeyInterface
|
||||
@@ -47,10 +45,9 @@ export class RootKeyManager extends AbstractService<RootKeyManagerEvent> {
|
||||
constructor(
|
||||
private device: DeviceInterface,
|
||||
private storage: StorageServiceInterface,
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private operators: EncryptionOperatorsInterface,
|
||||
private identifier: ApplicationIdentifier,
|
||||
private _reencryptTypeAItems: ReencryptTypeAItems,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(eventBus)
|
||||
@@ -58,6 +55,12 @@ export class RootKeyManager extends AbstractService<RootKeyManagerEvent> {
|
||||
|
||||
override deinit() {
|
||||
super.deinit()
|
||||
;(this.device as unknown) = undefined
|
||||
;(this.storage as unknown) = undefined
|
||||
;(this.operators as unknown) = undefined
|
||||
;(this.identifier as unknown) = undefined
|
||||
;(this._reencryptTypeAItems as unknown) = undefined
|
||||
|
||||
this.rootKey = undefined
|
||||
this.memoizedRootKeyParams = undefined
|
||||
}
|
||||
@@ -307,7 +310,7 @@ export class RootKeyManager extends AbstractService<RootKeyManagerEvent> {
|
||||
if (this.keyMode === KeyMode.WrapperOnly || this.keyMode === KeyMode.RootKeyPlusWrapper) {
|
||||
if (this.keyMode === KeyMode.WrapperOnly) {
|
||||
this.setRootKeyInstance(wrappingKey)
|
||||
await this.reencryptApplicableItemsAfterUserRootKeyChange()
|
||||
await this._reencryptTypeAItems.execute()
|
||||
} else {
|
||||
await this.wrapAndPersistRootKey(wrappingKey)
|
||||
}
|
||||
@@ -473,19 +476,4 @@ export class RootKeyManager extends AbstractService<RootKeyManagerEvent> {
|
||||
keyParams: keyParams.getPortableValue(),
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* When the root key changes, we must re-encrypt all relevant items with this new root key (by simply re-syncing).
|
||||
*/
|
||||
public async reencryptApplicableItemsAfterUserRootKeyChange(): Promise<void> {
|
||||
const items = this.items.getItems(ContentTypesUsingRootKeyEncryption())
|
||||
if (items.length > 0) {
|
||||
/**
|
||||
* Do not call sync after marking dirty.
|
||||
* Re-encrypting items keys is called by consumers who have specific flows who
|
||||
* will sync on their own timing
|
||||
*/
|
||||
await this.mutator.setItemsDirty(items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DiscardItemsLocally } from './../UseCase/DiscardItemsLocally'
|
||||
import { UserKeyPairChangedEventData } from './../Session/UserKeyPairChangedEventData'
|
||||
import { ClientDisplayableError, UserEventType } from '@standardnotes/responses'
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import {
|
||||
DecryptedItemInterface,
|
||||
PayloadEmitSource,
|
||||
@@ -29,7 +29,7 @@ import { CreateSharedVault } from './UseCase/CreateSharedVault'
|
||||
import { SendVaultDataChangedMessage } from './UseCase/SendVaultDataChangedMessage'
|
||||
import { ConvertToSharedVault } from './UseCase/ConvertToSharedVault'
|
||||
import { GetVault } from '../Vault/UseCase/GetVault'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { ContentType, NotificationType, Uuid } from '@standardnotes/domain-core'
|
||||
import { HandleKeyPairChange } from '../Contacts/UseCase/HandleKeyPairChange'
|
||||
import { FindContact } from '../Contacts/UseCase/FindContact'
|
||||
import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface'
|
||||
@@ -121,18 +121,18 @@ export class SharedVaultService
|
||||
}
|
||||
|
||||
private async handleUserEvent(event: UserEventServiceEventPayload): Promise<void> {
|
||||
switch (event.eventPayload.eventType) {
|
||||
case UserEventType.RemovedFromSharedVault: {
|
||||
switch (event.eventPayload.props.type.value) {
|
||||
case NotificationType.TYPES.RemovedFromSharedVault: {
|
||||
const vault = this._getVault.execute<SharedVaultListingInterface>({
|
||||
sharedVaultUuid: event.eventPayload.sharedVaultUuid,
|
||||
sharedVaultUuid: event.eventPayload.props.sharedVaultUuid.value,
|
||||
})
|
||||
if (!vault.isFailed()) {
|
||||
await this._deleteThirdPartyVault.execute(vault.getValue())
|
||||
}
|
||||
break
|
||||
}
|
||||
case UserEventType.SharedVaultItemRemoved: {
|
||||
const item = this.items.findItem(event.eventPayload.itemUuid)
|
||||
case NotificationType.TYPES.SharedVaultItemRemoved: {
|
||||
const item = this.items.findItem((event.eventPayload.props.itemUuid as Uuid).value)
|
||||
if (item) {
|
||||
void this._discardItemsLocally.execute([item])
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ export class NotifyVaultUsersOfKeyRotation implements UseCaseInterface<void> {
|
||||
sharedVault: params.sharedVault,
|
||||
sharedVaultContacts: !contacts.isFailed() ? contacts.getValue() : [],
|
||||
recipient: recipient.getValue(),
|
||||
permissions: invite.permissions,
|
||||
permission: invite.permission,
|
||||
senderUuid: params.senderUuid,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ReencryptTypeAItems } from './../Encryption/UseCase/TypeA/ReencryptTypeAItems'
|
||||
import { EncryptionProviderInterface } from './../Encryption/EncryptionProviderInterface'
|
||||
import { UserApiServiceInterface } from '@standardnotes/api'
|
||||
import { UserRequestType } from '@standardnotes/common'
|
||||
@@ -25,6 +26,7 @@ describe('UserService', () => {
|
||||
let challengeService: ChallengeServiceInterface
|
||||
let protectionService: ProtectionsClientInterface
|
||||
let userApiService: UserApiServiceInterface
|
||||
let reencryptTypeAItems: ReencryptTypeAItems
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
|
||||
const createService = () =>
|
||||
@@ -38,6 +40,7 @@ describe('UserService', () => {
|
||||
challengeService,
|
||||
protectionService,
|
||||
userApiService,
|
||||
reencryptTypeAItems,
|
||||
internalEventBus,
|
||||
)
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import { AccountEvent } from './AccountEvent'
|
||||
import { SignedInOrRegisteredEventPayload } from './SignedInOrRegisteredEventPayload'
|
||||
import { CredentialsChangeFunctionResponse } from './CredentialsChangeFunctionResponse'
|
||||
import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface'
|
||||
import { ReencryptTypeAItems } from '../Encryption/UseCase/TypeA/ReencryptTypeAItems'
|
||||
|
||||
export class UserService
|
||||
extends AbstractService<AccountEvent, AccountEventData>
|
||||
@@ -49,33 +50,48 @@ export class UserService
|
||||
private readonly MINIMUM_PASSWORD_LENGTH = 8
|
||||
|
||||
constructor(
|
||||
private sessionManager: SessionsClientInterface,
|
||||
private sessions: SessionsClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private storageService: StorageServiceInterface,
|
||||
private itemManager: ItemManagerInterface,
|
||||
private encryptionService: EncryptionProviderInterface,
|
||||
private alertService: AlertService,
|
||||
private challengeService: ChallengeServiceInterface,
|
||||
private protectionService: ProtectionsClientInterface,
|
||||
private userApiService: UserApiServiceInterface,
|
||||
private storage: StorageServiceInterface,
|
||||
private items: ItemManagerInterface,
|
||||
private encryption: EncryptionProviderInterface,
|
||||
private alerts: AlertService,
|
||||
private challenges: ChallengeServiceInterface,
|
||||
private protections: ProtectionsClientInterface,
|
||||
private userApi: UserApiServiceInterface,
|
||||
private _reencryptTypeAItems: ReencryptTypeAItems,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
}
|
||||
|
||||
public override deinit(): void {
|
||||
super.deinit()
|
||||
;(this.sessions as unknown) = undefined
|
||||
;(this.sync as unknown) = undefined
|
||||
;(this.storage as unknown) = undefined
|
||||
;(this.items as unknown) = undefined
|
||||
;(this.encryption as unknown) = undefined
|
||||
;(this.alerts as unknown) = undefined
|
||||
;(this.challenges as unknown) = undefined
|
||||
;(this.protections as unknown) = undefined
|
||||
;(this.userApi as unknown) = undefined
|
||||
;(this._reencryptTypeAItems as unknown) = undefined
|
||||
}
|
||||
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === AccountEvent.SignedInOrRegistered) {
|
||||
const payload = (event.payload as AccountEventData).payload as SignedInOrRegisteredEventPayload
|
||||
this.sync.resetSyncState()
|
||||
|
||||
await this.storageService.setPersistencePolicy(
|
||||
await this.storage.setPersistencePolicy(
|
||||
payload.ephemeral ? StoragePersistencePolicies.Ephemeral : StoragePersistencePolicies.Default,
|
||||
)
|
||||
|
||||
if (payload.mergeLocal) {
|
||||
await this.sync.markAllItemsAsNeedingSyncAndPersist()
|
||||
} else {
|
||||
void this.itemManager.removeAllItemsFromMemory()
|
||||
void this.items.removeAllItemsFromMemory()
|
||||
await this.clearDatabase()
|
||||
}
|
||||
|
||||
@@ -88,37 +104,24 @@ export class UserService
|
||||
})
|
||||
.then(() => {
|
||||
if (!payload.awaitSync) {
|
||||
void this.encryptionService.decryptErroredPayloads()
|
||||
void this.encryption.decryptErroredPayloads()
|
||||
}
|
||||
})
|
||||
|
||||
if (payload.awaitSync) {
|
||||
await syncPromise
|
||||
|
||||
await this.encryptionService.decryptErroredPayloads()
|
||||
await this.encryption.decryptErroredPayloads()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override deinit(): void {
|
||||
super.deinit()
|
||||
;(this.sessionManager as unknown) = undefined
|
||||
;(this.sync as unknown) = undefined
|
||||
;(this.storageService as unknown) = undefined
|
||||
;(this.itemManager as unknown) = undefined
|
||||
;(this.encryptionService as unknown) = undefined
|
||||
;(this.alertService as unknown) = undefined
|
||||
;(this.challengeService as unknown) = undefined
|
||||
;(this.protectionService as unknown) = undefined
|
||||
;(this.userApiService as unknown) = undefined
|
||||
}
|
||||
|
||||
getUserUuid(): string {
|
||||
return this.sessionManager.userUuid
|
||||
return this.sessions.userUuid
|
||||
}
|
||||
|
||||
isSignedIn(): boolean {
|
||||
return this.sessionManager.isSignedIn()
|
||||
return this.sessions.isSignedIn()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,7 +134,7 @@ export class UserService
|
||||
ephemeral = false,
|
||||
mergeLocal = true,
|
||||
): Promise<UserRegistrationResponseBody> {
|
||||
if (this.encryptionService.hasAccount()) {
|
||||
if (this.encryption.hasAccount()) {
|
||||
throw Error('Tried to register when an account already exists.')
|
||||
}
|
||||
|
||||
@@ -143,7 +146,7 @@ export class UserService
|
||||
|
||||
try {
|
||||
this.lockSyncing()
|
||||
const response = await this.sessionManager.register(email, password, ephemeral)
|
||||
const response = await this.sessions.register(email, password, ephemeral)
|
||||
|
||||
await this.notifyEventSync(AccountEvent.SignedInOrRegistered, {
|
||||
payload: {
|
||||
@@ -177,7 +180,7 @@ export class UserService
|
||||
mergeLocal = true,
|
||||
awaitSync = false,
|
||||
): Promise<HttpResponse<SignInResponse>> {
|
||||
if (this.encryptionService.hasAccount()) {
|
||||
if (this.encryption.hasAccount()) {
|
||||
throw Error('Tried to sign in when an account already exists.')
|
||||
}
|
||||
|
||||
@@ -191,7 +194,7 @@ export class UserService
|
||||
/** Prevent a timed sync from occuring while signing in. */
|
||||
this.lockSyncing()
|
||||
|
||||
const { response } = await this.sessionManager.signIn(email, password, strict, ephemeral)
|
||||
const { response } = await this.sessions.signIn(email, password, strict, ephemeral)
|
||||
|
||||
if (!isErrorResponse(response)) {
|
||||
const notifyingFunction = awaitSync ? this.notifyEventSync.bind(this) : this.notifyEvent.bind(this)
|
||||
@@ -218,7 +221,7 @@ export class UserService
|
||||
message?: string
|
||||
}> {
|
||||
if (
|
||||
!(await this.protectionService.authorizeAction(ChallengeReason.DeleteAccount, {
|
||||
!(await this.protections.authorizeAction(ChallengeReason.DeleteAccount, {
|
||||
fallBackToAccountPassword: true,
|
||||
requireAccountPassword: true,
|
||||
forcePrompt: false,
|
||||
@@ -230,8 +233,8 @@ export class UserService
|
||||
}
|
||||
}
|
||||
|
||||
const uuid = this.sessionManager.getSureUser().uuid
|
||||
const response = await this.userApiService.deleteAccount(uuid)
|
||||
const uuid = this.sessions.getSureUser().uuid
|
||||
const response = await this.userApi.deleteAccount(uuid)
|
||||
if (isErrorResponse(response)) {
|
||||
return {
|
||||
error: true,
|
||||
@@ -241,7 +244,7 @@ export class UserService
|
||||
|
||||
await this.signOut(true)
|
||||
|
||||
void this.alertService.alert(InfoStrings.AccountDeleted)
|
||||
void this.alerts.alert(InfoStrings.AccountDeleted)
|
||||
|
||||
return {
|
||||
error: false,
|
||||
@@ -249,9 +252,9 @@ export class UserService
|
||||
}
|
||||
|
||||
async submitUserRequest(requestType: UserRequestType): Promise<boolean> {
|
||||
const userUuid = this.sessionManager.getSureUser().uuid
|
||||
const userUuid = this.sessions.getSureUser().uuid
|
||||
try {
|
||||
const result = await this.userApiService.submitUserRequest({
|
||||
const result = await this.userApi.submitUserRequest({
|
||||
userUuid,
|
||||
requestType,
|
||||
})
|
||||
@@ -274,11 +277,7 @@ export class UserService
|
||||
public async correctiveSignIn(rootKey: SNRootKey): Promise<HttpResponse<SignInResponse>> {
|
||||
this.lockSyncing()
|
||||
|
||||
const response = await this.sessionManager.bypassChecksAndSignInWithRootKey(
|
||||
rootKey.keyParams.identifier,
|
||||
rootKey,
|
||||
false,
|
||||
)
|
||||
const response = await this.sessions.bypassChecksAndSignInWithRootKey(rootKey.keyParams.identifier, rootKey, false)
|
||||
|
||||
if (!isErrorResponse(response)) {
|
||||
await this.notifyEvent(AccountEvent.SignedInOrRegistered, {
|
||||
@@ -313,16 +312,16 @@ export class UserService
|
||||
}): Promise<CredentialsChangeFunctionResponse> {
|
||||
const result = await this.performCredentialsChange(parameters)
|
||||
if (result.error) {
|
||||
void this.alertService.alert(result.error.message)
|
||||
void this.alerts.alert(result.error.message)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public async signOut(force = false, source = DeinitSource.SignOut): Promise<void> {
|
||||
const performSignOut = async () => {
|
||||
await this.sessionManager.signOut()
|
||||
await this.encryptionService.deleteWorkspaceSpecificKeyStateFromDevice()
|
||||
await this.storageService.clearAllData()
|
||||
await this.sessions.signOut()
|
||||
await this.encryption.deleteWorkspaceSpecificKeyStateFromDevice()
|
||||
await this.storage.clearAllData()
|
||||
await this.notifyEvent(AccountEvent.SignedOut, { payload: { source } })
|
||||
}
|
||||
|
||||
@@ -332,10 +331,10 @@ export class UserService
|
||||
return
|
||||
}
|
||||
|
||||
const dirtyItems = this.itemManager.getDirtyItems()
|
||||
const dirtyItems = this.items.getDirtyItems()
|
||||
if (dirtyItems.length > 0) {
|
||||
const singular = dirtyItems.length === 1
|
||||
const didConfirm = await this.alertService.confirm(
|
||||
const didConfirm = await this.alerts.confirm(
|
||||
`There ${singular ? 'is' : 'are'} ${dirtyItems.length} ${
|
||||
singular ? 'item' : 'items'
|
||||
} with unsynced changes. If you sign out, these changes will be lost forever. Are you sure you want to sign out?`,
|
||||
@@ -353,7 +352,7 @@ export class UserService
|
||||
canceled?: true
|
||||
error?: { message: string }
|
||||
}> {
|
||||
if (!this.sessionManager.isUserMissingKeyPair()) {
|
||||
if (!this.sessions.isUserMissingKeyPair()) {
|
||||
throw Error('Cannot update account with first time keypair if user already has a keypair')
|
||||
}
|
||||
|
||||
@@ -367,8 +366,8 @@ export class UserService
|
||||
canceled?: true
|
||||
error?: { message: string }
|
||||
}> {
|
||||
const hasPasscode = this.encryptionService.hasPasscode()
|
||||
const hasAccount = this.encryptionService.hasAccount()
|
||||
const hasPasscode = this.encryption.hasPasscode()
|
||||
const hasAccount = this.encryption.hasAccount()
|
||||
const prompts = []
|
||||
if (hasPasscode) {
|
||||
prompts.push(
|
||||
@@ -389,11 +388,11 @@ export class UserService
|
||||
)
|
||||
}
|
||||
const challenge = new Challenge(prompts, ChallengeReason.ProtocolUpgrade, true)
|
||||
const response = await this.challengeService.promptForChallengeResponse(challenge)
|
||||
const response = await this.challenges.promptForChallengeResponse(challenge)
|
||||
if (!response) {
|
||||
return { canceled: true }
|
||||
}
|
||||
const dismissBlockingDialog = await this.alertService.blockingDialog(
|
||||
const dismissBlockingDialog = await this.alerts.blockingDialog(
|
||||
Messages.DO_NOT_CLOSE_APPLICATION,
|
||||
Messages.UPGRADING_ENCRYPTION,
|
||||
)
|
||||
@@ -436,11 +435,11 @@ export class UserService
|
||||
if (passcode.length < this.MINIMUM_PASSCODE_LENGTH) {
|
||||
return false
|
||||
}
|
||||
if (!(await this.protectionService.authorizeAddingPasscode())) {
|
||||
if (!(await this.protections.authorizeAddingPasscode())) {
|
||||
return false
|
||||
}
|
||||
|
||||
const dismissBlockingDialog = await this.alertService.blockingDialog(
|
||||
const dismissBlockingDialog = await this.alerts.blockingDialog(
|
||||
Messages.DO_NOT_CLOSE_APPLICATION,
|
||||
Messages.SETTING_PASSCODE,
|
||||
)
|
||||
@@ -453,11 +452,11 @@ export class UserService
|
||||
}
|
||||
|
||||
public async removePasscode(): Promise<boolean> {
|
||||
if (!(await this.protectionService.authorizeRemovingPasscode())) {
|
||||
if (!(await this.protections.authorizeRemovingPasscode())) {
|
||||
return false
|
||||
}
|
||||
|
||||
const dismissBlockingDialog = await this.alertService.blockingDialog(
|
||||
const dismissBlockingDialog = await this.alerts.blockingDialog(
|
||||
Messages.DO_NOT_CLOSE_APPLICATION,
|
||||
Messages.REMOVING_PASSCODE,
|
||||
)
|
||||
@@ -479,11 +478,11 @@ export class UserService
|
||||
if (newPasscode.length < this.MINIMUM_PASSCODE_LENGTH) {
|
||||
return false
|
||||
}
|
||||
if (!(await this.protectionService.authorizeChangingPasscode())) {
|
||||
if (!(await this.protections.authorizeChangingPasscode())) {
|
||||
return false
|
||||
}
|
||||
|
||||
const dismissBlockingDialog = await this.alertService.blockingDialog(
|
||||
const dismissBlockingDialog = await this.alerts.blockingDialog(
|
||||
Messages.DO_NOT_CLOSE_APPLICATION,
|
||||
origination === KeyParamsOrigination.ProtocolUpgrade
|
||||
? Messages.ProtocolUpgradeStrings.UpgradingPasscode
|
||||
@@ -499,7 +498,7 @@ export class UserService
|
||||
}
|
||||
|
||||
public async populateSessionFromDemoShareToken(token: Base64String): Promise<void> {
|
||||
await this.sessionManager.populateSessionFromDemoShareToken(token)
|
||||
await this.sessions.populateSessionFromDemoShareToken(token)
|
||||
await this.notifyEvent(AccountEvent.SignedInOrRegistered, {
|
||||
payload: {
|
||||
ephemeral: false,
|
||||
@@ -512,14 +511,14 @@ export class UserService
|
||||
|
||||
private async setPasscodeWithoutWarning(passcode: string, origination: KeyParamsOrigination) {
|
||||
const identifier = UuidGenerator.GenerateUuid()
|
||||
const key = await this.encryptionService.createRootKey(identifier, passcode, origination)
|
||||
await this.encryptionService.setNewRootKeyWrapper(key)
|
||||
const key = await this.encryption.createRootKey(identifier, passcode, origination)
|
||||
await this.encryption.setNewRootKeyWrapper(key)
|
||||
await this.rewriteItemsKeys()
|
||||
await this.sync.sync()
|
||||
}
|
||||
|
||||
private async removePasscodeWithoutWarning() {
|
||||
await this.encryptionService.removePasscode()
|
||||
await this.encryption.removePasscode()
|
||||
await this.rewriteItemsKeys()
|
||||
}
|
||||
|
||||
@@ -532,9 +531,9 @@ export class UserService
|
||||
* https://github.com/standardnotes/desktop/issues/131
|
||||
*/
|
||||
private async rewriteItemsKeys(): Promise<void> {
|
||||
const itemsKeys = this.itemManager.getDisplayableItemsKeys()
|
||||
const itemsKeys = this.items.getDisplayableItemsKeys()
|
||||
const payloads = itemsKeys.map((key) => key.payloadRepresentation())
|
||||
await this.storageService.deletePayloads(payloads)
|
||||
await this.storage.deletePayloads(payloads)
|
||||
await this.sync.persistPayloads(payloads)
|
||||
}
|
||||
|
||||
@@ -547,7 +546,7 @@ export class UserService
|
||||
}
|
||||
|
||||
private clearDatabase(): Promise<void> {
|
||||
return this.storageService.clearAllPayloads()
|
||||
return this.storage.clearAllPayloads()
|
||||
}
|
||||
|
||||
private async performCredentialsChange(parameters: {
|
||||
@@ -558,7 +557,7 @@ export class UserService
|
||||
newPassword?: string
|
||||
passcode?: string
|
||||
}): Promise<CredentialsChangeFunctionResponse> {
|
||||
const { wrappingKey, canceled } = await this.challengeService.getWrappingKeyIfApplicable(parameters.passcode)
|
||||
const { wrappingKey, canceled } = await this.challenges.getWrappingKeyIfApplicable(parameters.passcode)
|
||||
|
||||
if (canceled) {
|
||||
return { error: Error(Messages.CredentialsChangeStrings.PasscodeRequired) }
|
||||
@@ -572,14 +571,14 @@ export class UserService
|
||||
}
|
||||
}
|
||||
|
||||
const accountPasswordValidation = await this.encryptionService.validateAccountPassword(parameters.currentPassword)
|
||||
const accountPasswordValidation = await this.encryption.validateAccountPassword(parameters.currentPassword)
|
||||
if (!accountPasswordValidation.valid) {
|
||||
return {
|
||||
error: Error(Messages.INVALID_PASSWORD),
|
||||
}
|
||||
}
|
||||
|
||||
const user = this.sessionManager.getUser() as User
|
||||
const user = this.sessions.getUser() as User
|
||||
const currentEmail = user.email
|
||||
const { currentRootKey, newRootKey } = await this.recomputeRootKeysForCredentialChange({
|
||||
currentPassword: parameters.currentPassword,
|
||||
@@ -591,7 +590,7 @@ export class UserService
|
||||
|
||||
this.lockSyncing()
|
||||
|
||||
const { response } = await this.sessionManager.changeCredentials({
|
||||
const { response } = await this.sessions.changeCredentials({
|
||||
currentServerPassword: currentRootKey.serverPassword as string,
|
||||
newRootKey: newRootKey,
|
||||
wrappingKey,
|
||||
@@ -604,20 +603,20 @@ export class UserService
|
||||
return { error: Error(response.data.error?.message) }
|
||||
}
|
||||
|
||||
const rollback = await this.encryptionService.createNewItemsKeyWithRollback()
|
||||
await this.encryptionService.reencryptApplicableItemsAfterUserRootKeyChange()
|
||||
const rollback = await this.encryption.createNewItemsKeyWithRollback()
|
||||
await this._reencryptTypeAItems.execute()
|
||||
await this.sync.sync({ awaitAll: true })
|
||||
|
||||
const defaultItemsKey = this.encryptionService.getSureDefaultItemsKey()
|
||||
const defaultItemsKey = this.encryption.getSureDefaultItemsKey()
|
||||
const itemsKeyWasSynced = !defaultItemsKey.neverSynced
|
||||
|
||||
if (!itemsKeyWasSynced) {
|
||||
await this.sessionManager.changeCredentials({
|
||||
await this.sessions.changeCredentials({
|
||||
currentServerPassword: newRootKey.serverPassword as string,
|
||||
newRootKey: currentRootKey,
|
||||
wrappingKey,
|
||||
})
|
||||
await this.encryptionService.reencryptApplicableItemsAfterUserRootKeyChange()
|
||||
await this._reencryptTypeAItems.execute()
|
||||
await rollback()
|
||||
await this.sync.sync({ awaitAll: true })
|
||||
|
||||
@@ -634,11 +633,11 @@ export class UserService
|
||||
newEmail?: string
|
||||
newPassword?: string
|
||||
}): Promise<{ currentRootKey: SNRootKey; newRootKey: SNRootKey }> {
|
||||
const currentRootKey = await this.encryptionService.computeRootKey(
|
||||
const currentRootKey = await this.encryption.computeRootKey(
|
||||
parameters.currentPassword,
|
||||
(await this.encryptionService.getRootKeyParams()) as SNRootKeyParams,
|
||||
this.encryption.getRootKeyParams() as SNRootKeyParams,
|
||||
)
|
||||
const newRootKey = await this.encryptionService.createRootKey(
|
||||
const newRootKey = await this.encryption.createRootKey(
|
||||
parameters.newEmail ?? parameters.currentEmail,
|
||||
parameters.newPassword ?? parameters.currentPassword,
|
||||
parameters.origination,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { InternalEventHandlerInterface } from '../Internal/InternalEventHandlerI
|
||||
import { InternalEventInterface } from '../Internal/InternalEventInterface'
|
||||
import { AbstractService } from '../Service/AbstractService'
|
||||
import { UserEventServiceEventPayload, UserEventServiceEvent } from './UserEventServiceEvent'
|
||||
import { NotificationPayload } from '@standardnotes/domain-core'
|
||||
|
||||
export class UserEventService
|
||||
extends AbstractService<UserEventServiceEvent, UserEventServiceEventPayload>
|
||||
@@ -28,9 +29,13 @@ export class UserEventService
|
||||
}
|
||||
|
||||
for (const serverEvent of userEvents) {
|
||||
const serviceEvent: UserEventServiceEventPayload = {
|
||||
eventPayload: JSON.parse(serverEvent.event_payload),
|
||||
const eventPayloadOrError = NotificationPayload.createFromString(serverEvent.payload)
|
||||
if (eventPayloadOrError.isFailed()) {
|
||||
continue
|
||||
}
|
||||
const eventPayload = eventPayloadOrError.getValue()
|
||||
|
||||
const serviceEvent: UserEventServiceEventPayload = { eventPayload }
|
||||
|
||||
await this.notifyEventSync(UserEventServiceEvent.UserEventReceived, serviceEvent)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { UserEventPayload } from '@standardnotes/responses'
|
||||
import { NotificationPayload } from '@standardnotes/domain-core'
|
||||
|
||||
export enum UserEventServiceEvent {
|
||||
UserEventReceived = 'UserEventReceived',
|
||||
}
|
||||
|
||||
export type UserEventServiceEventPayload = {
|
||||
eventPayload: UserEventPayload
|
||||
eventPayload: NotificationPayload
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SharedVaultInviteServerHash, SharedVaultPermission } from '@standardnotes/responses'
|
||||
import { SharedVaultInviteServerHash } from '@standardnotes/responses'
|
||||
import {
|
||||
TrustedContactInterface,
|
||||
SharedVaultListingInterface,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { SendVaultInvite } from './SendVaultInvite'
|
||||
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
|
||||
import { EncryptMessage } from '../../Encryption/UseCase/Asymmetric/EncryptMessage'
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { Result, SharedVaultUserPermission, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { ShareContactWithVault } from '../../SharedVaults/UseCase/ShareContactWithVault'
|
||||
import { KeySystemKeyManagerInterface } from '../../KeySystem/KeySystemKeyManagerInterface'
|
||||
|
||||
@@ -29,7 +29,7 @@ export class InviteToVault implements UseCaseInterface<SharedVaultInviteServerHa
|
||||
sharedVault: SharedVaultListingInterface
|
||||
sharedVaultContacts: TrustedContactInterface[]
|
||||
recipient: TrustedContactInterface
|
||||
permissions: SharedVaultPermission
|
||||
permission: string
|
||||
}): Promise<Result<SharedVaultInviteServerHash>> {
|
||||
const createInviteResult = await this.inviteContact(params)
|
||||
|
||||
@@ -74,8 +74,14 @@ export class InviteToVault implements UseCaseInterface<SharedVaultInviteServerHa
|
||||
sharedVault: SharedVaultListingInterface
|
||||
sharedVaultContacts: TrustedContactInterface[]
|
||||
recipient: TrustedContactInterface
|
||||
permissions: SharedVaultPermission
|
||||
permission: string
|
||||
}): Promise<Result<SharedVaultInviteServerHash>> {
|
||||
const permissionOrError = SharedVaultUserPermission.create(params.permission)
|
||||
if (permissionOrError.isFailed()) {
|
||||
return Result.fail(permissionOrError.getError())
|
||||
}
|
||||
const permission = permissionOrError.getValue()
|
||||
|
||||
const keySystemRootKey = this.keyManager.getPrimaryKeySystemRootKey(params.sharedVault.systemIdentifier)
|
||||
if (!keySystemRootKey) {
|
||||
return Result.fail('Cannot invite contact; key system root key not found')
|
||||
@@ -127,7 +133,7 @@ export class InviteToVault implements UseCaseInterface<SharedVaultInviteServerHa
|
||||
sharedVaultUuid: params.sharedVault.sharing.sharedVaultUuid,
|
||||
recipientUuid: params.recipient.contactUuid,
|
||||
encryptedMessage: encryptedMessage.getValue(),
|
||||
permissions: params.permissions,
|
||||
permission: permission.value,
|
||||
})
|
||||
|
||||
return createInviteResult
|
||||
|
||||
@@ -49,7 +49,7 @@ export class ReuploadInvite implements UseCaseInterface<void> {
|
||||
sharedVaultUuid: params.previousInvite.shared_vault_uuid,
|
||||
recipientUuid: params.recipient.contactUuid,
|
||||
encryptedMessage: encryptedMessage.getValue(),
|
||||
permissions: params.previousInvite.permissions,
|
||||
permission: params.previousInvite.permission,
|
||||
})
|
||||
|
||||
return createInviteResult
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import {
|
||||
SharedVaultInviteServerHash,
|
||||
isErrorResponse,
|
||||
SharedVaultPermission,
|
||||
getErrorFromErrorResponse,
|
||||
} from '@standardnotes/responses'
|
||||
import { SharedVaultInviteServerHash, isErrorResponse, getErrorFromErrorResponse } from '@standardnotes/responses'
|
||||
import { SharedVaultInvitesServerInterface } from '@standardnotes/api'
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { Result, SharedVaultUserPermission, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class SendVaultInvite implements UseCaseInterface<SharedVaultInviteServerHash> {
|
||||
constructor(private vaultInvitesServer: SharedVaultInvitesServerInterface) {}
|
||||
@@ -14,13 +9,19 @@ export class SendVaultInvite implements UseCaseInterface<SharedVaultInviteServer
|
||||
sharedVaultUuid: string
|
||||
recipientUuid: string
|
||||
encryptedMessage: string
|
||||
permissions: SharedVaultPermission
|
||||
permission: string
|
||||
}): Promise<Result<SharedVaultInviteServerHash>> {
|
||||
const permissionOrError = SharedVaultUserPermission.create(params.permission)
|
||||
if (permissionOrError.isFailed()) {
|
||||
return Result.fail(permissionOrError.getError())
|
||||
}
|
||||
const permission = permissionOrError.getValue()
|
||||
|
||||
const response = await this.vaultInvitesServer.createInvite({
|
||||
sharedVaultUuid: params.sharedVaultUuid,
|
||||
recipientUuid: params.recipientUuid,
|
||||
encryptedMessage: params.encryptedMessage,
|
||||
permissions: params.permissions,
|
||||
permission: permission,
|
||||
})
|
||||
|
||||
if (isErrorResponse(response)) {
|
||||
|
||||
@@ -27,7 +27,6 @@ import { VaultInviteServiceInterface } from './VaultInviteServiceInterface'
|
||||
import {
|
||||
ClientDisplayableError,
|
||||
SharedVaultInviteServerHash,
|
||||
SharedVaultPermission,
|
||||
SharedVaultUserServerHash,
|
||||
isErrorResponse,
|
||||
} from '@standardnotes/responses'
|
||||
@@ -173,7 +172,7 @@ export class VaultInviteService
|
||||
public async inviteContactToSharedVault(
|
||||
sharedVault: SharedVaultListingInterface,
|
||||
contact: TrustedContactInterface,
|
||||
permissions: SharedVaultPermission,
|
||||
permission: string,
|
||||
): Promise<Result<SharedVaultInviteServerHash>> {
|
||||
const contactsResult = await this._getVaultContacts.execute({
|
||||
sharedVaultUuid: sharedVault.sharing.sharedVaultUuid,
|
||||
@@ -194,7 +193,7 @@ export class VaultInviteService
|
||||
sharedVault,
|
||||
recipient: contact,
|
||||
sharedVaultContacts: contacts,
|
||||
permissions,
|
||||
permission,
|
||||
})
|
||||
|
||||
void this.notifyEvent(VaultInviteServiceEvent.InviteSent)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { InviteRecord } from './InviteRecord'
|
||||
import { ApplicationServiceInterface } from '../Service/ApplicationServiceInterface'
|
||||
import { SharedVaultListingInterface, TrustedContactInterface } from '@standardnotes/models'
|
||||
import { ClientDisplayableError, SharedVaultInviteServerHash, SharedVaultPermission } from '@standardnotes/responses'
|
||||
import { ClientDisplayableError, SharedVaultInviteServerHash } from '@standardnotes/responses'
|
||||
import { VaultInviteServiceEvent } from './VaultInviteServiceEvent'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface VaultInviteServiceInterface extends ApplicationServiceInterface
|
||||
inviteContactToSharedVault(
|
||||
sharedVault: SharedVaultListingInterface,
|
||||
contact: TrustedContactInterface,
|
||||
permissions: SharedVaultPermission,
|
||||
permission: string,
|
||||
): Promise<Result<SharedVaultInviteServerHash>>
|
||||
getCachedPendingInviteRecords(): InviteRecord[]
|
||||
deleteInvite(invite: SharedVaultInviteServerHash): Promise<ClientDisplayableError | void>
|
||||
|
||||
@@ -75,6 +75,7 @@ export * from './Encryption/UseCase/TypeA/DecryptPayload'
|
||||
export * from './Encryption/UseCase/TypeA/DecryptPayloadWithKeyLookup'
|
||||
export * from './Encryption/UseCase/TypeA/EncryptPayload'
|
||||
export * from './Encryption/UseCase/TypeA/EncryptPayloadWithKeyLookup'
|
||||
export * from './Encryption/UseCase/TypeA/ReencryptTypeAItems'
|
||||
export * from './Event/ApplicationEvent'
|
||||
export * from './Event/ApplicationEventCallback'
|
||||
export * from './Event/ApplicationStageChangedEventPayload'
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.202.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.202.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.202.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
## [2.202.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -119,6 +119,7 @@ import {
|
||||
DeleteContact,
|
||||
VaultLockService,
|
||||
RemoveItemsFromMemory,
|
||||
ReencryptTypeAItems,
|
||||
} from '@standardnotes/services'
|
||||
import { ItemManager } from '../../Services/Items/ItemManager'
|
||||
import { PayloadManager } from '../../Services/Payloads/PayloadManager'
|
||||
@@ -202,6 +203,10 @@ export class Dependencies {
|
||||
}
|
||||
|
||||
private registerUseCaseMakers() {
|
||||
this.factory.set(TYPES.ReencryptTypeAItems, () => {
|
||||
return new ReencryptTypeAItems(this.get(TYPES.ItemManager), this.get(TYPES.MutatorService))
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.ImportDataUseCase, () => {
|
||||
return new ImportDataUseCase(
|
||||
this.get(TYPES.ItemManager),
|
||||
@@ -616,10 +621,9 @@ export class Dependencies {
|
||||
return new RootKeyManager(
|
||||
this.get(TYPES.DeviceInterface),
|
||||
this.get(TYPES.DiskStorageService),
|
||||
this.get(TYPES.ItemManager),
|
||||
this.get(TYPES.MutatorService),
|
||||
this.get(TYPES.EncryptionOperators),
|
||||
this.options.identifier,
|
||||
this.get(TYPES.ReencryptTypeAItems),
|
||||
this.get(TYPES.InternalEventBus),
|
||||
)
|
||||
})
|
||||
@@ -1086,6 +1090,7 @@ export class Dependencies {
|
||||
this.get(TYPES.ChallengeService),
|
||||
this.get(TYPES.ProtectionService),
|
||||
this.get(TYPES.UserApiService),
|
||||
this.get(TYPES.ReencryptTypeAItems),
|
||||
this.get(TYPES.InternalEventBus),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -151,6 +151,7 @@ export const TYPES = {
|
||||
DecryptBackupFile: Symbol.for('DecryptBackupFile'),
|
||||
IsVaultOwner: Symbol.for('IsVaultOwner'),
|
||||
RemoveItemsFromMemory: Symbol.for('RemoveItemsFromMemory'),
|
||||
ReencryptTypeAItems: Symbol.for('ReencryptTypeAItems'),
|
||||
|
||||
// Mappers
|
||||
SessionStorageMapper: Symbol.for('SessionStorageMapper'),
|
||||
|
||||
@@ -14,7 +14,6 @@ export class Migration2_202_1 extends Migration {
|
||||
this.registerStageHandler(ApplicationStage.FullSyncCompleted_13, async () => {
|
||||
await this.migrateComponentDataToUserPreferences()
|
||||
await this.migrateActiveComponentsToUserPreferences()
|
||||
await this.deleteComponentsWhichAreNativeFeatures()
|
||||
|
||||
this.markDone()
|
||||
})
|
||||
@@ -70,29 +69,4 @@ export class Migration2_202_1 extends Migration {
|
||||
await this.services.preferences.setValueDetached(PrefKey.ActiveThemes, Uuids(activeThemes))
|
||||
await this.services.preferences.setValueDetached(PrefKey.ActiveComponents, Uuids(activeComponents))
|
||||
}
|
||||
|
||||
private async deleteComponentsWhichAreNativeFeatures(): Promise<void> {
|
||||
const componentsToDelete = [
|
||||
...this.services.itemManager.getItems<ComponentInterface>(ContentType.TYPES.Component),
|
||||
...this.services.itemManager.getItems<ComponentInterface>(ContentType.TYPES.Theme),
|
||||
].filter((candidate) => {
|
||||
const nativeFeature = FindNativeFeature(candidate.identifier)
|
||||
if (!nativeFeature) {
|
||||
return false
|
||||
}
|
||||
|
||||
const isDeprecatedAndThusShouldNotDeleteComponentSinceUserHasItRetained = nativeFeature.deprecated
|
||||
if (isDeprecatedAndThusShouldNotDeleteComponentSinceUserHasItRetained) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if (componentsToDelete.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
await this.services.mutator.setItemsToBeDeleted(componentsToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,13 +286,11 @@ export class PayloadManager extends AbstractService implements PayloadManagerInt
|
||||
/**
|
||||
* Imports an array of payloads from an external source (such as a backup file)
|
||||
* and marks the items as dirty.
|
||||
* @returns Resulting items
|
||||
*/
|
||||
public async importPayloads(payloads: DecryptedPayloadInterface[], historyMap: HistoryMap): Promise<string[]> {
|
||||
public async importPayloads(payloads: FullyFormedPayloadInterface[], historyMap: HistoryMap): Promise<string[]> {
|
||||
const sourcedPayloads = payloads.map((p) => p.copy(undefined, PayloadSource.FileImport))
|
||||
|
||||
const delta = new DeltaFileImport(this.getMasterCollection(), sourcedPayloads, historyMap)
|
||||
|
||||
const emit = delta.result()
|
||||
|
||||
await this.emitDeltaEmit(emit)
|
||||
|
||||
@@ -58,9 +58,9 @@ export class ServerSyncResponse {
|
||||
|
||||
this.vaultInvites = this.successResponseData?.shared_vault_invites || []
|
||||
|
||||
this.asymmetricMessages = this.successResponseData?.asymmetric_messages || []
|
||||
this.asymmetricMessages = this.successResponseData?.messages || []
|
||||
|
||||
this.userEvents = this.successResponseData?.user_events || []
|
||||
this.userEvents = this.successResponseData?.notifications || []
|
||||
|
||||
deepFreeze(this)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export const VaultTests = {
|
||||
'vaults/pkc.test.js',
|
||||
'vaults/contacts.test.js',
|
||||
'vaults/crypto.test.js',
|
||||
'vaults/importing.test.js',
|
||||
'vaults/asymmetric-messages.test.js',
|
||||
'vaults/keypair-change.test.js',
|
||||
'vaults/signatures.test.js',
|
||||
@@ -16,7 +17,7 @@ export const VaultTests = {
|
||||
'vaults/conflicts.test.js',
|
||||
'vaults/deletion.test.js',
|
||||
'vaults/permissions.test.js',
|
||||
'vaults/key_rotation.test.js',
|
||||
'vaults/key-rotation.test.js',
|
||||
'vaults/files.test.js',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -755,7 +755,7 @@ describe('keys', function () {
|
||||
currentServerPassword: currentRootKey.serverPassword,
|
||||
newRootKey,
|
||||
})
|
||||
await this.application.encryption.reencryptApplicableItemsAfterUserRootKeyChange()
|
||||
await this.application.dependencies.get(TYPES.ReencryptTypeAItems).execute()
|
||||
/** Note: this may result in a deadlock if features_service syncs and results in an error */
|
||||
await this.application.sync.sync({ awaitAll: true })
|
||||
|
||||
|
||||
@@ -369,6 +369,17 @@ export class AppContext {
|
||||
})
|
||||
}
|
||||
|
||||
spyOnFunctionResult(object, functionName) {
|
||||
return new Promise((resolve) => {
|
||||
sinon.stub(object, functionName).callsFake(async (params) => {
|
||||
object[functionName].restore()
|
||||
const result = await object[functionName](params)
|
||||
resolve(result)
|
||||
return result
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
resolveWhenAsymmetricMessageProcessingCompletes() {
|
||||
return this.resolveWhenAsyncFunctionCompletes(this.asymmetric, 'handleRemoteReceivedAsymmetricMessages')
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ export const acceptAllInvites = async (context) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const createSharedVaultWithAcceptedInvite = async (context, permissions = SharedVaultPermission.Write) => {
|
||||
export const createSharedVaultWithAcceptedInvite = async (context, permission = SharedVaultUserPermission.PERMISSIONS.Write) => {
|
||||
const { sharedVault, contact, contactContext, deinitContactContext } =
|
||||
await createSharedVaultWithUnacceptedButTrustedInvite(context, permissions)
|
||||
await createSharedVaultWithUnacceptedButTrustedInvite(context, permission)
|
||||
|
||||
const promise = contactContext.awaitNextSyncSharedVaultFromScratchEvent()
|
||||
|
||||
@@ -53,11 +53,11 @@ export const createSharedVaultWithAcceptedInvite = async (context, permissions =
|
||||
|
||||
export const createSharedVaultWithAcceptedInviteAndNote = async (
|
||||
context,
|
||||
permissions = SharedVaultPermission.Write,
|
||||
permission = SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
) => {
|
||||
const { sharedVault, contactContext, contact, deinitContactContext } = await createSharedVaultWithAcceptedInvite(
|
||||
context,
|
||||
permissions,
|
||||
permission,
|
||||
)
|
||||
const note = await context.createSyncedNote('foo', 'bar')
|
||||
const updatedNote = await moveItemToVault(context, sharedVault, note)
|
||||
@@ -68,7 +68,7 @@ export const createSharedVaultWithAcceptedInviteAndNote = async (
|
||||
|
||||
export const createSharedVaultWithUnacceptedButTrustedInvite = async (
|
||||
context,
|
||||
permissions = SharedVaultPermission.Write,
|
||||
permission = SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
) => {
|
||||
const sharedVault = await createSharedVault(context)
|
||||
|
||||
@@ -76,7 +76,12 @@ export const createSharedVaultWithUnacceptedButTrustedInvite = async (
|
||||
const contact = await createTrustedContactForUserOfContext(context, contactContext)
|
||||
await createTrustedContactForUserOfContext(contactContext, context)
|
||||
|
||||
const invite = (await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, permissions)).getValue()
|
||||
const inviteOrError = await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, permission)
|
||||
if (inviteOrError.isFailed()) {
|
||||
throw new Error(inviteOrError.getError())
|
||||
}
|
||||
const invite = inviteOrError.getValue()
|
||||
|
||||
await contactContext.sync()
|
||||
|
||||
return { sharedVault, contact, contactContext, deinitContactContext, invite }
|
||||
@@ -86,11 +91,11 @@ export const createSharedVaultAndInviteContact = async (
|
||||
createInContext,
|
||||
inviteContext,
|
||||
inviteContact,
|
||||
permissions = SharedVaultPermission.Write,
|
||||
permission = SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
) => {
|
||||
const sharedVault = await createSharedVault(createInContext)
|
||||
|
||||
await createInContext.vaultInvites.inviteContactToSharedVault(sharedVault, inviteContact, permissions)
|
||||
await createInContext.vaultInvites.inviteContactToSharedVault(sharedVault, inviteContact, permission)
|
||||
|
||||
const promise = inviteContext.awaitNextSyncSharedVaultFromScratchEvent()
|
||||
|
||||
@@ -105,26 +110,26 @@ export const createSharedVaultAndInviteContact = async (
|
||||
|
||||
export const createSharedVaultWithUnacceptedAndUntrustedInvite = async (
|
||||
context,
|
||||
permissions = SharedVaultPermission.Write,
|
||||
permission = SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
) => {
|
||||
const sharedVault = await createSharedVault(context)
|
||||
|
||||
const { contactContext, deinitContactContext } = await createContactContext()
|
||||
const contact = await createTrustedContactForUserOfContext(context, contactContext)
|
||||
|
||||
const invite = (await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, permissions)).getValue()
|
||||
const invite = (await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, permission)).getValue()
|
||||
await contactContext.sync()
|
||||
|
||||
return { sharedVault, contact, contactContext, deinitContactContext, invite }
|
||||
}
|
||||
|
||||
export const inviteNewPartyToSharedVault = async (context, sharedVault, permissions = SharedVaultPermission.Write) => {
|
||||
export const inviteNewPartyToSharedVault = async (context, sharedVault, permission = SharedVaultUserPermission.PERMISSIONS.Write) => {
|
||||
const { contactContext: thirdPartyContext, deinitContactContext: deinitThirdPartyContext } =
|
||||
await createContactContext()
|
||||
|
||||
const thirdPartyContact = await createTrustedContactForUserOfContext(context, thirdPartyContext)
|
||||
await createTrustedContactForUserOfContext(thirdPartyContext, context)
|
||||
await context.vaultInvites.inviteContactToSharedVault(sharedVault, thirdPartyContact, permissions)
|
||||
await context.vaultInvites.inviteContactToSharedVault(sharedVault, thirdPartyContact, permission)
|
||||
|
||||
await thirdPartyContext.sync()
|
||||
|
||||
|
||||
@@ -121,71 +121,4 @@ describe('migrations', () => {
|
||||
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
|
||||
describe('2.202.1', () => {
|
||||
let application
|
||||
|
||||
beforeEach(async () => {
|
||||
application = await Factory.createAppWithRandNamespace()
|
||||
|
||||
await application.prepareForLaunch({
|
||||
receiveChallenge: () => {},
|
||||
})
|
||||
await application.launch(true)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await Factory.safeDeinit(application)
|
||||
})
|
||||
|
||||
it('remove components that are available as native features', async function () {
|
||||
const editor = CreateDecryptedItemFromPayload(
|
||||
new DecryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Component,
|
||||
content: FillItemContent({
|
||||
package_info: {
|
||||
identifier: NativeFeatureIdentifier.TYPES.MarkdownProEditor,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
await application.mutator.insertItem(editor)
|
||||
await application.sync.sync()
|
||||
|
||||
expect(application.items.getItems(ContentType.TYPES.Component).length).to.equal(1)
|
||||
|
||||
/** Run migration */
|
||||
const migration = new Migration2_202_1(application.migrations.services)
|
||||
await migration.handleStage(ApplicationStage.FullSyncCompleted_13)
|
||||
await application.sync.sync()
|
||||
|
||||
expect(application.items.getItems(ContentType.TYPES.Component).length).to.equal(0)
|
||||
})
|
||||
|
||||
it('do not remove components that are available as native features but deprecated', async function () {
|
||||
const editor = CreateDecryptedItemFromPayload(
|
||||
new DecryptedPayload({
|
||||
uuid: '123',
|
||||
content_type: ContentType.TYPES.Component,
|
||||
content: FillItemContent({
|
||||
package_info: {
|
||||
identifier: NativeFeatureIdentifier.TYPES.DeprecatedBoldEditor,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
await application.mutator.insertItem(editor)
|
||||
await application.sync.sync()
|
||||
|
||||
expect(application.items.getItems(ContentType.TYPES.Component).length).to.equal(1)
|
||||
|
||||
/** Run migration */
|
||||
const migration = new Migration2_202_1(application.migrations.services)
|
||||
await migration.handleStage(ApplicationStage.FullSyncCompleted_13)
|
||||
await application.sync.sync()
|
||||
|
||||
expect(application.items.getItems(ContentType.TYPES.Component).length).to.equal(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -882,8 +882,4 @@ describe('importing', function () {
|
||||
expect(application.items.referencesForItem(importedTag).length).to.equal(1)
|
||||
expect(application.items.itemsReferencingItem(importedNote).length).to.equal(1)
|
||||
})
|
||||
|
||||
it('should decrypt backup file which contains a vaulted note without a synced key system root key', async () => {
|
||||
console.error('TODO: Implement this test')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -58,10 +58,15 @@
|
||||
|
||||
if (MainRegistry.VaultTests.enabled) {
|
||||
InternalFeatureService.get().enableFeature(InternalFeature.Vaults);
|
||||
await loadTests(MainRegistry.VaultTests.files);
|
||||
}
|
||||
|
||||
if (!MainRegistry.VaultTests.enabled || !MainRegistry.VaultTests.enabled.exclusive) {
|
||||
if (MainRegistry.VaultTests.exclusive) {
|
||||
await loadTests(MainRegistry.VaultTests.files);
|
||||
} else {
|
||||
await loadTests([
|
||||
...MainRegistry.BaseTests,
|
||||
...MainRegistry.VaultTests.files
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
await loadTests(MainRegistry.BaseTests);
|
||||
}
|
||||
|
||||
@@ -73,4 +78,4 @@
|
||||
<div id="mocha"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -8,7 +8,6 @@ describe('asymmetric messages', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let context
|
||||
let service
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
@@ -22,8 +21,6 @@ describe('asymmetric messages', function () {
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
|
||||
service = context.asymmetric
|
||||
})
|
||||
|
||||
it('should not trust message if the trusted payload data recipientUuid does not match the message user uuid', async () => {
|
||||
@@ -321,7 +318,7 @@ describe('asymmetric messages', function () {
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('should process sender keypair changed message', async () => {
|
||||
it.skip('should process sender keypair changed message', async () => {
|
||||
const { contactContext, deinitContactContext } = await Collaboration.createContactContext()
|
||||
await Collaboration.createTrustedContactForUserOfContext(context, contactContext)
|
||||
await Collaboration.createTrustedContactForUserOfContext(contactContext, context)
|
||||
@@ -344,7 +341,7 @@ describe('asymmetric messages', function () {
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('sender keypair changed message should be signed using old key pair', async () => {
|
||||
it.skip('sender keypair changed message should be signed using old key pair', async () => {
|
||||
const { contactContext, deinitContactContext } = await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
|
||||
const oldKeyPair = context.encryption.getKeyPair()
|
||||
|
||||
@@ -81,7 +81,7 @@ describe('shared vault conflicts', function () {
|
||||
|
||||
it('attempting to modify note as read user should result in SharedVaultInsufficientPermissionsError', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context, SharedVaultPermission.Read)
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context, SharedVaultUserPermission.PERMISSIONS.Read)
|
||||
|
||||
const promise = contactContext.resolveWithConflicts()
|
||||
await contactContext.changeNoteTitleAndSync(note, 'new title')
|
||||
@@ -123,8 +123,9 @@ describe('shared vault conflicts', function () {
|
||||
sinon.stub(objectToSpy, 'payloadsByPreparingForServer').callsFake(async (params) => {
|
||||
objectToSpy.payloadsByPreparingForServer.restore()
|
||||
const payloads = await objectToSpy.payloadsByPreparingForServer(params)
|
||||
const nonExistentSharedVaultUuid = '00000000-0000-0000-0000-000000000000'
|
||||
for (const payload of payloads) {
|
||||
payload.shared_vault_uuid = 'non-existent-vault-uuid-123'
|
||||
payload.shared_vault_uuid = nonExistentSharedVaultUuid
|
||||
}
|
||||
|
||||
return payloads
|
||||
|
||||
@@ -101,7 +101,6 @@ describe('contacts', function () {
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('should be able to refresh a contact using a collaborationID that includes full chain of previouos public keys', async () => {
|
||||
console.error('TODO: implement test')
|
||||
it.skip('should be able to refresh a contact using a collaborationID that includes full chain of previous public keys', async () => {
|
||||
})
|
||||
})
|
||||
|
||||
@@ -35,12 +35,25 @@ describe('shared vault crypto', function () {
|
||||
expect(recreatedContext.encryption.getSigningKeyPair()).to.not.be.undefined
|
||||
})
|
||||
|
||||
it('changing user password should re-encrypt all key system root keys', async () => {
|
||||
console.error('TODO: implement')
|
||||
})
|
||||
it('changing user password should re-encrypt all key system root keys and contacts with new user root key', async () => {
|
||||
await Collaboration.createPrivateVault(context)
|
||||
const spy = context.spyOnFunctionResult(context.application.sync, 'payloadsByPreparingForServer')
|
||||
await context.changePassword('new_password')
|
||||
|
||||
it('changing user password should re-encrypt all trusted contacts', async () => {
|
||||
console.error('TODO: implement')
|
||||
const payloads = await spy
|
||||
const keyPayloads = payloads.filter(
|
||||
(payload) =>
|
||||
payload.content_type === ContentType.TYPES.KeySystemRootKey ||
|
||||
payload.content_type === ContentType.TYPES.TrustedContact,
|
||||
)
|
||||
expect(keyPayloads.length).to.equal(2)
|
||||
|
||||
for (const payload of payloads) {
|
||||
const keyParams = context.encryption.getEmbeddedPayloadAuthenticatedData(new EncryptedPayload(payload)).kp
|
||||
|
||||
const userKeyParams = context.encryption.getRootKeyParams().content
|
||||
expect(keyParams).to.eql(userKeyParams)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -77,7 +90,7 @@ describe('shared vault crypto', function () {
|
||||
await deinitContactContext()
|
||||
})
|
||||
|
||||
it('encrypting an item into storage then loading it should verify authenticity of original content rather than most recent symmetric signature', async () => {
|
||||
it.skip('encrypting an item into storage then loading it should verify authenticity of original content rather than most recent symmetric signature', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('shared vault deletion', function () {
|
||||
|
||||
it('leaving a shared vault should remove its items locally', async () => {
|
||||
const { sharedVault, note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context, SharedVaultPermission.Admin)
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context, SharedVaultUserPermission.PERMISSIONS.Admin)
|
||||
|
||||
const originalNote = contactContext.items.findItem(note.uuid)
|
||||
expect(originalNote).to.not.be.undefined
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as Collaboration from '../lib/Collaboration.js'
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('shared vault files', function () {
|
||||
describe.skip('shared vault files', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let context
|
||||
@@ -179,7 +179,7 @@ describe('shared vault files', function () {
|
||||
|
||||
it('should be able to delete vault file as write user', async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context, SharedVaultPermission.Write)
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context, SharedVaultUserPermission.PERMISSIONS.Write)
|
||||
const response = await fetch('/mocha/assets/small_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
|
||||
@@ -201,7 +201,7 @@ describe('shared vault files', function () {
|
||||
context.anticipateConsoleError('Could not create valet token')
|
||||
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context, SharedVaultPermission.Read)
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context, SharedVaultUserPermission.PERMISSIONS.Read)
|
||||
const response = await fetch('/mocha/assets/small_file.md')
|
||||
const buffer = new Uint8Array(await response.arrayBuffer())
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import * as Factory from '../lib/factory.js'
|
||||
import * as Collaboration from '../lib/Collaboration.js'
|
||||
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe.skip('vault importing', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let context
|
||||
|
||||
afterEach(async function () {
|
||||
await context.deinit()
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
beforeEach(async function () {
|
||||
localStorage.clear()
|
||||
|
||||
context = await Factory.createAppContextWithRealCrypto()
|
||||
|
||||
await context.launch()
|
||||
await context.register()
|
||||
})
|
||||
|
||||
it('should import vaulted items with synced root key', async () => {
|
||||
console.error('TODO: implement')
|
||||
})
|
||||
|
||||
it('should import vaulted items with non-present root key', async () => {
|
||||
const vault = await context.vaults.createUserInputtedPasswordVault({
|
||||
name: 'test vault',
|
||||
userInputtedPassword: 'test password',
|
||||
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
||||
})
|
||||
|
||||
const note = await context.createSyncedNote('foo', 'bar')
|
||||
await Collaboration.moveItemToVault(context, vault, note)
|
||||
|
||||
const backupData = await context.application.createEncryptedBackupFileForAutomatedDesktopBackups()
|
||||
|
||||
const otherContext = await Factory.createAppContextWithRealCrypto()
|
||||
await otherContext.launch()
|
||||
|
||||
await otherContext.application.importData(backupData)
|
||||
|
||||
const expectedImportedItems = ['vault-items-key', 'note']
|
||||
const invalidItems = otherContext.items.invalidItems
|
||||
expect(invalidItems.length).to.equal(expectedImportedItems.length)
|
||||
|
||||
const encryptedItem = invalidItems[0]
|
||||
expect(encryptedItem.key_system_identifier).to.equal(vault.systemIdentifier)
|
||||
expect(encryptedItem.errorDecrypting).to.be.true
|
||||
expect(encryptedItem.uuid).to.equal(note.uuid)
|
||||
|
||||
await otherContext.deinit()
|
||||
})
|
||||
})
|
||||
@@ -28,14 +28,14 @@ describe('shared vault invites', function () {
|
||||
const contact = await Collaboration.createTrustedContactForUserOfContext(context, contactContext)
|
||||
|
||||
const vaultInvite = (
|
||||
await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, SharedVaultPermission.Write)
|
||||
await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, SharedVaultUserPermission.PERMISSIONS.Write)
|
||||
).getValue()
|
||||
|
||||
expect(vaultInvite).to.not.be.undefined
|
||||
expect(vaultInvite.shared_vault_uuid).to.equal(sharedVault.sharing.sharedVaultUuid)
|
||||
expect(vaultInvite.user_uuid).to.equal(contact.contactUuid)
|
||||
expect(vaultInvite.encrypted_message).to.not.be.undefined
|
||||
expect(vaultInvite.permissions).to.equal(SharedVaultPermission.Write)
|
||||
expect(vaultInvite.permission).to.equal(SharedVaultUserPermission.PERMISSIONS.Write)
|
||||
expect(vaultInvite.updated_at_timestamp).to.not.be.undefined
|
||||
expect(vaultInvite.created_at_timestamp).to.not.be.undefined
|
||||
|
||||
@@ -100,7 +100,7 @@ describe('shared vault invites', function () {
|
||||
/** Sync the contact context so that they wouldn't naturally receive changes made before this point */
|
||||
await contactContext.sync()
|
||||
|
||||
await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, SharedVaultPermission.Write)
|
||||
await context.vaultInvites.inviteContactToSharedVault(sharedVault, contact, SharedVaultUserPermission.PERMISSIONS.Write)
|
||||
|
||||
/** Contact should now sync and expect to find note */
|
||||
const promise = contactContext.awaitNextSyncSharedVaultFromScratchEvent()
|
||||
@@ -125,7 +125,7 @@ describe('shared vault invites', function () {
|
||||
await context.vaultInvites.inviteContactToSharedVault(
|
||||
sharedVault,
|
||||
currentContextContact,
|
||||
SharedVaultPermission.Write,
|
||||
SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
)
|
||||
|
||||
await contactContext.vaultInvites.downloadInboundInvites()
|
||||
@@ -143,7 +143,7 @@ describe('shared vault invites', function () {
|
||||
await context.vaultInvites.inviteContactToSharedVault(
|
||||
sharedVault,
|
||||
currentContextContact,
|
||||
SharedVaultPermission.Write,
|
||||
SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
)
|
||||
|
||||
await contactContext.vaultInvites.downloadInboundInvites()
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('shared vault items', function () {
|
||||
await context.vaultInvites.inviteContactToSharedVault(
|
||||
sharedVault,
|
||||
currentContextContact,
|
||||
SharedVaultPermission.Write,
|
||||
SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
)
|
||||
await Collaboration.moveItemToVault(context, sharedVault, note)
|
||||
|
||||
|
||||
+53
-4
@@ -4,7 +4,7 @@ import * as Collaboration from '../lib/Collaboration.js'
|
||||
chai.use(chaiAsPromised)
|
||||
const expect = chai.expect
|
||||
|
||||
describe('shared vault key rotation', function () {
|
||||
describe('vault key rotation', function () {
|
||||
this.timeout(Factory.TwentySecondTimeout)
|
||||
|
||||
let context
|
||||
@@ -29,17 +29,66 @@ describe('shared vault key rotation', function () {
|
||||
|
||||
contactContext.lockSyncing()
|
||||
|
||||
const spy = sinon.spy(context.keys, 'queueVaultItemsKeysForReencryption')
|
||||
const callSpy = sinon.spy(context.keys, 'queueVaultItemsKeysForReencryption')
|
||||
const syncSpy = context.spyOnFunctionResult(context.application.sync, 'payloadsByPreparingForServer')
|
||||
|
||||
const promise = context.resolveWhenSharedVaultKeyRotationInvitesGetSent(sharedVault)
|
||||
await context.vaults.rotateVaultRootKey(sharedVault)
|
||||
await promise
|
||||
await syncSpy
|
||||
|
||||
expect(spy.callCount).to.equal(1)
|
||||
expect(callSpy.callCount).to.equal(1)
|
||||
|
||||
const payloads = await syncSpy
|
||||
const keyPayloads = payloads.filter((payload) => payload.content_type === ContentType.TYPES.KeySystemItemsKey)
|
||||
expect(keyPayloads.length).to.equal(2)
|
||||
|
||||
const vaultRootKey = context.keys.getPrimaryKeySystemRootKey(sharedVault.systemIdentifier)
|
||||
|
||||
for (const payload of keyPayloads) {
|
||||
const keyParams = context.encryption.getEmbeddedPayloadAuthenticatedData(new EncryptedPayload(payload)).kp
|
||||
expect(keyParams).to.eql(vaultRootKey.keyParams)
|
||||
}
|
||||
|
||||
deinitContactContext()
|
||||
})
|
||||
|
||||
it('should update value of local storage mode key', async () => {
|
||||
const vault = await context.vaults.createUserInputtedPasswordVault({
|
||||
name: 'test vault',
|
||||
userInputtedPassword: 'test password',
|
||||
storagePreference: KeySystemRootKeyStorageMode.Local,
|
||||
})
|
||||
|
||||
const beforeKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
||||
|
||||
await context.vaults.rotateVaultRootKey(vault, 'test password')
|
||||
|
||||
const afterKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
||||
|
||||
expect(afterKey.keyParams.creationTimestamp).to.be.greaterThan(beforeKey.keyParams.creationTimestamp)
|
||||
expect(afterKey.key).to.not.equal(beforeKey.key)
|
||||
expect(afterKey.itemsKey).to.not.equal(beforeKey.itemsKey)
|
||||
})
|
||||
|
||||
it('should update value of mem storage mode key', async () => {
|
||||
const vault = await context.vaults.createUserInputtedPasswordVault({
|
||||
name: 'test vault',
|
||||
userInputtedPassword: 'test password',
|
||||
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
||||
})
|
||||
|
||||
const beforeKey = context.keys.getMemCachedRootKey(vault.systemIdentifier)
|
||||
|
||||
await context.vaults.rotateVaultRootKey(vault, 'test password')
|
||||
|
||||
const afterKey = context.keys.getMemCachedRootKey(vault.systemIdentifier)
|
||||
|
||||
expect(afterKey.keyParams.creationTimestamp).to.be.greaterThan(beforeKey.keyParams.creationTimestamp)
|
||||
expect(afterKey.key).to.not.equal(beforeKey.key)
|
||||
expect(afterKey.itemsKey).to.not.equal(beforeKey.itemsKey)
|
||||
})
|
||||
|
||||
it("rotating a vault's key should send an asymmetric message to all members", async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
||||
@@ -56,7 +105,7 @@ describe('shared vault key rotation', function () {
|
||||
|
||||
const message = outboundMessages[0]
|
||||
expect(message).to.not.be.undefined
|
||||
expect(message.user_uuid).to.equal(contactContext.userUuid)
|
||||
expect(message.recipient_uuid).to.equal(contactContext.userUuid)
|
||||
expect(message.encrypted_message).to.not.be.undefined
|
||||
|
||||
await deinitContactContext()
|
||||
@@ -23,7 +23,7 @@ describe('keypair change', function () {
|
||||
await context.register()
|
||||
})
|
||||
|
||||
it('contacts should be able to handle receiving multiple keypair changed messages and trust them in order', async () => {
|
||||
it.skip('contacts should be able to handle receiving multiple keypair changed messages and trust them in order', async () => {
|
||||
const { note, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('shared vault permissions', function () {
|
||||
const result = await contactContext.vaultInvites.inviteContactToSharedVault(
|
||||
sharedVault,
|
||||
thirdPartyContact,
|
||||
SharedVaultPermission.Write,
|
||||
SharedVaultUserPermission.PERMISSIONS.Write,
|
||||
)
|
||||
|
||||
expect(result.isFailed()).to.be.true
|
||||
@@ -56,7 +56,7 @@ describe('shared vault permissions', function () {
|
||||
|
||||
it('should be able to leave shared vault as added admin', async () => {
|
||||
const { contactVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context, SharedVaultPermission.Admin)
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context, SharedVaultUserPermission.PERMISSIONS.Admin)
|
||||
|
||||
const result = await contactContext.vaultUsers.leaveSharedVault(contactVault)
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('shared vault permissions', function () {
|
||||
|
||||
it('read user should not be able to make changes to items', async () => {
|
||||
const { sharedVault, contactContext, deinitContactContext } =
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context, SharedVaultPermission.Read)
|
||||
await Collaboration.createSharedVaultWithAcceptedInvite(context, SharedVaultUserPermission.PERMISSIONS.Read)
|
||||
const note = await context.createSyncedNote('foo', 'bar')
|
||||
await Collaboration.moveItemToVault(context, sharedVault, note)
|
||||
await contactContext.sync()
|
||||
|
||||
@@ -104,10 +104,27 @@ describe('shared vaults', function () {
|
||||
})
|
||||
|
||||
it('should convert a vault to a shared vault', async () => {
|
||||
console.error('TODO')
|
||||
})
|
||||
const privateVault = await context.vaults.createRandomizedVault({
|
||||
name: 'My Private Vault',
|
||||
})
|
||||
|
||||
it('should send metadata change message when changing name or description', async () => {
|
||||
console.error('TODO')
|
||||
const note = await context.createSyncedNote('foo', 'bar')
|
||||
await context.vaults.moveItemToVault(privateVault, note)
|
||||
|
||||
const sharedVault = await context.sharedVaults.convertVaultToSharedVault(privateVault)
|
||||
|
||||
const { thirdPartyContext, deinitThirdPartyContext } = await Collaboration.inviteNewPartyToSharedVault(
|
||||
context,
|
||||
sharedVault,
|
||||
)
|
||||
|
||||
await Collaboration.acceptAllInvites(thirdPartyContext)
|
||||
|
||||
const contextNote = thirdPartyContext.items.findItem(note.uuid)
|
||||
expect(contextNote).to.not.be.undefined
|
||||
expect(contextNote.title).to.equal('foo')
|
||||
expect(contextNote.text).to.equal(note.text)
|
||||
|
||||
await deinitThirdPartyContext()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/snjs",
|
||||
"version": "2.202.16",
|
||||
"version": "2.202.19",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -37,7 +37,7 @@
|
||||
"@babel/preset-env": "*",
|
||||
"@standardnotes/api": "workspace:*",
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/domain-core": "^1.24.0",
|
||||
"@standardnotes/domain-events": "^2.108.1",
|
||||
"@standardnotes/encryption": "workspace:*",
|
||||
"@standardnotes/features": "workspace:*",
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.28.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.28.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.28.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-27)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.28.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/ui-services",
|
||||
"version": "1.28.14",
|
||||
"version": "1.28.17",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "^1.50.0",
|
||||
"@standardnotes/domain-core": "^1.22.0",
|
||||
"@standardnotes/domain-core": "^1.24.0",
|
||||
"@standardnotes/features": "workspace:^",
|
||||
"@standardnotes/filepicker": "workspace:^",
|
||||
"@standardnotes/models": "workspace:^",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user