mirror of
https://github.com/standardnotes/app
synced 2026-09-13 09:46:02 -04:00
Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ad5d028af | ||
|
|
596e041c42 | ||
|
|
80436cd0b9 | ||
|
|
40c0a08e74 | ||
|
|
560e91bce9 | ||
|
|
2a3db43df5 | ||
|
|
2e59aa5a07 | ||
|
|
98c0228139 | ||
|
|
d6bcc808d5 | ||
|
|
e18978d4ba | ||
|
|
ac4231d8eb | ||
|
|
3281ac9d37 | ||
|
|
f2d089ab24 | ||
|
|
a91ab4abbf | ||
|
|
0af41cbcad | ||
|
|
1adb21a06e | ||
|
|
412b192b85 | ||
|
|
8ad9b8ae2a | ||
|
|
c52d8e7309 | ||
|
|
406abb4929 | ||
|
|
814a156e1b | ||
|
|
e67f694c85 | ||
|
|
a77535456c | ||
|
|
e698b1c990 | ||
|
|
ab2e88a710 | ||
|
|
68db05581b | ||
|
|
7acf23033d |
@@ -6,6 +6,3 @@ DEFAULT_SYNC_SERVER=https://api.standardnotes.com
|
||||
DASHBOARD_URL=http://standardnotes.com/dashboard
|
||||
PLANS_URL=https://standardnotes.com/plans
|
||||
PURCHASE_URL=https://standardnotes.com/purchase
|
||||
|
||||
# Used by Rails internals and not Standard Notes related
|
||||
SECRET_KEY_BASE=<enter a secret string here>
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -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.26.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.26.29](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.26.29",
|
||||
"version": "1.26.30",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -13,4 +13,5 @@ export const SharedVaultInvitesPaths = {
|
||||
`/v1/shared-vaults/${sharedVaultUuid}/invites/${inviteUuid}`,
|
||||
deleteAllSharedVaultInvites: (sharedVaultUuid: string) => `/v1/shared-vaults/${sharedVaultUuid}/invites`,
|
||||
deleteAllInboundInvites: '/v1/shared-vaults/invites/inbound',
|
||||
deleteAllOutboundInvites: '/v1/shared-vaults/invites/outbound',
|
||||
}
|
||||
|
||||
@@ -72,4 +72,8 @@ export class SharedVaultInvitesServer implements SharedVaultInvitesServerInterfa
|
||||
deleteAllInboundInvites(): Promise<HttpResponse<{ success: boolean }>> {
|
||||
return this.httpService.delete(SharedVaultInvitesPaths.deleteAllInboundInvites)
|
||||
}
|
||||
|
||||
deleteAllOutboundInvites(): Promise<HttpResponse<{ success: boolean }>> {
|
||||
return this.httpService.delete(SharedVaultInvitesPaths.deleteAllOutboundInvites)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,5 @@ export interface SharedVaultInvitesServerInterface {
|
||||
): Promise<HttpResponse<DeleteAllSharedVaultInvitesResponse>>
|
||||
deleteInvite(params: DeleteInviteRequestParams): Promise<HttpResponse<DeleteInviteResponse>>
|
||||
deleteAllInboundInvites(): Promise<HttpResponse<{ success: boolean }>>
|
||||
deleteAllOutboundInvites(): Promise<HttpResponse<{ success: boolean }>>
|
||||
}
|
||||
|
||||
@@ -3,6 +3,46 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.113](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.112](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.111](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.110](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.109](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.108](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.107](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.106](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.105](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.104](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/clipper
|
||||
|
||||
## [1.1.103](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**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.103",
|
||||
"version": "1.1.113",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build-mv2": "yarn clean && webpack --config ./webpack.config.prod.js",
|
||||
|
||||
@@ -3,6 +3,46 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.108.44](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.43](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.42](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.41](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.40](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.39](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.38](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.37](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.36](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.35](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.108.34](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.108.34",
|
||||
"version": "3.108.44",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
|
||||
@@ -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.21.53](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.21.52](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.21.52",
|
||||
"version": "1.21.53",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
KeySystemRootKeyInterface,
|
||||
RootKeyInterface,
|
||||
KeySystemRootKeyParamsInterface,
|
||||
PortablePublicKeySet,
|
||||
} from '@standardnotes/models'
|
||||
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { firstHalfOfString, secondHalfOfString, splitString, UuidGenerator } from '@standardnotes/utils'
|
||||
@@ -28,11 +29,12 @@ import { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
|
||||
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
|
||||
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'
|
||||
import { OperatorInterface } from '../OperatorInterface/OperatorInterface'
|
||||
import { PublicKeySet } from '../Types/PublicKeySet'
|
||||
|
||||
import { AsymmetricDecryptResult } from '../Types/AsymmetricDecryptResult'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
import { AsyncOperatorInterface } from '../OperatorInterface/AsyncOperatorInterface'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { ContentType, Result } from '@standardnotes/domain-core'
|
||||
import { AsymmetricItemAdditionalData } from '../../Types/EncryptionAdditionalData'
|
||||
|
||||
const NO_IV = '00000000000000000000000000000000'
|
||||
|
||||
@@ -272,11 +274,23 @@ export class SNProtocolOperator001 implements OperatorInterface, AsyncOperatorIn
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
asymmetricDecryptOwnMessage(_dto: {
|
||||
message: string
|
||||
ownPrivateKey: string
|
||||
recipientPublicKey: string
|
||||
}): Result<AsymmetricDecryptResult> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
asymmetricSignatureVerifyDetached(_encryptedString: string): AsymmetricSignatureVerificationDetachedResult {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(_string: string): PublicKeySet {
|
||||
asymmetricStringGetAdditionalData(_dto: { encryptedString: string }): Result<AsymmetricItemAdditionalData> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(_string: string): PortablePublicKeySet {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
KeySystemIdentifier,
|
||||
RootKeyInterface,
|
||||
KeySystemRootKeyParamsInterface,
|
||||
PortablePublicKeySet,
|
||||
} from '@standardnotes/models'
|
||||
import { KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
||||
import { HexString, PkcKeyPair, PureCryptoInterface, Utf8String } from '@standardnotes/sncrypto-common'
|
||||
@@ -30,9 +31,9 @@ import { OperatorInterface } from '../OperatorInterface/OperatorInterface'
|
||||
import { AsymmetricallyEncryptedString } from '../Types/Types'
|
||||
import { AsymmetricItemAdditionalData } from '../../Types/EncryptionAdditionalData'
|
||||
import { V004AsymmetricStringComponents } from './V004AlgorithmTypes'
|
||||
import { AsymmetricEncryptUseCase } from './UseCase/Asymmetric/AsymmetricEncrypt'
|
||||
import { AsymmetricEncrypt004 } from './UseCase/Asymmetric/AsymmetricEncrypt'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from './UseCase/Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricDecryptUseCase } from './UseCase/Asymmetric/AsymmetricDecrypt'
|
||||
import { AsymmetricDecrypt004 } from './UseCase/Asymmetric/AsymmetricDecrypt'
|
||||
import { GenerateDecryptedParametersUseCase } from './UseCase/Symmetric/GenerateDecryptedParameters'
|
||||
import { GenerateEncryptedParametersUseCase } from './UseCase/Symmetric/GenerateEncryptedParameters'
|
||||
import { DeriveRootKeyUseCase } from './UseCase/RootKey/DeriveRootKey'
|
||||
@@ -41,14 +42,15 @@ import { CreateRootKeyUseCase } from './UseCase/RootKey/CreateRootKey'
|
||||
import { UuidGenerator } from '@standardnotes/utils'
|
||||
import { CreateKeySystemItemsKeyUseCase } from './UseCase/KeySystem/CreateKeySystemItemsKey'
|
||||
import { AsymmetricDecryptResult } from '../Types/AsymmetricDecryptResult'
|
||||
import { PublicKeySet } from '../Types/PublicKeySet'
|
||||
import { CreateRandomKeySystemRootKey } from './UseCase/KeySystem/CreateRandomKeySystemRootKey'
|
||||
import { CreateUserInputKeySystemRootKey } from './UseCase/KeySystem/CreateUserInputKeySystemRootKey'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
import { AsymmetricSignatureVerificationDetachedUseCase } from './UseCase/Asymmetric/AsymmetricSignatureVerificationDetached'
|
||||
import { AsymmetricSignatureVerificationDetached004 } from './UseCase/Asymmetric/AsymmetricSignatureVerificationDetached'
|
||||
import { DeriveKeySystemRootKeyUseCase } from './UseCase/KeySystem/DeriveKeySystemRootKey'
|
||||
import { SyncOperatorInterface } from '../OperatorInterface/SyncOperatorInterface'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { ContentType, Result } from '@standardnotes/domain-core'
|
||||
import { AsymmetricStringGetAdditionalData004 } from './UseCase/Asymmetric/AsymmetricStringGetAdditionalData'
|
||||
import { AsymmetricDecryptOwnMessage004 } from './UseCase/Asymmetric/AsymmetricDecryptOwnMessage'
|
||||
|
||||
export class SNProtocolOperator004 implements OperatorInterface, SyncOperatorInterface {
|
||||
constructor(protected readonly crypto: PureCryptoInterface) {}
|
||||
@@ -167,7 +169,7 @@ export class SNProtocolOperator004 implements OperatorInterface, SyncOperatorInt
|
||||
senderSigningKeyPair: PkcKeyPair
|
||||
recipientPublicKey: HexString
|
||||
}): AsymmetricallyEncryptedString {
|
||||
const usecase = new AsymmetricEncryptUseCase(this.crypto)
|
||||
const usecase = new AsymmetricEncrypt004(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
@@ -175,18 +177,34 @@ export class SNProtocolOperator004 implements OperatorInterface, SyncOperatorInt
|
||||
stringToDecrypt: AsymmetricallyEncryptedString
|
||||
recipientSecretKey: HexString
|
||||
}): AsymmetricDecryptResult | null {
|
||||
const usecase = new AsymmetricDecryptUseCase(this.crypto)
|
||||
const usecase = new AsymmetricDecrypt004(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
asymmetricDecryptOwnMessage(dto: {
|
||||
message: AsymmetricallyEncryptedString
|
||||
ownPrivateKey: HexString
|
||||
recipientPublicKey: HexString
|
||||
}): Result<AsymmetricDecryptResult> {
|
||||
const usecase = new AsymmetricDecryptOwnMessage004(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
asymmetricSignatureVerifyDetached(
|
||||
encryptedString: AsymmetricallyEncryptedString,
|
||||
): AsymmetricSignatureVerificationDetachedResult {
|
||||
const usecase = new AsymmetricSignatureVerificationDetachedUseCase(this.crypto)
|
||||
const usecase = new AsymmetricSignatureVerificationDetached004(this.crypto)
|
||||
return usecase.execute({ encryptedString })
|
||||
}
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PublicKeySet {
|
||||
asymmetricStringGetAdditionalData(dto: {
|
||||
encryptedString: AsymmetricallyEncryptedString
|
||||
}): Result<AsymmetricItemAdditionalData> {
|
||||
const usecase = new AsymmetricStringGetAdditionalData004(this.crypto)
|
||||
return usecase.execute(dto)
|
||||
}
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PortablePublicKeySet {
|
||||
const [_, __, ___, additionalDataString] = <V004AsymmetricStringComponents>string.split(':')
|
||||
const parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
const additionalData = parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
|
||||
+5
-5
@@ -1,27 +1,27 @@
|
||||
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { getMockedCrypto } from '../../MockedCrypto'
|
||||
import { AsymmetricDecryptUseCase } from './AsymmetricDecrypt'
|
||||
import { AsymmetricEncryptUseCase } from './AsymmetricEncrypt'
|
||||
import { AsymmetricDecrypt004 } from './AsymmetricDecrypt'
|
||||
import { AsymmetricEncrypt004 } from './AsymmetricEncrypt'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
|
||||
describe('asymmetric decrypt use case', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
let usecase: AsymmetricDecryptUseCase
|
||||
let usecase: AsymmetricDecrypt004
|
||||
let recipientKeyPair: PkcKeyPair
|
||||
let senderKeyPair: PkcKeyPair
|
||||
let senderSigningKeyPair: PkcKeyPair
|
||||
|
||||
beforeEach(() => {
|
||||
crypto = getMockedCrypto()
|
||||
usecase = new AsymmetricDecryptUseCase(crypto)
|
||||
usecase = new AsymmetricDecrypt004(crypto)
|
||||
recipientKeyPair = crypto.sodiumCryptoBoxSeedKeypair('recipient-seedling')
|
||||
senderKeyPair = crypto.sodiumCryptoBoxSeedKeypair('sender-seedling')
|
||||
senderSigningKeyPair = crypto.sodiumCryptoSignSeedKeypair('sender-signing-seedling')
|
||||
})
|
||||
|
||||
const getEncryptedString = () => {
|
||||
const encryptUsecase = new AsymmetricEncryptUseCase(crypto)
|
||||
const encryptUsecase = new AsymmetricEncrypt004(crypto)
|
||||
|
||||
const result = encryptUsecase.execute({
|
||||
stringToEncrypt: 'foobar',
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsisten
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { AsymmetricDecryptResult } from '../../../Types/AsymmetricDecryptResult'
|
||||
|
||||
export class AsymmetricDecryptUseCase {
|
||||
export class AsymmetricDecrypt004 {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { HexString, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { AsymmetricallyEncryptedString } from '../../../Types/Types'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { AsymmetricDecryptResult } from '../../../Types/AsymmetricDecryptResult'
|
||||
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class AsymmetricDecryptOwnMessage004 implements SyncUseCaseInterface<AsymmetricDecryptResult> {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
execute(dto: {
|
||||
message: AsymmetricallyEncryptedString
|
||||
ownPrivateKey: HexString
|
||||
recipientPublicKey: HexString
|
||||
}): Result<AsymmetricDecryptResult> {
|
||||
const [_, nonce, ciphertext, additionalDataString] = <V004AsymmetricStringComponents>dto.message.split(':')
|
||||
|
||||
const additionalData = this.parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
|
||||
try {
|
||||
const plaintext = this.crypto.sodiumCryptoBoxEasyDecrypt(
|
||||
ciphertext,
|
||||
nonce,
|
||||
dto.recipientPublicKey,
|
||||
dto.ownPrivateKey,
|
||||
)
|
||||
|
||||
if (!plaintext) {
|
||||
return Result.fail('Could not decrypt message')
|
||||
}
|
||||
|
||||
const signatureVerified = this.crypto.sodiumCryptoSignVerify(
|
||||
ciphertext,
|
||||
additionalData.signingData.signature,
|
||||
additionalData.signingData.publicKey,
|
||||
)
|
||||
|
||||
return Result.ok({
|
||||
plaintext,
|
||||
signatureVerified,
|
||||
signaturePublicKey: additionalData.signingData.publicKey,
|
||||
senderPublicKey: additionalData.senderPublicKey,
|
||||
})
|
||||
} catch (error) {
|
||||
return Result.fail('Could not decrypt message')
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,20 +1,20 @@
|
||||
import { PkcKeyPair, PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { getMockedCrypto } from '../../MockedCrypto'
|
||||
import { AsymmetricEncryptUseCase } from './AsymmetricEncrypt'
|
||||
import { AsymmetricEncrypt004 } from './AsymmetricEncrypt'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
|
||||
describe('asymmetric encrypt use case', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
let usecase: AsymmetricEncryptUseCase
|
||||
let usecase: AsymmetricEncrypt004
|
||||
let encryptionKeyPair: PkcKeyPair
|
||||
let signingKeyPair: PkcKeyPair
|
||||
let parseBase64Usecase: ParseConsistentBase64JsonPayloadUseCase
|
||||
|
||||
beforeEach(() => {
|
||||
crypto = getMockedCrypto()
|
||||
usecase = new AsymmetricEncryptUseCase(crypto)
|
||||
usecase = new AsymmetricEncrypt004(crypto)
|
||||
encryptionKeyPair = crypto.sodiumCryptoBoxSeedKeypair('seedling')
|
||||
signingKeyPair = crypto.sodiumCryptoSignSeedKeypair('seedling')
|
||||
parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(crypto)
|
||||
|
||||
@@ -5,7 +5,7 @@ import { V004AsymmetricCiphertextPrefix, V004AsymmetricStringComponents } from '
|
||||
import { CreateConsistentBase64JsonPayloadUseCase } from '../Utils/CreateConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
|
||||
export class AsymmetricEncryptUseCase {
|
||||
export class AsymmetricEncrypt004 {
|
||||
private base64DataUsecase = new CreateConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
@@ -21,8 +21,8 @@ export class AsymmetricEncryptUseCase {
|
||||
const ciphertext = this.crypto.sodiumCryptoBoxEasyEncrypt(
|
||||
dto.stringToEncrypt,
|
||||
nonce,
|
||||
dto.senderKeyPair.privateKey,
|
||||
dto.recipientPublicKey,
|
||||
dto.senderKeyPair.privateKey,
|
||||
)
|
||||
|
||||
const additionalData: AsymmetricItemAdditionalData = {
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsisten
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../../../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
|
||||
export class AsymmetricSignatureVerificationDetachedUseCase {
|
||||
export class AsymmetricSignatureVerificationDetached004 {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { AsymmetricallyEncryptedString } from '../../../Types/Types'
|
||||
import { V004AsymmetricStringComponents } from '../../V004AlgorithmTypes'
|
||||
import { ParseConsistentBase64JsonPayloadUseCase } from '../Utils/ParseConsistentBase64JsonPayload'
|
||||
import { AsymmetricItemAdditionalData } from '../../../../Types/EncryptionAdditionalData'
|
||||
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class AsymmetricStringGetAdditionalData004 implements SyncUseCaseInterface<AsymmetricItemAdditionalData> {
|
||||
private parseBase64Usecase = new ParseConsistentBase64JsonPayloadUseCase(this.crypto)
|
||||
|
||||
constructor(private readonly crypto: PureCryptoInterface) {}
|
||||
|
||||
execute(dto: { encryptedString: AsymmetricallyEncryptedString }): Result<AsymmetricItemAdditionalData> {
|
||||
const [_, __, ___, additionalDataString] = <V004AsymmetricStringComponents>dto.encryptedString.split(':')
|
||||
|
||||
const additionalData = this.parseBase64Usecase.execute<AsymmetricItemAdditionalData>(additionalDataString)
|
||||
|
||||
return Result.ok(additionalData)
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -2,8 +2,9 @@ import { ProtocolVersion, ProtocolVersionLatest } from '@standardnotes/common'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { createOperatorForVersion } from './Functions'
|
||||
import { AnyOperatorInterface } from './OperatorInterface/TypeCheck'
|
||||
import { EncryptionOperatorsInterface } from './EncryptionOperatorsInterface'
|
||||
|
||||
export class OperatorManager {
|
||||
export class EncryptionOperators implements EncryptionOperatorsInterface {
|
||||
private operators: Record<string, AnyOperatorInterface> = {}
|
||||
|
||||
constructor(private crypto: PureCryptoInterface) {
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { AnyOperatorInterface } from './OperatorInterface/TypeCheck'
|
||||
|
||||
export interface EncryptionOperatorsInterface {
|
||||
operatorForVersion(version: ProtocolVersion): AnyOperatorInterface
|
||||
defaultOperator(): AnyOperatorInterface
|
||||
deinit(): void
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
KeySystemRootKeyInterface,
|
||||
KeySystemIdentifier,
|
||||
KeySystemRootKeyParamsInterface,
|
||||
PortablePublicKeySet,
|
||||
} from '@standardnotes/models'
|
||||
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||
import { EncryptedOutputParameters } from '../../Types/EncryptedParameters'
|
||||
@@ -15,8 +16,9 @@ import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedA
|
||||
import { HexString, PkcKeyPair } from '@standardnotes/sncrypto-common'
|
||||
import { AsymmetricallyEncryptedString } from '../Types/Types'
|
||||
import { AsymmetricDecryptResult } from '../Types/AsymmetricDecryptResult'
|
||||
import { PublicKeySet } from '../Types/PublicKeySet'
|
||||
import { AsymmetricSignatureVerificationDetachedResult } from '../Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
import { AsymmetricItemAdditionalData } from '../../Types/EncryptionAdditionalData'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
/**w
|
||||
* An operator is responsible for performing crypto operations, such as generating keys
|
||||
@@ -92,11 +94,21 @@ export interface OperatorInterface {
|
||||
recipientSecretKey: HexString
|
||||
}): AsymmetricDecryptResult | null
|
||||
|
||||
asymmetricDecryptOwnMessage(dto: {
|
||||
message: AsymmetricallyEncryptedString
|
||||
ownPrivateKey: HexString
|
||||
recipientPublicKey: HexString
|
||||
}): Result<AsymmetricDecryptResult>
|
||||
|
||||
asymmetricSignatureVerifyDetached(
|
||||
encryptedString: AsymmetricallyEncryptedString,
|
||||
): AsymmetricSignatureVerificationDetachedResult
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PublicKeySet
|
||||
asymmetricStringGetAdditionalData(dto: {
|
||||
encryptedString: AsymmetricallyEncryptedString
|
||||
}): Result<AsymmetricItemAdditionalData>
|
||||
|
||||
getSenderPublicKeySetFromAsymmetricallyEncryptedString(string: AsymmetricallyEncryptedString): PortablePublicKeySet
|
||||
|
||||
versionForAsymmetricallyEncryptedString(encryptedString: string): ProtocolVersion
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ import {
|
||||
ErrorDecryptingParameters,
|
||||
} from '../Types/EncryptedParameters'
|
||||
import { DecryptedParameters } from '../Types/DecryptedParameters'
|
||||
import { OperatorManager } from './OperatorManager'
|
||||
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
|
||||
import { isAsyncOperator } from './OperatorInterface/TypeCheck'
|
||||
import { EncryptionOperatorsInterface } from './EncryptionOperatorsInterface'
|
||||
|
||||
export async function encryptPayload(
|
||||
payload: DecryptedPayloadInterface,
|
||||
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | RootKeyInterface,
|
||||
operatorManager: OperatorManager,
|
||||
operatorManager: EncryptionOperatorsInterface,
|
||||
signingKeyPair: PkcKeyPair | undefined,
|
||||
): Promise<EncryptedOutputParameters> {
|
||||
const operator = operatorManager.operatorForVersion(key.keyVersion)
|
||||
@@ -42,7 +42,7 @@ export async function encryptPayload(
|
||||
export async function decryptPayload<C extends ItemContent = ItemContent>(
|
||||
payload: EncryptedPayloadInterface,
|
||||
key: ItemsKeyInterface | KeySystemItemsKeyInterface | KeySystemRootKeyInterface | RootKeyInterface,
|
||||
operatorManager: OperatorManager,
|
||||
operatorManager: EncryptionOperatorsInterface,
|
||||
): Promise<DecryptedParameters<C> | ErrorDecryptingParameters> {
|
||||
const operator = operatorManager.operatorForVersion(payload.version)
|
||||
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { ItemsKeyInterface } from '@standardnotes/models'
|
||||
|
||||
export function findDefaultItemsKey(itemsKeys: ItemsKeyInterface[]): ItemsKeyInterface | undefined {
|
||||
if (itemsKeys.length === 1) {
|
||||
return itemsKeys[0]
|
||||
}
|
||||
|
||||
const defaultKeys = itemsKeys.filter((key) => {
|
||||
return key.isDefault
|
||||
})
|
||||
|
||||
if (defaultKeys.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (defaultKeys.length === 1) {
|
||||
return defaultKeys[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Prioritize one that is synced, as neverSynced keys will likely be deleted after
|
||||
* DownloadFirst sync.
|
||||
*/
|
||||
const syncedKeys = defaultKeys.filter((key) => !key.neverSynced)
|
||||
if (syncedKeys.length > 0) {
|
||||
return syncedKeys[0]
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
@@ -1,42 +1,30 @@
|
||||
export * from './Algorithm'
|
||||
export * from './Backups/BackupFileType'
|
||||
|
||||
export * from './Keys/ItemsKey/ItemsKey'
|
||||
export * from './Keys/ItemsKey/ItemsKeyMutator'
|
||||
export * from './Keys/ItemsKey/Registration'
|
||||
|
||||
export * from './Keys/KeySystemItemsKey/KeySystemItemsKey'
|
||||
export * from './Keys/KeySystemItemsKey/KeySystemItemsKeyMutator'
|
||||
export * from './Keys/KeySystemItemsKey/Registration'
|
||||
|
||||
export * from './Keys/RootKey/Functions'
|
||||
export * from './Keys/RootKey/KeyParamsFunctions'
|
||||
export * from './Keys/RootKey/ProtocolVersionForKeyParams'
|
||||
export * from './Keys/RootKey/RootKey'
|
||||
export * from './Keys/RootKey/RootKeyParams'
|
||||
export * from './Keys/RootKey/ValidKeyParamsKeys'
|
||||
|
||||
export * from './Keys/Utils/KeyRecoveryStrings'
|
||||
|
||||
export * from './Operator/001/Operator001'
|
||||
export * from './Operator/002/Operator002'
|
||||
export * from './Operator/003/Operator003'
|
||||
export * from './Operator/004/Operator004'
|
||||
export * from './Operator/004/V004AlgorithmHelpers'
|
||||
|
||||
export * from './Operator/EncryptionOperators'
|
||||
export * from './Operator/EncryptionOperatorsInterface'
|
||||
export * from './Operator/Functions'
|
||||
export * from './Operator/OperatorInterface/OperatorInterface'
|
||||
export * from './Operator/OperatorManager'
|
||||
export * from './Operator/OperatorWrapper'
|
||||
export * from './Operator/Types/PublicKeySet'
|
||||
export * from './Operator/Types/AsymmetricSignatureVerificationDetachedResult'
|
||||
export * from './Operator/Types/Types'
|
||||
|
||||
export * from './Service/Encryption/EncryptionProviderInterface'
|
||||
export * from './Service/KeySystemKeyManagerInterface'
|
||||
export * from './Service/Functions'
|
||||
export * from './Service/RootKey/KeyMode'
|
||||
|
||||
export * from './Split/AbstractKeySplit'
|
||||
export * from './Split/EncryptionSplit'
|
||||
export * from './Split/EncryptionTypeSplit'
|
||||
@@ -44,11 +32,9 @@ export * from './Split/Functions'
|
||||
export * from './Split/KeyedDecryptionSplit'
|
||||
export * from './Split/KeyedEncryptionSplit'
|
||||
export * from './StandardException'
|
||||
|
||||
export * from './Types/EncryptedParameters'
|
||||
export * from './Types/DecryptedParameters'
|
||||
export * from './Types/EncryptedParameters'
|
||||
export * from './Types/ItemAuthenticatedData'
|
||||
export * from './Types/LegacyAttachedData'
|
||||
export * from './Types/RootKeyEncryptedAuthenticatedData'
|
||||
|
||||
export * from './Username/PrivateUsername'
|
||||
|
||||
@@ -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.28.62](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.28.61](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.28.61",
|
||||
"version": "1.28.62",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -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.16.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.16.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.16.7",
|
||||
"version": "1.16.8",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,52 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.56.24](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.23](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.22](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.21](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.20](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.19](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.18](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.17](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Shared image links are now correctly handled on iOS ([ab2e88a](https://github.com/standardnotes/app/commit/ab2e88a710c65771312bd9d02cec0b400d352a0e))
|
||||
|
||||
## [3.56.14](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.56.13](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
@@ -118,7 +118,24 @@ class ReceiveSharingIntent: NSObject {
|
||||
|
||||
@objc
|
||||
func clearFileNames(){
|
||||
print("clearFileNames");
|
||||
let appDomain = Bundle.main.bundleIdentifier!
|
||||
let groupName = "group.\(appDomain)"
|
||||
let userDefaults = UserDefaults(suiteName: groupName)
|
||||
userDefaults?.set(nil, forKey: "ShareKey.media")
|
||||
userDefaults?.set(nil, forKey: "ShareKey.text")
|
||||
userDefaults?.set(nil, forKey: "ShareKey.url")
|
||||
let containerURL = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: groupName)!
|
||||
let shareTempDirPath = containerURL.appendingPathComponent("ShareTemp", isDirectory: true)
|
||||
do {
|
||||
let fileNames = try FileManager.default.contentsOfDirectory(atPath: shareTempDirPath.relativePath)
|
||||
for fileName in fileNames {
|
||||
let filePath = shareTempDirPath.appendingPathComponent(fileName)
|
||||
try FileManager.default.removeItem(at: filePath)
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -95,16 +95,11 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
attachment.loadItem(forTypeIdentifier: imageContentType, options: nil) { [weak self] data, error in
|
||||
|
||||
if error == nil, let url = data as? URL, let this = self {
|
||||
// this.redirectToHostApp(type: .media)
|
||||
// Always copy
|
||||
let fileExtension = this.getExtension(from: url, type: .video)
|
||||
let newName = UUID().uuidString
|
||||
let newPath = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||
.appendingPathComponent("\(newName).\(fileExtension)")
|
||||
let copied = this.copyFile(at: url, to: newPath)
|
||||
if(copied) {
|
||||
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .image))
|
||||
if let copiedPath = this.copyToTemporaryPath(url: url, name: "\(newName).\(fileExtension)") {
|
||||
this.sharedMedia.append(SharedMediaFile(path: copiedPath, thumbnail: nil, duration: nil, type: .image))
|
||||
}
|
||||
|
||||
if index == (content.attachments?.count)! - 1 {
|
||||
@@ -125,15 +120,8 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
// Always copy
|
||||
let fileExtension = this.getExtension(from: url, type: .video)
|
||||
let newName = UUID().uuidString
|
||||
let newPath = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||
.appendingPathComponent("\(newName).\(fileExtension)")
|
||||
let copied = this.copyFile(at: url, to: newPath)
|
||||
if(copied) {
|
||||
guard let sharedFile = this.getSharedMediaFile(forVideo: newPath) else {
|
||||
return
|
||||
}
|
||||
this.sharedMedia.append(sharedFile)
|
||||
if let copiedPath = this.copyToTemporaryPath(url: url, name: "\(newName).\(fileExtension)") {
|
||||
this.sharedMedia.append(SharedMediaFile(path: copiedPath, thumbnail: nil, duration: nil, type: .video))
|
||||
}
|
||||
|
||||
if index == (content.attachments?.count)! - 1 {
|
||||
@@ -151,14 +139,9 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
|
||||
if error == nil, let url = data as? URL, let this = self {
|
||||
|
||||
// Always copy
|
||||
let newName = this.getFileName(from :url)
|
||||
let newPath = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||
.appendingPathComponent("\(newName)")
|
||||
let copied = this.copyFile(at: url, to: newPath)
|
||||
if (copied) {
|
||||
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .file))
|
||||
if let copiedPath = this.copyToTemporaryPath(url: url, name: newName) {
|
||||
this.sharedMedia.append(SharedMediaFile(path: copiedPath, thumbnail: nil, duration: nil, type: .file))
|
||||
}
|
||||
|
||||
if index == (content.attachments?.count)! - 1 {
|
||||
@@ -171,6 +154,32 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
}
|
||||
}
|
||||
|
||||
private func directoryExistsAtPath(_ path: String) -> Bool {
|
||||
var isDirectory = ObjCBool(true)
|
||||
let exists = FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory)
|
||||
return exists && isDirectory.boolValue
|
||||
}
|
||||
|
||||
private func copyToTemporaryPath(url: URL, name: String) -> String? {
|
||||
let containerURL = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||
let shareTempDirPath = containerURL.appendingPathComponent("ShareTemp", isDirectory: true)
|
||||
if !directoryExistsAtPath(shareTempDirPath.absoluteString) {
|
||||
do {
|
||||
try FileManager.default.createDirectory(atPath: shareTempDirPath.relativePath, withIntermediateDirectories: true, attributes: nil)
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
let newPath = shareTempDirPath
|
||||
.appendingPathComponent("\(name)")
|
||||
let copied = self.copyFile(at: url, to: newPath)
|
||||
if (copied) {
|
||||
return newPath.absoluteString
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func redirectToHostApp() {
|
||||
let userDefaults = UserDefaults(suiteName: "group.\(hostAppBundleIdentifier)")
|
||||
userDefaults?.set(self.toData(data: self.sharedMedia), forKey: "\(self.sharedKey).media")
|
||||
@@ -263,40 +272,6 @@ class ShareViewController: SLComposeServiceViewController {
|
||||
return true
|
||||
}
|
||||
|
||||
private func getSharedMediaFile(forVideo: URL) -> SharedMediaFile? {
|
||||
let asset = AVAsset(url: forVideo)
|
||||
let duration = (CMTimeGetSeconds(asset.duration) * 1000).rounded()
|
||||
let thumbnailPath = getThumbnailPath(for: forVideo)
|
||||
|
||||
if FileManager.default.fileExists(atPath: thumbnailPath.path) {
|
||||
return SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video)
|
||||
}
|
||||
|
||||
var saved = false
|
||||
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
|
||||
assetImgGenerate.appliesPreferredTrackTransform = true
|
||||
// let scale = UIScreen.main.scale
|
||||
assetImgGenerate.maximumSize = CGSize(width: 360, height: 360)
|
||||
do {
|
||||
let img = try assetImgGenerate.copyCGImage(at: CMTimeMakeWithSeconds(600, preferredTimescale: Int32(1.0)), actualTime: nil)
|
||||
try UIImage.pngData(UIImage(cgImage: img))()?.write(to: thumbnailPath)
|
||||
saved = true
|
||||
} catch {
|
||||
saved = false
|
||||
}
|
||||
|
||||
return saved ? SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video) : nil
|
||||
|
||||
}
|
||||
|
||||
private func getThumbnailPath(for url: URL) -> URL {
|
||||
let fileName = Data(url.lastPathComponent.utf8).base64EncodedString().replacingOccurrences(of: "==", with: "")
|
||||
let path = FileManager.default
|
||||
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||
.appendingPathComponent("\(fileName).jpg")
|
||||
return path
|
||||
}
|
||||
|
||||
class SharedMediaFile: Codable {
|
||||
var path: String; // can be image, video or url path. It can also be text content
|
||||
var thumbnail: String?; // video thumbnail
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.56.13",
|
||||
"version": "3.56.24",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -71,6 +71,7 @@ export class ReceivedSharedItemsHandler {
|
||||
}
|
||||
|
||||
deinit() {
|
||||
ReceiveSharingIntent.clearFileNames()
|
||||
this.receivedItemsQueue = []
|
||||
this.eventSub?.remove()
|
||||
}
|
||||
@@ -100,51 +101,21 @@ export class ReceivedSharedItemsHandler {
|
||||
}
|
||||
|
||||
handleItemsQueue = async () => {
|
||||
if (!this.receivedItemsQueue.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const item = this.receivedItemsQueue.shift()
|
||||
if (!item) {
|
||||
return
|
||||
}
|
||||
|
||||
if (isReceivedAndroidFile(item)) {
|
||||
const data = await readFile(item.contentUri, 'base64')
|
||||
const file = {
|
||||
name: item.fileName || item.contentUri,
|
||||
data,
|
||||
mimeType: item.mimeType,
|
||||
}
|
||||
this.webViewRef.current?.postMessage(
|
||||
JSON.stringify({
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedFile,
|
||||
messageType: 'event',
|
||||
messageData: file,
|
||||
}),
|
||||
)
|
||||
} else if (isReceivedIosFile(item)) {
|
||||
const data = await readFile(item.path, 'base64')
|
||||
const file = {
|
||||
name: item.fileName || item.path,
|
||||
data,
|
||||
mimeType: item.mimeType,
|
||||
}
|
||||
this.webViewRef.current?.postMessage(
|
||||
JSON.stringify({
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedFile,
|
||||
messageType: 'event',
|
||||
messageData: file,
|
||||
}),
|
||||
)
|
||||
if (isReceivedAndroidFile(item) || isReceivedIosFile(item)) {
|
||||
await this.sendFileToWebView(item).catch(console.error)
|
||||
} else if (isReceivedWeblink(item)) {
|
||||
this.webViewRef.current?.postMessage(
|
||||
JSON.stringify({
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedText,
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedLink,
|
||||
messageType: 'event',
|
||||
messageData: {
|
||||
title: item.subject || item.weblink,
|
||||
text: item.weblink,
|
||||
link: item.weblink,
|
||||
},
|
||||
}),
|
||||
)
|
||||
@@ -160,13 +131,43 @@ export class ReceivedSharedItemsHandler {
|
||||
)
|
||||
}
|
||||
|
||||
if (!this.receivedItemsQueue.length) {
|
||||
if (Platform.OS === 'ios') {
|
||||
ReceiveSharingIntent.clearFileNames()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.handleItemsQueue().catch(console.error)
|
||||
}
|
||||
|
||||
sendFileToWebView = async (item: ReceivedAndroidFile | ReceivedIosFile) => {
|
||||
const path = isReceivedAndroidFile(item) ? item.contentUri : item.path
|
||||
const data = await readFile(decodeURIComponent(path), 'base64')
|
||||
const file = {
|
||||
name: item.fileName || item.path,
|
||||
data,
|
||||
mimeType: item.mimeType,
|
||||
}
|
||||
this.webViewRef.current?.postMessage(
|
||||
JSON.stringify({
|
||||
reactNativeEvent: ReactNativeToWebEvent.ReceivedFile,
|
||||
messageType: 'event',
|
||||
messageData: file,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private addSharedItemsToQueue = async (url?: string) => {
|
||||
const received =
|
||||
Platform.OS === 'ios' ? await ReceiveSharingIntent.getFileNames(url) : await ReceiveSharingIntent.getFileNames()
|
||||
ReceiveSharingIntent.clearFileNames()
|
||||
|
||||
// On iOS, we need to wait for the whole queue
|
||||
// to finish before clearing
|
||||
if (Platform.OS === 'android') {
|
||||
ReceiveSharingIntent.clearFileNames()
|
||||
}
|
||||
|
||||
if (!received) {
|
||||
return
|
||||
|
||||
@@ -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.46.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
## [1.46.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/models",
|
||||
"version": "1.46.11",
|
||||
"version": "1.46.12",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ export class DecryptedItemMutator<
|
||||
this.mutableContent = mutableCopy
|
||||
}
|
||||
|
||||
public override getResult() {
|
||||
public override getResult(): DecryptedPayloadInterface<C> {
|
||||
if (this.type === MutationType.NonDirtying) {
|
||||
return this.immutablePayload.copy({
|
||||
content: this.mutableContent,
|
||||
|
||||
@@ -3,7 +3,6 @@ export enum AsymmetricMessagePayloadType {
|
||||
SharedVaultRootKeyChanged = 'shared-vault-root-key-changed',
|
||||
SenderKeypairChanged = 'sender-keypair-changed',
|
||||
SharedVaultMetadataChanged = 'shared-vault-metadata-changed',
|
||||
|
||||
/**
|
||||
* Shared Vault Invites conform to the asymmetric message protocol, but are sent via the dedicated
|
||||
* SharedVaultInvite model and not the AsymmetricMessage model on the server side.
|
||||
|
||||
+8
-2
@@ -1,13 +1,19 @@
|
||||
import { KeySystemRootKeyContentSpecialized } from '../../../Syncable/KeySystemRootKey/KeySystemRootKeyContent'
|
||||
import { TrustedContactContentSpecialized } from '../../../Syncable/TrustedContact/TrustedContactContent'
|
||||
import { ContactPublicKeySetJsonInterface } from '../../../Syncable/TrustedContact/PublicKeySet/ContactPublicKeySetJsonInterface'
|
||||
import { AsymmetricMessageDataCommon } from '../AsymmetricMessageDataCommon'
|
||||
import { AsymmetricMessagePayloadType } from '../AsymmetricMessagePayloadType'
|
||||
|
||||
export type VaultInviteDelegatedContact = {
|
||||
name?: string
|
||||
contactUuid: string
|
||||
publicKeySet: ContactPublicKeySetJsonInterface
|
||||
}
|
||||
|
||||
export type AsymmetricMessageSharedVaultInvite = {
|
||||
type: AsymmetricMessagePayloadType.SharedVaultInvite
|
||||
data: AsymmetricMessageDataCommon & {
|
||||
rootKey: KeySystemRootKeyContentSpecialized
|
||||
trustedContacts: TrustedContactContentSpecialized[]
|
||||
trustedContacts: VaultInviteDelegatedContact[]
|
||||
metadata: {
|
||||
name: string
|
||||
description?: string
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { TrustedContactContentSpecialized } from '../../../Syncable/TrustedContact/TrustedContactContent'
|
||||
import { TrustedContactContentSpecialized } from '../../../Syncable/TrustedContact/Content/TrustedContactContent'
|
||||
import { AsymmetricMessageDataCommon } from '../AsymmetricMessageDataCommon'
|
||||
import { AsymmetricMessagePayloadType } from '../AsymmetricMessagePayloadType'
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ItemContent } from '../../../Abstract/Content/ItemContent'
|
||||
import { ContactPublicKeySetJsonInterface } from '../PublicKeySet/ContactPublicKeySetJsonInterface'
|
||||
|
||||
export type TrustedContactContentSpecialized = {
|
||||
name: string
|
||||
contactUuid: string
|
||||
publicKeySet: ContactPublicKeySetJsonInterface
|
||||
isMe: boolean
|
||||
}
|
||||
|
||||
export type TrustedContactContent = TrustedContactContentSpecialized & ItemContent
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { DecryptedPayload, PayloadTimestampDefaults } from '../../../Abstract/Payload'
|
||||
import { TrustedContact } from '../TrustedContact'
|
||||
import { TrustedContactInterface } from '../TrustedContactInterface'
|
||||
import { TrustedContactMutator } from './TrustedContactMutator'
|
||||
import { ContactPublicKeySet } from '../PublicKeySet/ContactPublicKeySet'
|
||||
import { FillItemContentSpecialized } from '../../../Abstract/Content/ItemContent'
|
||||
import { TrustedContactContentSpecialized } from '../Content/TrustedContactContent'
|
||||
import { MutationType } from '../../../Abstract/Item'
|
||||
import { PortablePublicKeySet } from '../Types/PortablePublicKeySet'
|
||||
import { ContactPublicKeySetInterface } from '../PublicKeySet/ContactPublicKeySetInterface'
|
||||
|
||||
function createMockPublicKeySetChain(): ContactPublicKeySetInterface {
|
||||
const nMinusOne = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key-n-1',
|
||||
signing: 'signing-public-key-n-1',
|
||||
timestamp: new Date(-1),
|
||||
previousKeySet: undefined,
|
||||
})
|
||||
|
||||
const root = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key',
|
||||
signing: 'signing-public-key',
|
||||
timestamp: new Date(),
|
||||
previousKeySet: nMinusOne,
|
||||
})
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
describe('TrustedContactMutator', () => {
|
||||
let contact: TrustedContactInterface
|
||||
let mutator: TrustedContactMutator
|
||||
|
||||
beforeEach(() => {
|
||||
contact = new TrustedContact(
|
||||
new DecryptedPayload({
|
||||
uuid: 'item-uuid',
|
||||
content_type: ContentType.TYPES.TrustedContact,
|
||||
...PayloadTimestampDefaults(),
|
||||
content: FillItemContentSpecialized<TrustedContactContentSpecialized, TrustedContactInterface>({
|
||||
name: 'test',
|
||||
contactUuid: 'contact-uuid',
|
||||
isMe: true,
|
||||
publicKeySet: createMockPublicKeySetChain(),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
|
||||
mutator = new TrustedContactMutator(contact, MutationType.UpdateUserTimestamps)
|
||||
})
|
||||
|
||||
it('should set name', () => {
|
||||
mutator.name = 'new name'
|
||||
|
||||
const result = mutator.getResult()
|
||||
|
||||
expect(result.content.name).toEqual('new name')
|
||||
})
|
||||
|
||||
it('should add public key', () => {
|
||||
const currentKeySet = contact.publicKeySet
|
||||
|
||||
const newKeySet: PortablePublicKeySet = {
|
||||
encryption: 'new-encryption-public-key',
|
||||
signing: 'new-signing-public-key',
|
||||
}
|
||||
|
||||
mutator.addPublicKey(newKeySet)
|
||||
|
||||
const result = new TrustedContact(mutator.getResult())
|
||||
|
||||
expect(result.publicKeySet.encryption).toEqual(newKeySet.encryption)
|
||||
expect(result.publicKeySet.signing).toEqual(newKeySet.signing)
|
||||
expect(result.publicKeySet.previousKeySet).toEqual(currentKeySet)
|
||||
})
|
||||
|
||||
it('should replace public key set', () => {
|
||||
const replacement = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key-replacement',
|
||||
signing: 'signing-public-key-replacement',
|
||||
timestamp: new Date(),
|
||||
previousKeySet: undefined,
|
||||
})
|
||||
|
||||
mutator.replacePublicKeySet(replacement.asJson())
|
||||
|
||||
const result = new TrustedContact(mutator.getResult())
|
||||
|
||||
expect(result.publicKeySet.encryption).toEqual(replacement.encryption)
|
||||
expect(result.publicKeySet.signing).toEqual(replacement.signing)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
import { DecryptedItemMutator } from '../../../Abstract/Item'
|
||||
import { TrustedContactContent } from '../Content/TrustedContactContent'
|
||||
import { TrustedContactInterface } from '../TrustedContactInterface'
|
||||
import { ContactPublicKeySet } from '../PublicKeySet/ContactPublicKeySet'
|
||||
import { PortablePublicKeySet } from '../Types/PortablePublicKeySet'
|
||||
import { ContactPublicKeySetJsonInterface } from '../PublicKeySet/ContactPublicKeySetJsonInterface'
|
||||
|
||||
export class TrustedContactMutator extends DecryptedItemMutator<TrustedContactContent, TrustedContactInterface> {
|
||||
set name(newName: string) {
|
||||
this.mutableContent.name = newName
|
||||
}
|
||||
|
||||
addPublicKey(keySet: PortablePublicKeySet): void {
|
||||
const newKey = new ContactPublicKeySet({
|
||||
encryption: keySet.encryption,
|
||||
signing: keySet.signing,
|
||||
timestamp: new Date(),
|
||||
previousKeySet: this.immutableItem.publicKeySet,
|
||||
})
|
||||
|
||||
this.mutableContent.publicKeySet = newKey
|
||||
}
|
||||
|
||||
replacePublicKeySet(publicKeySet: ContactPublicKeySetJsonInterface): void {
|
||||
this.mutableContent.publicKeySet = publicKeySet
|
||||
}
|
||||
}
|
||||
+35
-36
@@ -5,36 +5,18 @@ export class ContactPublicKeySet implements ContactPublicKeySetInterface {
|
||||
encryption: string
|
||||
signing: string
|
||||
timestamp: Date
|
||||
isRevoked: boolean
|
||||
previousKeySet?: ContactPublicKeySet
|
||||
previousKeySet?: ContactPublicKeySetInterface
|
||||
|
||||
constructor(
|
||||
encryption: string,
|
||||
signing: string,
|
||||
timestamp: Date,
|
||||
isRevoked: boolean,
|
||||
previousKeySet: ContactPublicKeySet | undefined,
|
||||
) {
|
||||
this.encryption = encryption
|
||||
this.signing = signing
|
||||
this.timestamp = timestamp
|
||||
this.isRevoked = isRevoked
|
||||
this.previousKeySet = previousKeySet
|
||||
}
|
||||
|
||||
public findKeySet(params: {
|
||||
targetEncryptionPublicKey: string
|
||||
targetSigningPublicKey: string
|
||||
}): ContactPublicKeySetInterface | undefined {
|
||||
if (this.encryption === params.targetEncryptionPublicKey && this.signing === params.targetSigningPublicKey) {
|
||||
return this
|
||||
}
|
||||
|
||||
if (this.previousKeySet) {
|
||||
return this.previousKeySet.findKeySet(params)
|
||||
}
|
||||
|
||||
return undefined
|
||||
constructor(dto: {
|
||||
encryption: string
|
||||
signing: string
|
||||
timestamp: Date
|
||||
previousKeySet: ContactPublicKeySetInterface | undefined
|
||||
}) {
|
||||
this.encryption = dto.encryption
|
||||
this.signing = dto.signing
|
||||
this.timestamp = dto.timestamp
|
||||
this.previousKeySet = dto.previousKeySet
|
||||
}
|
||||
|
||||
public findKeySetWithSigningKey(signingKey: string): ContactPublicKeySetInterface | undefined {
|
||||
@@ -61,13 +43,30 @@ export class ContactPublicKeySet implements ContactPublicKeySetInterface {
|
||||
return undefined
|
||||
}
|
||||
|
||||
asJson(): ContactPublicKeySetJsonInterface {
|
||||
return {
|
||||
encryption: this.encryption,
|
||||
signing: this.signing,
|
||||
timestamp: this.timestamp,
|
||||
previousKeySet: this.previousKeySet ? this.previousKeySet.asJson() : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
mutableCopy(): ContactPublicKeySetInterface {
|
||||
return new ContactPublicKeySet({
|
||||
encryption: this.encryption,
|
||||
signing: this.signing,
|
||||
timestamp: this.timestamp,
|
||||
previousKeySet: this.previousKeySet ? ContactPublicKeySet.FromJson(this.previousKeySet.asJson()) : undefined,
|
||||
})
|
||||
}
|
||||
|
||||
static FromJson(json: ContactPublicKeySetJsonInterface): ContactPublicKeySetInterface {
|
||||
return new ContactPublicKeySet(
|
||||
json.encryption,
|
||||
json.signing,
|
||||
new Date(json.timestamp),
|
||||
json.isRevoked,
|
||||
json.previousKeySet ? ContactPublicKeySet.FromJson(json.previousKeySet) : undefined,
|
||||
)
|
||||
return new ContactPublicKeySet({
|
||||
encryption: json.encryption,
|
||||
signing: json.signing,
|
||||
timestamp: new Date(json.timestamp),
|
||||
previousKeySet: json.previousKeySet ? ContactPublicKeySet.FromJson(json.previousKeySet) : undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,15 +1,14 @@
|
||||
import { ContactPublicKeySetJsonInterface } from './ContactPublicKeySetJsonInterface'
|
||||
|
||||
export interface ContactPublicKeySetInterface {
|
||||
encryption: string
|
||||
signing: string
|
||||
timestamp: Date
|
||||
isRevoked: boolean
|
||||
previousKeySet?: ContactPublicKeySetInterface
|
||||
|
||||
findKeySet(params: {
|
||||
targetEncryptionPublicKey: string
|
||||
targetSigningPublicKey: string
|
||||
}): ContactPublicKeySetInterface | undefined
|
||||
|
||||
findKeySetWithPublicKey(publicKey: string): ContactPublicKeySetInterface | undefined
|
||||
findKeySetWithSigningKey(signingKey: string): ContactPublicKeySetInterface | undefined
|
||||
|
||||
asJson(): ContactPublicKeySetJsonInterface
|
||||
mutableCopy(): ContactPublicKeySetInterface
|
||||
}
|
||||
|
||||
-1
@@ -2,6 +2,5 @@ export interface ContactPublicKeySetJsonInterface {
|
||||
encryption: string
|
||||
signing: string
|
||||
timestamp: Date
|
||||
isRevoked: boolean
|
||||
previousKeySet?: ContactPublicKeySetJsonInterface
|
||||
}
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
import { ContactPublicKeySetInterface } from './ContactPublicKeySetInterface'
|
||||
|
||||
export type FindPublicKeySetResult =
|
||||
| {
|
||||
publicKeySet: ContactPublicKeySetInterface
|
||||
current: boolean
|
||||
}
|
||||
| undefined
|
||||
@@ -0,0 +1,109 @@
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { DecryptedPayload, PayloadTimestampDefaults } from '../../Abstract/Payload'
|
||||
import { ContactPublicKeySet } from './PublicKeySet/ContactPublicKeySet'
|
||||
import { ContactPublicKeySetInterface } from './PublicKeySet/ContactPublicKeySetInterface'
|
||||
import { TrustedContact } from './TrustedContact'
|
||||
import { TrustedContactInterface } from './TrustedContactInterface'
|
||||
import { FillItemContentSpecialized } from '../../Abstract/Content/ItemContent'
|
||||
import { ConflictStrategy } from '../../Abstract/Item'
|
||||
import { TrustedContactContentSpecialized } from './Content/TrustedContactContent'
|
||||
import { PublicKeyTrustStatus } from './Types/PublicKeyTrustStatus'
|
||||
|
||||
function createMockPublicKeySetChain(): ContactPublicKeySetInterface {
|
||||
const nMinusOne = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key-n-1',
|
||||
signing: 'signing-public-key-n-1',
|
||||
timestamp: new Date(-1),
|
||||
previousKeySet: undefined,
|
||||
})
|
||||
|
||||
const root = new ContactPublicKeySet({
|
||||
encryption: 'encryption-public-key',
|
||||
signing: 'signing-public-key',
|
||||
timestamp: new Date(),
|
||||
previousKeySet: nMinusOne,
|
||||
})
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
function CreateContact(params: {
|
||||
name?: string
|
||||
contactUuid?: string
|
||||
isMe?: boolean
|
||||
publicKeySet?: ContactPublicKeySetInterface
|
||||
}): TrustedContactInterface {
|
||||
return new TrustedContact(
|
||||
new DecryptedPayload({
|
||||
uuid: 'item-uuid',
|
||||
content_type: ContentType.TYPES.TrustedContact,
|
||||
...PayloadTimestampDefaults(),
|
||||
content: FillItemContentSpecialized<TrustedContactContentSpecialized, TrustedContactInterface>({
|
||||
name: params.name ?? 'test',
|
||||
contactUuid: params.contactUuid ?? 'contact-uuid',
|
||||
isMe: params.isMe ?? false,
|
||||
publicKeySet: params.publicKeySet ?? createMockPublicKeySetChain(),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
describe('isSingleton', () => {
|
||||
it('should be true', () => {
|
||||
const contact = CreateContact({})
|
||||
|
||||
expect(contact.isSingleton).toEqual(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('strategyWhenConflictingWithItem', () => {
|
||||
it('should be KeepBase', () => {
|
||||
const contact = CreateContact({})
|
||||
|
||||
expect(contact.strategyWhenConflictingWithItem({} as unknown as TrustedContactInterface)).toEqual(
|
||||
ConflictStrategy.KeepBase,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('TrustedContact', () => {
|
||||
describe('getTrustStatusForPublicKey', () => {
|
||||
it('should be trusted if key set is root', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForPublicKey('encryption-public-key')).toEqual(PublicKeyTrustStatus.Trusted)
|
||||
})
|
||||
|
||||
it('should be semi-trusted if key set is previous', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForPublicKey('encryption-public-key-n-1')).toEqual(PublicKeyTrustStatus.Previous)
|
||||
})
|
||||
|
||||
it('should return not trusted if public key is not found', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForPublicKey('not-found-public-key')).toEqual(PublicKeyTrustStatus.NotTrusted)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getTrustStatusForSigningPublicKey', () => {
|
||||
it('should be trusted if key set is root', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForSigningPublicKey('signing-public-key')).toEqual(PublicKeyTrustStatus.Trusted)
|
||||
})
|
||||
|
||||
it('should be semi-trusted if key set is previous', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForSigningPublicKey('signing-public-key-n-1')).toEqual(PublicKeyTrustStatus.Previous)
|
||||
})
|
||||
|
||||
it('should return not trusted if public key is not found', () => {
|
||||
const contact = CreateContact({ publicKeySet: createMockPublicKeySetChain() })
|
||||
|
||||
expect(contact.getTrustStatusForSigningPublicKey('not-found-public-key')).toEqual(PublicKeyTrustStatus.NotTrusted)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ConflictStrategy, DecryptedItem, DecryptedItemInterface } from '../../Abstract/Item'
|
||||
import { DecryptedPayloadInterface } from '../../Abstract/Payload'
|
||||
import { HistoryEntryInterface } from '../../Runtime/History'
|
||||
import { TrustedContactContent } from './TrustedContactContent'
|
||||
import { TrustedContactContent } from './Content/TrustedContactContent'
|
||||
import { TrustedContactInterface } from './TrustedContactInterface'
|
||||
import { FindPublicKeySetResult } from './PublicKeySet/FindPublicKeySetResult'
|
||||
import { ContactPublicKeySet } from './PublicKeySet/ContactPublicKeySet'
|
||||
import { ContactPublicKeySetInterface } from './PublicKeySet/ContactPublicKeySetInterface'
|
||||
import { Predicate } from '../../Runtime/Predicate/Predicate'
|
||||
import { PublicKeyTrustStatus } from './Types/PublicKeyTrustStatus'
|
||||
|
||||
export class TrustedContact extends DecryptedItem<TrustedContactContent> implements TrustedContactInterface {
|
||||
static singletonPredicate = new Predicate<TrustedContact>('isMe', '=', true)
|
||||
@@ -33,39 +33,36 @@ export class TrustedContact extends DecryptedItem<TrustedContactContent> impleme
|
||||
return TrustedContact.singletonPredicate
|
||||
}
|
||||
|
||||
public findKeySet(params: {
|
||||
targetEncryptionPublicKey: string
|
||||
targetSigningPublicKey: string
|
||||
}): FindPublicKeySetResult {
|
||||
const set = this.publicKeySet.findKeySet(params)
|
||||
if (!set) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
publicKeySet: set,
|
||||
current: set === this.publicKeySet,
|
||||
}
|
||||
hasCurrentOrPreviousSigningPublicKey(signingPublicKey: string): boolean {
|
||||
return this.publicKeySet.findKeySetWithSigningKey(signingPublicKey) !== undefined
|
||||
}
|
||||
|
||||
isPublicKeyTrusted(encryptionPublicKey: string): boolean {
|
||||
const keySet = this.publicKeySet.findKeySetWithPublicKey(encryptionPublicKey)
|
||||
|
||||
if (keySet && !keySet.isRevoked) {
|
||||
return true
|
||||
getTrustStatusForPublicKey(encryptionPublicKey: string): PublicKeyTrustStatus {
|
||||
if (this.publicKeySet.encryption === encryptionPublicKey) {
|
||||
return PublicKeyTrustStatus.Trusted
|
||||
}
|
||||
|
||||
return false
|
||||
const previous = this.publicKeySet.findKeySetWithPublicKey(encryptionPublicKey)
|
||||
|
||||
if (previous) {
|
||||
return PublicKeyTrustStatus.Previous
|
||||
}
|
||||
|
||||
return PublicKeyTrustStatus.NotTrusted
|
||||
}
|
||||
|
||||
isSigningKeyTrusted(signingKey: string): boolean {
|
||||
const keySet = this.publicKeySet.findKeySetWithSigningKey(signingKey)
|
||||
|
||||
if (keySet && !keySet.isRevoked) {
|
||||
return true
|
||||
getTrustStatusForSigningPublicKey(signingPublicKey: string): PublicKeyTrustStatus {
|
||||
if (this.publicKeySet.signing === signingPublicKey) {
|
||||
return PublicKeyTrustStatus.Trusted
|
||||
}
|
||||
|
||||
return false
|
||||
const previous = this.publicKeySet.findKeySetWithSigningKey(signingPublicKey)
|
||||
|
||||
if (previous) {
|
||||
return PublicKeyTrustStatus.Previous
|
||||
}
|
||||
|
||||
return PublicKeyTrustStatus.NotTrusted
|
||||
}
|
||||
|
||||
override strategyWhenConflictingWithItem(
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { ItemContent } from '../../Abstract/Content/ItemContent'
|
||||
import { ContactPublicKeySetInterface } from './PublicKeySet/ContactPublicKeySetInterface'
|
||||
|
||||
export type TrustedContactContentSpecialized = {
|
||||
name: string
|
||||
contactUuid: string
|
||||
publicKeySet: ContactPublicKeySetInterface
|
||||
isMe: boolean
|
||||
}
|
||||
|
||||
export type TrustedContactContent = TrustedContactContentSpecialized & ItemContent
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DecryptedItemInterface } from '../../Abstract/Item/Interfaces/DecryptedItem'
|
||||
import { FindPublicKeySetResult } from './PublicKeySet/FindPublicKeySetResult'
|
||||
import { TrustedContactContent } from './TrustedContactContent'
|
||||
import { TrustedContactContent } from './Content/TrustedContactContent'
|
||||
import { ContactPublicKeySetInterface } from './PublicKeySet/ContactPublicKeySetInterface'
|
||||
import { PublicKeyTrustStatus } from './Types/PublicKeyTrustStatus'
|
||||
|
||||
export interface TrustedContactInterface extends DecryptedItemInterface<TrustedContactContent> {
|
||||
name: string
|
||||
@@ -9,8 +9,7 @@ export interface TrustedContactInterface extends DecryptedItemInterface<TrustedC
|
||||
publicKeySet: ContactPublicKeySetInterface
|
||||
isMe: boolean
|
||||
|
||||
findKeySet(params: { targetEncryptionPublicKey: string; targetSigningPublicKey: string }): FindPublicKeySetResult
|
||||
|
||||
isPublicKeyTrusted(encryptionPublicKey: string): boolean
|
||||
isSigningKeyTrusted(signingKey: string): boolean
|
||||
getTrustStatusForPublicKey(encryptionPublicKey: string): PublicKeyTrustStatus
|
||||
getTrustStatusForSigningPublicKey(signingPublicKey: string): PublicKeyTrustStatus
|
||||
hasCurrentOrPreviousSigningPublicKey(signingPublicKey: string): boolean
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { DecryptedItemMutator } from '../../Abstract/Item'
|
||||
import { TrustedContactContent } from './TrustedContactContent'
|
||||
import { TrustedContactInterface } from './TrustedContactInterface'
|
||||
import { ContactPublicKeySet } from './PublicKeySet/ContactPublicKeySet'
|
||||
|
||||
export class TrustedContactMutator extends DecryptedItemMutator<TrustedContactContent, TrustedContactInterface> {
|
||||
set name(newName: string) {
|
||||
this.mutableContent.name = newName
|
||||
}
|
||||
|
||||
addPublicKey(params: { encryption: string; signing: string }): void {
|
||||
const newKey = new ContactPublicKeySet(
|
||||
params.encryption,
|
||||
params.signing,
|
||||
new Date(),
|
||||
false,
|
||||
this.immutableItem.publicKeySet,
|
||||
)
|
||||
|
||||
this.mutableContent.publicKeySet = newKey
|
||||
}
|
||||
|
||||
replacePublicKeySet(publicKeySet: ContactPublicKeySet): void {
|
||||
this.mutableContent.publicKeySet = publicKeySet
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export type PublicKeySet = {
|
||||
export type PortablePublicKeySet = {
|
||||
encryption: string
|
||||
signing: string
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum PublicKeyTrustStatus {
|
||||
Trusted = 'Trusted',
|
||||
Previous = 'Previous',
|
||||
NotTrusted = 'NotTrusted',
|
||||
}
|
||||
@@ -27,7 +27,7 @@ import { EncryptedItemInterface } from '../../Abstract/Item/Interfaces/Encrypted
|
||||
import { DeletedItemInterface } from '../../Abstract/Item/Interfaces/DeletedItem'
|
||||
import { SmartViewMutator } from '../../Syncable/SmartView'
|
||||
import { TrustedContact } from '../../Syncable/TrustedContact/TrustedContact'
|
||||
import { TrustedContactMutator } from '../../Syncable/TrustedContact/TrustedContactMutator'
|
||||
import { TrustedContactMutator } from '../../Syncable/TrustedContact/Mutator/TrustedContactMutator'
|
||||
import { KeySystemRootKey } from '../../Syncable/KeySystemRootKey/KeySystemRootKey'
|
||||
import { KeySystemRootKeyMutator } from '../../Syncable/KeySystemRootKey/KeySystemRootKeyMutator'
|
||||
import { VaultListing } from '../../Syncable/VaultListing/VaultListing'
|
||||
|
||||
@@ -97,11 +97,13 @@ export * from './Syncable/Theme'
|
||||
export * from './Syncable/UserPrefs'
|
||||
|
||||
export * from './Syncable/TrustedContact/TrustedContact'
|
||||
export * from './Syncable/TrustedContact/TrustedContactMutator'
|
||||
export * from './Syncable/TrustedContact/TrustedContactContent'
|
||||
export * from './Syncable/TrustedContact/Mutator/TrustedContactMutator'
|
||||
export * from './Syncable/TrustedContact/Content/TrustedContactContent'
|
||||
export * from './Syncable/TrustedContact/TrustedContactInterface'
|
||||
export * from './Syncable/TrustedContact/PublicKeySet/ContactPublicKeySetInterface'
|
||||
export * from './Syncable/TrustedContact/PublicKeySet/ContactPublicKeySet'
|
||||
export * from './Syncable/TrustedContact/Types/PortablePublicKeySet'
|
||||
export * from './Syncable/TrustedContact/Types/PublicKeyTrustStatus'
|
||||
|
||||
export * from './Syncable/KeySystemRootKey/KeySystemRootKey'
|
||||
export * from './Syncable/KeySystemRootKey/KeySystemRootKeyMutator'
|
||||
|
||||
@@ -3,6 +3,50 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.396](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.395](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.394](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.393](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.392](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.391](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.390](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.389](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.388](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.387](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-21)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.386](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.4.385](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/releases",
|
||||
"version": "1.4.385",
|
||||
"version": "1.4.396",
|
||||
"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.31](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/responses
|
||||
|
||||
## [1.13.30](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/responses
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/responses",
|
||||
"version": "1.13.30",
|
||||
"version": "1.13.31",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ export interface AsymmetricMessageServerHash {
|
||||
uuid: string
|
||||
user_uuid: string
|
||||
sender_uuid: string
|
||||
replaceabilityIdentifier?: string
|
||||
encrypted_message: string
|
||||
created_at_timestamp: number
|
||||
updated_at_timestamp: number
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.63.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.63.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.63.7](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-24)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.63.6](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-23)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.63.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2023-07-17)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/services",
|
||||
"version": "1.63.5",
|
||||
"version": "1.63.9",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
+3
-1
@@ -6,7 +6,9 @@ import { SNFeatureRepo } from '@standardnotes/models'
|
||||
import { ClientDisplayableError, HttpResponse } from '@standardnotes/responses'
|
||||
import { AnyFeatureDescription } from '@standardnotes/features'
|
||||
|
||||
export interface ApiServiceInterface extends AbstractService<ApiServiceEvent, ApiServiceEventData>, FilesApiInterface {
|
||||
export interface LegacyApiServiceInterface
|
||||
extends AbstractService<ApiServiceEvent, ApiServiceEventData>,
|
||||
FilesApiInterface {
|
||||
isThirdPartyHostUsed(): boolean
|
||||
|
||||
downloadOfflineFeaturesFromRepo(
|
||||
@@ -1,9 +1,13 @@
|
||||
import { VaultUserServiceInterface, VaultInviteServiceInterface } from '@standardnotes/services'
|
||||
import { VaultLockServiceInterface } from './../VaultLock/VaultLockServiceInterface'
|
||||
import { HistoryServiceInterface } from './../History/HistoryServiceInterface'
|
||||
import { InternalEventBusInterface } from './../Internal/InternalEventBusInterface'
|
||||
import { PreferenceServiceInterface } from './../Preferences/PreferenceServiceInterface'
|
||||
import { AsymmetricMessageServiceInterface } from './../AsymmetricMessage/AsymmetricMessageServiceInterface'
|
||||
import { SyncOptions } from './../Sync/SyncOptions'
|
||||
import { ImportDataReturnType } from './../Mutator/ImportDataUseCase'
|
||||
import { ChallengeServiceInterface } from './../Challenge/ChallengeServiceInterface'
|
||||
import { VaultServiceInterface } from './../Vaults/VaultServiceInterface'
|
||||
import { VaultServiceInterface } from '../Vault/VaultServiceInterface'
|
||||
import { ApplicationIdentifier } from '@standardnotes/common'
|
||||
import {
|
||||
BackupFile,
|
||||
@@ -34,6 +38,7 @@ import { UserClientInterface } from '../User/UserClientInterface'
|
||||
import { SessionsClientInterface } from '../Session/SessionsClientInterface'
|
||||
import { HomeServerServiceInterface } from '../HomeServer/HomeServerServiceInterface'
|
||||
import { User } from '@standardnotes/responses'
|
||||
import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface'
|
||||
|
||||
export interface ApplicationInterface {
|
||||
deinit(mode: DeinitMode, source: DeinitSource): void
|
||||
@@ -92,24 +97,29 @@ export interface ApplicationInterface {
|
||||
syncOptions?: SyncOptions,
|
||||
): Promise<void>
|
||||
|
||||
get features(): FeaturesClientInterface
|
||||
get componentManager(): ComponentManagerInterface
|
||||
get items(): ItemManagerInterface
|
||||
get mutator(): MutatorClientInterface
|
||||
get user(): UserClientInterface
|
||||
get files(): FilesClientInterface
|
||||
get subscriptions(): SubscriptionManagerInterface
|
||||
get fileBackups(): BackupServiceInterface | undefined
|
||||
get sessions(): SessionsClientInterface
|
||||
get homeServer(): HomeServerServiceInterface | undefined
|
||||
get vaults(): VaultServiceInterface
|
||||
get challenges(): ChallengeServiceInterface
|
||||
get alerts(): AlertService
|
||||
get asymmetric(): AsymmetricMessageServiceInterface
|
||||
get challenges(): ChallengeServiceInterface
|
||||
get componentManager(): ComponentManagerInterface
|
||||
get encryption(): EncryptionProviderInterface
|
||||
get events(): InternalEventBusInterface
|
||||
get features(): FeaturesClientInterface
|
||||
get fileBackups(): BackupServiceInterface | undefined
|
||||
get files(): FilesClientInterface
|
||||
get history(): HistoryServiceInterface
|
||||
get homeServer(): HomeServerServiceInterface | undefined
|
||||
get items(): ItemManagerInterface
|
||||
get mutator(): MutatorClientInterface
|
||||
get preferences(): PreferenceServiceInterface
|
||||
get sessions(): SessionsClientInterface
|
||||
get subscriptions(): SubscriptionManagerInterface
|
||||
get user(): UserClientInterface
|
||||
get vaults(): VaultServiceInterface
|
||||
get vaultLocks(): VaultLockServiceInterface
|
||||
get vaultUsers(): VaultUserServiceInterface
|
||||
get vaultInvites(): VaultInviteServiceInterface
|
||||
|
||||
readonly identifier: ApplicationIdentifier
|
||||
readonly platform: Platform
|
||||
deviceInterface: DeviceInterface
|
||||
alertService: AlertService
|
||||
device: DeviceInterface
|
||||
}
|
||||
|
||||
@@ -1,31 +1,124 @@
|
||||
import { GetVault } from './../Vault/UseCase/GetVault'
|
||||
import { SessionsClientInterface } from './../Session/SessionsClientInterface'
|
||||
import { EncryptionProviderInterface } from './../Encryption/EncryptionProviderInterface'
|
||||
import { GetUntrustedPayload } from './UseCase/GetUntrustedPayload'
|
||||
import { GetInboundMessages } from './UseCase/GetInboundMessages'
|
||||
import { GetOutboundMessages } from './UseCase/GetOutboundMessages'
|
||||
import { SendOwnContactChangeMessage } from './UseCase/SendOwnContactChangeMessage'
|
||||
import { HandleRootKeyChangedMessage } from './UseCase/HandleRootKeyChangedMessage'
|
||||
import { GetTrustedPayload } from './UseCase/GetTrustedPayload'
|
||||
import { ReplaceContactData } from './../Contacts/UseCase/ReplaceContactData'
|
||||
import { GetAllContacts } from './../Contacts/UseCase/GetAllContacts'
|
||||
import { FindContact } from './../Contacts/UseCase/FindContact'
|
||||
import { CreateOrEditContact } from './../Contacts/UseCase/CreateOrEditContact'
|
||||
import { MutatorClientInterface } from './../Mutator/MutatorClientInterface'
|
||||
import { HttpServiceInterface } from '@standardnotes/api'
|
||||
import { AsymmetricMessageServer } from '@standardnotes/api'
|
||||
import { AsymmetricMessageService } from './AsymmetricMessageService'
|
||||
import { ContactServiceInterface } from './../Contacts/ContactServiceInterface'
|
||||
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
|
||||
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessagePayloadType } from '@standardnotes/models'
|
||||
import {
|
||||
AsymmetricMessagePayloadType,
|
||||
AsymmetricMessageSenderKeypairChanged,
|
||||
AsymmetricMessageSharedVaultInvite,
|
||||
AsymmetricMessageSharedVaultMetadataChanged,
|
||||
AsymmetricMessageSharedVaultRootKeyChanged,
|
||||
AsymmetricMessageTrustedContactShare,
|
||||
KeySystemRootKeyContentSpecialized,
|
||||
TrustedContactInterface,
|
||||
} from '@standardnotes/models'
|
||||
|
||||
describe('AsymmetricMessageService', () => {
|
||||
let sync: jest.Mocked<SyncServiceInterface>
|
||||
let mutator: jest.Mocked<MutatorClientInterface>
|
||||
let encryption: jest.Mocked<EncryptionProviderInterface>
|
||||
let sessions: jest.Mocked<SessionsClientInterface>
|
||||
let service: AsymmetricMessageService
|
||||
|
||||
beforeEach(() => {
|
||||
const http = {} as jest.Mocked<HttpServiceInterface>
|
||||
http.delete = jest.fn()
|
||||
const messageServer = {} as jest.Mocked<AsymmetricMessageServer>
|
||||
messageServer.deleteMessage = jest.fn()
|
||||
|
||||
const encryption = {} as jest.Mocked<EncryptionProviderInterface>
|
||||
const contacts = {} as jest.Mocked<ContactServiceInterface>
|
||||
const items = {} as jest.Mocked<ItemManagerInterface>
|
||||
const sync = {} as jest.Mocked<SyncServiceInterface>
|
||||
const mutator = {} as jest.Mocked<MutatorClientInterface>
|
||||
encryption = {} as jest.Mocked<EncryptionProviderInterface>
|
||||
const createOrEditContact = {} as jest.Mocked<CreateOrEditContact>
|
||||
const findContact = {} as jest.Mocked<FindContact>
|
||||
const getAllContacts = {} as jest.Mocked<GetAllContacts>
|
||||
const replaceContactData = {} as jest.Mocked<ReplaceContactData>
|
||||
const getTrustedPayload = {} as jest.Mocked<GetTrustedPayload>
|
||||
const getVault = {} as jest.Mocked<GetVault>
|
||||
const handleRootKeyChangedMessage = {} as jest.Mocked<HandleRootKeyChangedMessage>
|
||||
const sendOwnContactChangedMessage = {} as jest.Mocked<SendOwnContactChangeMessage>
|
||||
const getOutboundMessagesUseCase = {} as jest.Mocked<GetOutboundMessages>
|
||||
const getInboundMessagesUseCase = {} as jest.Mocked<GetInboundMessages>
|
||||
const getUntrustedPayload = {} as jest.Mocked<GetUntrustedPayload>
|
||||
|
||||
sync = {} as jest.Mocked<SyncServiceInterface>
|
||||
sync.sync = jest.fn()
|
||||
|
||||
mutator = {} as jest.Mocked<MutatorClientInterface>
|
||||
mutator.changeItem = jest.fn()
|
||||
|
||||
const eventBus = {} as jest.Mocked<InternalEventBusInterface>
|
||||
eventBus.addEventHandler = jest.fn()
|
||||
|
||||
service = new AsymmetricMessageService(http, encryption, contacts, items, mutator, sync, eventBus)
|
||||
service = new AsymmetricMessageService(
|
||||
encryption,
|
||||
mutator,
|
||||
sessions,
|
||||
messageServer,
|
||||
createOrEditContact,
|
||||
findContact,
|
||||
getAllContacts,
|
||||
replaceContactData,
|
||||
getTrustedPayload,
|
||||
getVault,
|
||||
handleRootKeyChangedMessage,
|
||||
sendOwnContactChangedMessage,
|
||||
getOutboundMessagesUseCase,
|
||||
getInboundMessagesUseCase,
|
||||
getUntrustedPayload,
|
||||
eventBus,
|
||||
)
|
||||
})
|
||||
|
||||
describe('sortServerMessages', () => {
|
||||
it('should prioritize keypair changed messages over other messages', () => {
|
||||
const messages: AsymmetricMessageServerHash[] = [
|
||||
{
|
||||
uuid: 'keypair-changed-message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
updated_at_timestamp: 2,
|
||||
},
|
||||
{
|
||||
uuid: 'misc-message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 1,
|
||||
updated_at_timestamp: 1,
|
||||
},
|
||||
]
|
||||
|
||||
service.getUntrustedMessagePayload = jest.fn()
|
||||
service.getServerMessageType = jest.fn().mockImplementation((message) => {
|
||||
if (message.uuid === 'keypair-changed-message') {
|
||||
return AsymmetricMessagePayloadType.SenderKeypairChanged
|
||||
} else {
|
||||
return AsymmetricMessagePayloadType.ContactShare
|
||||
}
|
||||
})
|
||||
|
||||
const sorted = service.sortServerMessages(messages)
|
||||
expect(sorted[0].uuid).toEqual('keypair-changed-message')
|
||||
expect(sorted[1].uuid).toEqual('misc-message')
|
||||
|
||||
const reverseSorted = service.sortServerMessages(messages.reverse())
|
||||
expect(reverseSorted[0].uuid).toEqual('keypair-changed-message')
|
||||
expect(reverseSorted[1].uuid).toEqual('misc-message')
|
||||
})
|
||||
})
|
||||
|
||||
it('should process incoming messages oldest first', async () => {
|
||||
@@ -50,7 +143,9 @@ describe('AsymmetricMessageService', () => {
|
||||
|
||||
const trustedPayloadMock = { type: AsymmetricMessagePayloadType.ContactShare, data: { recipientUuid: '1' } }
|
||||
|
||||
service.getTrustedMessagePayload = jest.fn().mockReturnValue(trustedPayloadMock)
|
||||
service.getTrustedMessagePayload = service.getUntrustedMessagePayload = jest
|
||||
.fn()
|
||||
.mockReturnValue(trustedPayloadMock)
|
||||
|
||||
const handleTrustedContactShareMessageMock = jest.fn()
|
||||
service.handleTrustedContactShareMessage = handleTrustedContactShareMessageMock
|
||||
@@ -60,4 +155,174 @@ describe('AsymmetricMessageService', () => {
|
||||
expect(handleTrustedContactShareMessageMock.mock.calls[0][0]).toEqual(messages[1])
|
||||
expect(handleTrustedContactShareMessageMock.mock.calls[1][0]).toEqual(messages[0])
|
||||
})
|
||||
|
||||
it('should handle ContactShare message', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
updated_at_timestamp: 2,
|
||||
}
|
||||
|
||||
const decryptedMessagePayload: AsymmetricMessageTrustedContactShare = {
|
||||
type: AsymmetricMessagePayloadType.ContactShare,
|
||||
data: {
|
||||
recipientUuid: '1',
|
||||
trustedContact: {} as TrustedContactInterface,
|
||||
},
|
||||
}
|
||||
|
||||
service.handleTrustedContactShareMessage = jest.fn()
|
||||
service.getTrustedMessagePayload = service.getUntrustedMessagePayload = jest
|
||||
.fn()
|
||||
.mockReturnValue(decryptedMessagePayload)
|
||||
|
||||
await service.handleRemoteReceivedAsymmetricMessages([message])
|
||||
|
||||
expect(service.handleTrustedContactShareMessage).toHaveBeenCalledWith(message, decryptedMessagePayload)
|
||||
})
|
||||
|
||||
it('should handle SenderKeypairChanged message', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
updated_at_timestamp: 2,
|
||||
}
|
||||
|
||||
const decryptedMessagePayload: AsymmetricMessageSenderKeypairChanged = {
|
||||
type: AsymmetricMessagePayloadType.SenderKeypairChanged,
|
||||
data: {
|
||||
recipientUuid: '1',
|
||||
newEncryptionPublicKey: 'new-encryption-public-key',
|
||||
newSigningPublicKey: 'new-signing-public-key',
|
||||
},
|
||||
}
|
||||
|
||||
service.handleTrustedSenderKeypairChangedMessage = jest.fn()
|
||||
service.getTrustedMessagePayload = service.getUntrustedMessagePayload = jest
|
||||
.fn()
|
||||
.mockReturnValue(decryptedMessagePayload)
|
||||
|
||||
await service.handleRemoteReceivedAsymmetricMessages([message])
|
||||
|
||||
expect(service.handleTrustedSenderKeypairChangedMessage).toHaveBeenCalledWith(message, decryptedMessagePayload)
|
||||
})
|
||||
|
||||
it('should handle SharedVaultRootKeyChanged message', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
updated_at_timestamp: 2,
|
||||
}
|
||||
|
||||
const decryptedMessagePayload: AsymmetricMessageSharedVaultRootKeyChanged = {
|
||||
type: AsymmetricMessagePayloadType.SharedVaultRootKeyChanged,
|
||||
data: {
|
||||
recipientUuid: '1',
|
||||
rootKey: {} as KeySystemRootKeyContentSpecialized,
|
||||
},
|
||||
}
|
||||
|
||||
service.handleTrustedSharedVaultRootKeyChangedMessage = jest.fn()
|
||||
service.getTrustedMessagePayload = service.getUntrustedMessagePayload = jest
|
||||
.fn()
|
||||
.mockReturnValue(decryptedMessagePayload)
|
||||
|
||||
await service.handleRemoteReceivedAsymmetricMessages([message])
|
||||
|
||||
expect(service.handleTrustedSharedVaultRootKeyChangedMessage).toHaveBeenCalledWith(message, decryptedMessagePayload)
|
||||
})
|
||||
|
||||
it('should handle SharedVaultMetadataChanged message', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
updated_at_timestamp: 2,
|
||||
}
|
||||
|
||||
const decryptedMessagePayload: AsymmetricMessageSharedVaultMetadataChanged = {
|
||||
type: AsymmetricMessagePayloadType.SharedVaultMetadataChanged,
|
||||
data: {
|
||||
recipientUuid: '1',
|
||||
sharedVaultUuid: 'shared-vault-uuid',
|
||||
name: 'Vault name',
|
||||
description: 'Vault description',
|
||||
},
|
||||
}
|
||||
|
||||
service.handleTrustedVaultMetadataChangedMessage = jest.fn()
|
||||
service.getTrustedMessagePayload = service.getUntrustedMessagePayload = jest
|
||||
.fn()
|
||||
.mockReturnValue(decryptedMessagePayload)
|
||||
|
||||
await service.handleRemoteReceivedAsymmetricMessages([message])
|
||||
|
||||
expect(service.handleTrustedVaultMetadataChangedMessage).toHaveBeenCalledWith(message, decryptedMessagePayload)
|
||||
})
|
||||
|
||||
it('should throw if message type is SharedVaultInvite', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
updated_at_timestamp: 2,
|
||||
}
|
||||
|
||||
const decryptedMessagePayload: AsymmetricMessageSharedVaultInvite = {
|
||||
type: AsymmetricMessagePayloadType.SharedVaultInvite,
|
||||
data: {
|
||||
recipientUuid: '1',
|
||||
},
|
||||
} as AsymmetricMessageSharedVaultInvite
|
||||
|
||||
service.getTrustedMessagePayload = service.getUntrustedMessagePayload = jest
|
||||
.fn()
|
||||
.mockReturnValue(decryptedMessagePayload)
|
||||
|
||||
await expect(service.handleRemoteReceivedAsymmetricMessages([message])).rejects.toThrow(
|
||||
'Shared vault invites payloads are not handled as part of asymmetric messages',
|
||||
)
|
||||
})
|
||||
|
||||
it('should delete message from server after processing', async () => {
|
||||
const message: AsymmetricMessageServerHash = {
|
||||
uuid: 'message',
|
||||
user_uuid: '1',
|
||||
sender_uuid: '2',
|
||||
encrypted_message: 'encrypted_message',
|
||||
created_at_timestamp: 2,
|
||||
updated_at_timestamp: 2,
|
||||
}
|
||||
|
||||
const decryptedMessagePayload: AsymmetricMessageTrustedContactShare = {
|
||||
type: AsymmetricMessagePayloadType.ContactShare,
|
||||
data: {
|
||||
recipientUuid: '1',
|
||||
trustedContact: {} as TrustedContactInterface,
|
||||
},
|
||||
}
|
||||
|
||||
service.deleteMessageAfterProcessing = jest.fn()
|
||||
service.handleTrustedContactShareMessage = jest.fn()
|
||||
service.getTrustedMessagePayload = service.getUntrustedMessagePayload = jest
|
||||
.fn()
|
||||
.mockReturnValue(decryptedMessagePayload)
|
||||
|
||||
await service.handleRemoteReceivedAsymmetricMessages([message])
|
||||
|
||||
expect(service.deleteMessageAfterProcessing).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { SessionsClientInterface } from './../Session/SessionsClientInterface'
|
||||
import { MutatorClientInterface } from './../Mutator/MutatorClientInterface'
|
||||
import { ContactServiceInterface } from './../Contacts/ContactServiceInterface'
|
||||
import { AsymmetricMessageServerHash, ClientDisplayableError, isClientDisplayableError } from '@standardnotes/responses'
|
||||
import { SyncEvent, SyncEventReceivedAsymmetricMessagesData } from '../Event/SyncEvent'
|
||||
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||
import { InternalEventHandlerInterface } from '../Internal/InternalEventHandlerInterface'
|
||||
import { InternalEventInterface } from '../Internal/InternalEventInterface'
|
||||
import { AbstractService } from '../Service/AbstractService'
|
||||
import { GetAsymmetricMessageTrustedPayload } from './UseCase/GetAsymmetricMessageTrustedPayload'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { GetTrustedPayload } from './UseCase/GetTrustedPayload'
|
||||
import {
|
||||
AsymmetricMessageSharedVaultRootKeyChanged,
|
||||
AsymmetricMessagePayloadType,
|
||||
@@ -16,61 +15,69 @@ import {
|
||||
AsymmetricMessagePayload,
|
||||
AsymmetricMessageSharedVaultMetadataChanged,
|
||||
VaultListingMutator,
|
||||
MutationType,
|
||||
PayloadEmitSource,
|
||||
VaultListingInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { HandleTrustedSharedVaultRootKeyChangedMessage } from './UseCase/HandleTrustedSharedVaultRootKeyChangedMessage'
|
||||
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
|
||||
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
|
||||
import { HandleRootKeyChangedMessage } from './UseCase/HandleRootKeyChangedMessage'
|
||||
import { SessionEvent } from '../Session/SessionEvent'
|
||||
import { AsymmetricMessageServer, HttpServiceInterface } from '@standardnotes/api'
|
||||
import { AsymmetricMessageServer } from '@standardnotes/api'
|
||||
import { UserKeyPairChangedEventData } from '../Session/UserKeyPairChangedEventData'
|
||||
import { SendOwnContactChangeMessage } from './UseCase/SendOwnContactChangeMessage'
|
||||
import { GetOutboundAsymmetricMessages } from './UseCase/GetOutboundAsymmetricMessages'
|
||||
import { GetInboundAsymmetricMessages } from './UseCase/GetInboundAsymmetricMessages'
|
||||
import { GetVaultUseCase } from '../Vaults/UseCase/GetVault'
|
||||
import { GetOutboundMessages } from './UseCase/GetOutboundMessages'
|
||||
import { GetInboundMessages } from './UseCase/GetInboundMessages'
|
||||
import { GetVault } from '../Vault/UseCase/GetVault'
|
||||
import { AsymmetricMessageServiceInterface } from './AsymmetricMessageServiceInterface'
|
||||
import { GetUntrustedPayload } from './UseCase/GetUntrustedPayload'
|
||||
import { FindContact } from '../Contacts/UseCase/FindContact'
|
||||
import { CreateOrEditContact } from '../Contacts/UseCase/CreateOrEditContact'
|
||||
import { ReplaceContactData } from '../Contacts/UseCase/ReplaceContactData'
|
||||
import { GetAllContacts } from '../Contacts/UseCase/GetAllContacts'
|
||||
import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface'
|
||||
|
||||
export class AsymmetricMessageService
|
||||
extends AbstractService
|
||||
implements AsymmetricMessageServiceInterface, InternalEventHandlerInterface
|
||||
{
|
||||
private messageServer: AsymmetricMessageServer
|
||||
|
||||
constructor(
|
||||
http: HttpServiceInterface,
|
||||
private encryption: EncryptionProviderInterface,
|
||||
private contacts: ContactServiceInterface,
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private sessions: SessionsClientInterface,
|
||||
private messageServer: AsymmetricMessageServer,
|
||||
private _createOrEditContact: CreateOrEditContact,
|
||||
private _findContact: FindContact,
|
||||
private _getAllContacts: GetAllContacts,
|
||||
private _replaceContactData: ReplaceContactData,
|
||||
private _getTrustedPayload: GetTrustedPayload,
|
||||
private _getVault: GetVault,
|
||||
private _handleRootKeyChangedMessage: HandleRootKeyChangedMessage,
|
||||
private _sendOwnContactChangedMessage: SendOwnContactChangeMessage,
|
||||
private _getOutboundMessagesUseCase: GetOutboundMessages,
|
||||
private _getInboundMessagesUseCase: GetInboundMessages,
|
||||
private _getUntrustedPayload: GetUntrustedPayload,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(eventBus)
|
||||
|
||||
this.messageServer = new AsymmetricMessageServer(http)
|
||||
|
||||
eventBus.addEventHandler(this, SyncEvent.ReceivedAsymmetricMessages)
|
||||
eventBus.addEventHandler(this, SessionEvent.UserKeyPairChanged)
|
||||
}
|
||||
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === SessionEvent.UserKeyPairChanged) {
|
||||
void this.messageServer.deleteAllInboundMessages()
|
||||
void this.sendOwnContactChangeEventToAllContacts(event.payload as UserKeyPairChangedEventData)
|
||||
}
|
||||
|
||||
if (event.type === SyncEvent.ReceivedAsymmetricMessages) {
|
||||
void this.handleRemoteReceivedAsymmetricMessages(event.payload as SyncEventReceivedAsymmetricMessagesData)
|
||||
switch (event.type) {
|
||||
case SessionEvent.UserKeyPairChanged:
|
||||
void this.messageServer.deleteAllInboundMessages()
|
||||
void this.sendOwnContactChangeEventToAllContacts(event.payload as UserKeyPairChangedEventData)
|
||||
break
|
||||
case SyncEvent.ReceivedAsymmetricMessages:
|
||||
void this.handleRemoteReceivedAsymmetricMessages(event.payload as SyncEventReceivedAsymmetricMessagesData)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public async getOutboundMessages(): Promise<AsymmetricMessageServerHash[] | ClientDisplayableError> {
|
||||
const usecase = new GetOutboundAsymmetricMessages(this.messageServer)
|
||||
return usecase.execute()
|
||||
return this._getOutboundMessagesUseCase.execute()
|
||||
}
|
||||
|
||||
public async getInboundMessages(): Promise<AsymmetricMessageServerHash[] | ClientDisplayableError> {
|
||||
const usecase = new GetInboundAsymmetricMessages(this.messageServer)
|
||||
return usecase.execute()
|
||||
return this._getInboundMessagesUseCase.execute()
|
||||
}
|
||||
|
||||
public async downloadAndProcessInboundMessages(): Promise<void> {
|
||||
@@ -83,118 +90,220 @@ export class AsymmetricMessageService
|
||||
}
|
||||
|
||||
private async sendOwnContactChangeEventToAllContacts(data: UserKeyPairChangedEventData): Promise<void> {
|
||||
if (!data.oldKeyPair || !data.oldSigningKeyPair) {
|
||||
if (!data.previous) {
|
||||
return
|
||||
}
|
||||
|
||||
const useCase = new SendOwnContactChangeMessage(this.encryption, this.messageServer)
|
||||
const contacts = this._getAllContacts.execute()
|
||||
if (contacts.isFailed()) {
|
||||
return
|
||||
}
|
||||
|
||||
const contacts = this.contacts.getAllContacts()
|
||||
|
||||
for (const contact of contacts) {
|
||||
for (const contact of contacts.getValue()) {
|
||||
if (contact.isMe) {
|
||||
continue
|
||||
}
|
||||
|
||||
await useCase.execute({
|
||||
senderOldKeyPair: data.oldKeyPair,
|
||||
senderOldSigningKeyPair: data.oldSigningKeyPair,
|
||||
senderNewKeyPair: data.newKeyPair,
|
||||
senderNewSigningKeyPair: data.newSigningKeyPair,
|
||||
await this._sendOwnContactChangedMessage.execute({
|
||||
senderOldKeyPair: data.previous.encryption,
|
||||
senderOldSigningKeyPair: data.previous.signing,
|
||||
senderNewKeyPair: data.current.encryption,
|
||||
senderNewSigningKeyPair: data.current.signing,
|
||||
contact,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sortServerMessages(messages: AsymmetricMessageServerHash[]): AsymmetricMessageServerHash[] {
|
||||
const SortedPriorityTypes = [AsymmetricMessagePayloadType.SenderKeypairChanged]
|
||||
|
||||
const priority: AsymmetricMessageServerHash[] = []
|
||||
const regular: AsymmetricMessageServerHash[] = []
|
||||
|
||||
const allMessagesOldestFirst = messages.slice().sort((a, b) => a.created_at_timestamp - b.created_at_timestamp)
|
||||
|
||||
const messageTypeMap: Record<string, AsymmetricMessagePayloadType> = {}
|
||||
|
||||
for (const message of allMessagesOldestFirst) {
|
||||
const messageType = this.getServerMessageType(message)
|
||||
if (!messageType) {
|
||||
continue
|
||||
}
|
||||
|
||||
messageTypeMap[message.uuid] = messageType
|
||||
|
||||
if (SortedPriorityTypes.includes(messageType)) {
|
||||
priority.push(message)
|
||||
} else {
|
||||
regular.push(message)
|
||||
}
|
||||
}
|
||||
|
||||
const sortedPriority = priority.sort((a, b) => {
|
||||
const typeA = messageTypeMap[a.uuid]
|
||||
const typeB = messageTypeMap[b.uuid]
|
||||
|
||||
if (typeA !== typeB) {
|
||||
return SortedPriorityTypes.indexOf(typeA) - SortedPriorityTypes.indexOf(typeB)
|
||||
}
|
||||
|
||||
return a.created_at_timestamp - b.created_at_timestamp
|
||||
})
|
||||
|
||||
const regularMessagesOldestFirst = regular.sort((a, b) => a.created_at_timestamp - b.created_at_timestamp)
|
||||
|
||||
return [...sortedPriority, ...regularMessagesOldestFirst]
|
||||
}
|
||||
|
||||
getServerMessageType(message: AsymmetricMessageServerHash): AsymmetricMessagePayloadType | undefined {
|
||||
const result = this.getUntrustedMessagePayload(message)
|
||||
|
||||
if (!result) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return result.type
|
||||
}
|
||||
|
||||
async handleRemoteReceivedAsymmetricMessages(messages: AsymmetricMessageServerHash[]): Promise<void> {
|
||||
if (messages.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const sortedMessages = messages.slice().sort((a, b) => a.created_at_timestamp - b.created_at_timestamp)
|
||||
const sortedMessages = this.sortServerMessages(messages)
|
||||
|
||||
for (const message of sortedMessages) {
|
||||
const trustedMessagePayload = this.getTrustedMessagePayload(message)
|
||||
if (!trustedMessagePayload) {
|
||||
const trustedPayload = this.getTrustedMessagePayload(message)
|
||||
if (!trustedPayload) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (trustedMessagePayload.data.recipientUuid !== message.user_uuid) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (trustedMessagePayload.type === AsymmetricMessagePayloadType.ContactShare) {
|
||||
await this.handleTrustedContactShareMessage(message, trustedMessagePayload)
|
||||
} else if (trustedMessagePayload.type === AsymmetricMessagePayloadType.SenderKeypairChanged) {
|
||||
await this.handleTrustedSenderKeypairChangedMessage(message, trustedMessagePayload)
|
||||
} else if (trustedMessagePayload.type === AsymmetricMessagePayloadType.SharedVaultRootKeyChanged) {
|
||||
await this.handleTrustedSharedVaultRootKeyChangedMessage(message, trustedMessagePayload)
|
||||
} else if (trustedMessagePayload.type === AsymmetricMessagePayloadType.SharedVaultMetadataChanged) {
|
||||
await this.handleVaultMetadataChangedMessage(message, trustedMessagePayload)
|
||||
} else if (trustedMessagePayload.type === AsymmetricMessagePayloadType.SharedVaultInvite) {
|
||||
throw new Error('Shared vault invites payloads are not handled as part of asymmetric messages')
|
||||
}
|
||||
|
||||
await this.deleteMessageAfterProcessing(message)
|
||||
await this.handleTrustedMessageResult(message, trustedPayload)
|
||||
}
|
||||
}
|
||||
|
||||
getTrustedMessagePayload(message: AsymmetricMessageServerHash): AsymmetricMessagePayload | undefined {
|
||||
const useCase = new GetAsymmetricMessageTrustedPayload(this.encryption, this.contacts)
|
||||
private async handleTrustedMessageResult(
|
||||
message: AsymmetricMessageServerHash,
|
||||
payload: AsymmetricMessagePayload,
|
||||
): Promise<void> {
|
||||
if (payload.type === AsymmetricMessagePayloadType.ContactShare) {
|
||||
await this.handleTrustedContactShareMessage(message, payload)
|
||||
} else if (payload.type === AsymmetricMessagePayloadType.SenderKeypairChanged) {
|
||||
await this.handleTrustedSenderKeypairChangedMessage(message, payload)
|
||||
} else if (payload.type === AsymmetricMessagePayloadType.SharedVaultRootKeyChanged) {
|
||||
await this.handleTrustedSharedVaultRootKeyChangedMessage(message, payload)
|
||||
} else if (payload.type === AsymmetricMessagePayloadType.SharedVaultMetadataChanged) {
|
||||
await this.handleTrustedVaultMetadataChangedMessage(message, payload)
|
||||
} else if (payload.type === AsymmetricMessagePayloadType.SharedVaultInvite) {
|
||||
throw new Error('Shared vault invites payloads are not handled as part of asymmetric messages')
|
||||
}
|
||||
|
||||
return useCase.execute({
|
||||
await this.deleteMessageAfterProcessing(message)
|
||||
}
|
||||
|
||||
getUntrustedMessagePayload(message: AsymmetricMessageServerHash): AsymmetricMessagePayload | undefined {
|
||||
const result = this._getUntrustedPayload.execute({
|
||||
privateKey: this.encryption.getKeyPair().privateKey,
|
||||
message,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return result.getValue()
|
||||
}
|
||||
|
||||
private async deleteMessageAfterProcessing(message: AsymmetricMessageServerHash): Promise<void> {
|
||||
getTrustedMessagePayload(message: AsymmetricMessageServerHash): AsymmetricMessagePayload | undefined {
|
||||
const contact = this._findContact.execute({ userUuid: message.sender_uuid })
|
||||
if (contact.isFailed()) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const result = this._getTrustedPayload.execute({
|
||||
privateKey: this.encryption.getKeyPair().privateKey,
|
||||
sender: contact.getValue(),
|
||||
ownUserUuid: this.sessions.userUuid,
|
||||
message,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return result.getValue()
|
||||
}
|
||||
|
||||
async deleteMessageAfterProcessing(message: AsymmetricMessageServerHash): Promise<void> {
|
||||
await this.messageServer.deleteMessage({ messageUuid: message.uuid })
|
||||
}
|
||||
|
||||
async handleVaultMetadataChangedMessage(
|
||||
async handleTrustedVaultMetadataChangedMessage(
|
||||
_message: AsymmetricMessageServerHash,
|
||||
trustedPayload: AsymmetricMessageSharedVaultMetadataChanged,
|
||||
): Promise<void> {
|
||||
const vault = new GetVaultUseCase(this.items).execute({ sharedVaultUuid: trustedPayload.data.sharedVaultUuid })
|
||||
if (!vault) {
|
||||
const vault = this._getVault.execute<VaultListingInterface>({
|
||||
sharedVaultUuid: trustedPayload.data.sharedVaultUuid,
|
||||
})
|
||||
if (vault.isFailed()) {
|
||||
return
|
||||
}
|
||||
|
||||
await this.mutator.changeItem<VaultListingMutator>(vault, (mutator) => {
|
||||
mutator.name = trustedPayload.data.name
|
||||
mutator.description = trustedPayload.data.description
|
||||
})
|
||||
await this.mutator.changeItem<VaultListingMutator>(
|
||||
vault.getValue(),
|
||||
(mutator) => {
|
||||
mutator.name = trustedPayload.data.name
|
||||
mutator.description = trustedPayload.data.description
|
||||
},
|
||||
MutationType.UpdateUserTimestamps,
|
||||
PayloadEmitSource.RemoteRetrieved,
|
||||
)
|
||||
}
|
||||
|
||||
async handleTrustedContactShareMessage(
|
||||
_message: AsymmetricMessageServerHash,
|
||||
trustedPayload: AsymmetricMessageTrustedContactShare,
|
||||
): Promise<void> {
|
||||
await this.contacts.createOrUpdateTrustedContactFromContactShare(trustedPayload.data.trustedContact)
|
||||
if (trustedPayload.data.trustedContact.isMe) {
|
||||
return
|
||||
}
|
||||
|
||||
await this._replaceContactData.execute(trustedPayload.data.trustedContact)
|
||||
}
|
||||
|
||||
private async handleTrustedSenderKeypairChangedMessage(
|
||||
async handleTrustedSenderKeypairChangedMessage(
|
||||
message: AsymmetricMessageServerHash,
|
||||
trustedPayload: AsymmetricMessageSenderKeypairChanged,
|
||||
): Promise<void> {
|
||||
await this.contacts.createOrEditTrustedContact({
|
||||
await this._createOrEditContact.execute({
|
||||
contactUuid: message.sender_uuid,
|
||||
publicKey: trustedPayload.data.newEncryptionPublicKey,
|
||||
signingPublicKey: trustedPayload.data.newSigningPublicKey,
|
||||
})
|
||||
}
|
||||
|
||||
private async handleTrustedSharedVaultRootKeyChangedMessage(
|
||||
async handleTrustedSharedVaultRootKeyChangedMessage(
|
||||
_message: AsymmetricMessageServerHash,
|
||||
trustedPayload: AsymmetricMessageSharedVaultRootKeyChanged,
|
||||
): Promise<void> {
|
||||
const useCase = new HandleTrustedSharedVaultRootKeyChangedMessage(
|
||||
this.mutator,
|
||||
this.items,
|
||||
this.sync,
|
||||
this.encryption,
|
||||
)
|
||||
await useCase.execute(trustedPayload)
|
||||
await this._handleRootKeyChangedMessage.execute(trustedPayload)
|
||||
}
|
||||
|
||||
public override deinit(): void {
|
||||
super.deinit()
|
||||
;(this.messageServer as unknown) = undefined
|
||||
;(this.encryption as unknown) = undefined
|
||||
;(this.mutator as unknown) = undefined
|
||||
;(this._createOrEditContact as unknown) = undefined
|
||||
;(this._findContact as unknown) = undefined
|
||||
;(this._getAllContacts as unknown) = undefined
|
||||
;(this._replaceContactData as unknown) = undefined
|
||||
;(this._getTrustedPayload as unknown) = undefined
|
||||
;(this._getVault as unknown) = undefined
|
||||
;(this._handleRootKeyChangedMessage as unknown) = undefined
|
||||
;(this._sendOwnContactChangedMessage as unknown) = undefined
|
||||
;(this._getOutboundMessagesUseCase as unknown) = undefined
|
||||
;(this._getInboundMessagesUseCase as unknown) = undefined
|
||||
;(this._getUntrustedPayload as unknown) = undefined
|
||||
}
|
||||
}
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { ContactServiceInterface } from '../../Contacts/ContactServiceInterface'
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessagePayload } from '@standardnotes/models'
|
||||
|
||||
export class GetAsymmetricMessageTrustedPayload<M extends AsymmetricMessagePayload> {
|
||||
constructor(private encryption: EncryptionProviderInterface, private contacts: ContactServiceInterface) {}
|
||||
|
||||
execute(dto: { privateKey: string; message: AsymmetricMessageServerHash }): M | undefined {
|
||||
const trustedContact = this.contacts.findTrustedContact(dto.message.sender_uuid)
|
||||
if (!trustedContact) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const decryptionResult = this.encryption.asymmetricallyDecryptMessage<M>({
|
||||
encryptedString: dto.message.encrypted_message,
|
||||
trustedSender: trustedContact,
|
||||
privateKey: dto.privateKey,
|
||||
})
|
||||
|
||||
return decryptionResult
|
||||
}
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessagePayload } from '@standardnotes/models'
|
||||
|
||||
export class GetAsymmetricMessageUntrustedPayload<M extends AsymmetricMessagePayload> {
|
||||
constructor(private encryption: EncryptionProviderInterface) {}
|
||||
|
||||
execute(dto: { privateKey: string; message: AsymmetricMessageServerHash }): M | undefined {
|
||||
const decryptionResult = this.encryption.asymmetricallyDecryptMessage<M>({
|
||||
encryptedString: dto.message.encrypted_message,
|
||||
trustedSender: undefined,
|
||||
privateKey: dto.privateKey,
|
||||
})
|
||||
|
||||
return decryptionResult
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { ClientDisplayableError, isErrorResponse, AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessageServerInterface } from '@standardnotes/api'
|
||||
|
||||
export class GetInboundAsymmetricMessages {
|
||||
export class GetInboundMessages {
|
||||
constructor(private messageServer: AsymmetricMessageServerInterface) {}
|
||||
|
||||
async execute(): Promise<AsymmetricMessageServerHash[] | ClientDisplayableError> {
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { ClientDisplayableError, isErrorResponse, AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessageServerInterface } from '@standardnotes/api'
|
||||
|
||||
export class GetOutboundAsymmetricMessages {
|
||||
export class GetOutboundMessages {
|
||||
constructor(private messageServer: AsymmetricMessageServerInterface) {}
|
||||
|
||||
async execute(): Promise<AsymmetricMessageServerHash[] | ClientDisplayableError> {
|
||||
@@ -0,0 +1,17 @@
|
||||
import { AsymmetricMessagePayloadType } from '@standardnotes/models'
|
||||
|
||||
const TypesUsingReplaceableIdentifiers = [
|
||||
AsymmetricMessagePayloadType.SharedVaultRootKeyChanged,
|
||||
AsymmetricMessagePayloadType.SharedVaultMetadataChanged,
|
||||
]
|
||||
|
||||
export function GetReplaceabilityIdentifier(
|
||||
type: AsymmetricMessagePayloadType,
|
||||
sharedVaultUuid: string,
|
||||
keySystemIdentifier: string,
|
||||
): string | undefined {
|
||||
if (!TypesUsingReplaceableIdentifiers.includes(type)) {
|
||||
return undefined
|
||||
}
|
||||
return [type, sharedVaultUuid, keySystemIdentifier].join(':')
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessagePayload, TrustedContactInterface } from '@standardnotes/models'
|
||||
import { DecryptMessage } from '../../Encryption/UseCase/Asymmetric/DecryptMessage'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
import { GetTrustedPayload } from './GetTrustedPayload'
|
||||
|
||||
describe('GetTrustedPayload', () => {
|
||||
let decryptMessage: jest.Mocked<DecryptMessage>
|
||||
let getTrustedPayload: GetTrustedPayload
|
||||
|
||||
beforeEach(() => {
|
||||
decryptMessage = {} as jest.Mocked<DecryptMessage>
|
||||
decryptMessage.execute = jest.fn()
|
||||
|
||||
getTrustedPayload = new GetTrustedPayload(decryptMessage)
|
||||
})
|
||||
|
||||
describe('execute', () => {
|
||||
const mockDto = {
|
||||
privateKey: 'test-private-key',
|
||||
message: {} as AsymmetricMessageServerHash,
|
||||
sender: {} as TrustedContactInterface,
|
||||
ownUserUuid: 'test-user-uuid',
|
||||
}
|
||||
|
||||
it('should return failure when decryption fails', () => {
|
||||
decryptMessage.execute = jest.fn().mockReturnValue(Result.fail('Decryption failed'))
|
||||
|
||||
const result = getTrustedPayload.execute(mockDto)
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Decryption failed')
|
||||
})
|
||||
|
||||
it('should return failure when recipientUuid is not equal to ownUserUuid', () => {
|
||||
const mockPayload: AsymmetricMessagePayload = {
|
||||
data: {
|
||||
recipientUuid: 'another-user-uuid',
|
||||
},
|
||||
} as AsymmetricMessagePayload
|
||||
decryptMessage.execute = jest.fn().mockReturnValue(Result.ok(mockPayload))
|
||||
|
||||
const result = getTrustedPayload.execute(mockDto)
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
expect(result.getError()).toBe('Message is not for this user')
|
||||
})
|
||||
|
||||
it('should return success when recipientUuid is equal to ownUserUuid', () => {
|
||||
const mockPayload: AsymmetricMessagePayload = {
|
||||
data: {
|
||||
recipientUuid: 'test-user-uuid',
|
||||
},
|
||||
} as AsymmetricMessagePayload
|
||||
decryptMessage.execute = jest.fn().mockReturnValue(Result.ok(mockPayload))
|
||||
|
||||
const result = getTrustedPayload.execute(mockDto)
|
||||
|
||||
expect(result.isFailed()).toBe(false)
|
||||
expect(result.getValue()).toBe(mockPayload)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,33 @@
|
||||
import { AsymmetricMessageServerHash } from '@standardnotes/responses'
|
||||
import { AsymmetricMessagePayload, TrustedContactInterface } from '@standardnotes/models'
|
||||
import { DecryptMessage } from '../../Encryption/UseCase/Asymmetric/DecryptMessage'
|
||||
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class GetTrustedPayload implements SyncUseCaseInterface<AsymmetricMessagePayload> {
|
||||
constructor(private decryptMessage: DecryptMessage) {}
|
||||
|
||||
execute<M extends AsymmetricMessagePayload>(dto: {
|
||||
privateKey: string
|
||||
message: AsymmetricMessageServerHash
|
||||
sender: TrustedContactInterface
|
||||
ownUserUuid: string
|
||||
}): Result<M> {
|
||||
const result = this.decryptMessage.execute<M>({
|
||||
message: dto.message.encrypted_message,
|
||||
sender: dto.sender,
|
||||
privateKey: dto.privateKey,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
return result
|
||||
}
|
||||
|
||||
const recipientUuid = result.getValue().data.recipientUuid
|
||||
|
||||
if (recipientUuid !== dto.ownUserUuid) {
|
||||
return Result.fail('Message is not for this user')
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user