refactor: application dependency management (#2363)
This commit is contained in:
@@ -1,81 +1,62 @@
|
||||
import { MutatorClientInterface } from './../Mutator/MutatorClientInterface'
|
||||
import { ApplicationStage } from './../Application/ApplicationStage'
|
||||
import { SingletonManagerInterface } from './../Singleton/SingletonManagerInterface'
|
||||
import { UserKeyPairChangedEventData } from './../Session/UserKeyPairChangedEventData'
|
||||
import { SessionEvent } from './../Session/SessionEvent'
|
||||
import { InternalEventInterface } from './../Internal/InternalEventInterface'
|
||||
import { InternalEventHandlerInterface } from './../Internal/InternalEventHandlerInterface'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { SharedVaultInviteServerHash, SharedVaultUserServerHash } from '@standardnotes/responses'
|
||||
import {
|
||||
TrustedContactContent,
|
||||
TrustedContactContentSpecialized,
|
||||
TrustedContactInterface,
|
||||
FillItemContent,
|
||||
TrustedContactMutator,
|
||||
DecryptedItemInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { TrustedContactInterface, TrustedContactMutator, DecryptedItemInterface } from '@standardnotes/models'
|
||||
import { AbstractService } from '../Service/AbstractService'
|
||||
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
|
||||
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
|
||||
import { SessionsClientInterface } from '../Session/SessionsClientInterface'
|
||||
import { ContactServiceEvent, ContactServiceInterface } from '../Contacts/ContactServiceInterface'
|
||||
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||
import { UserClientInterface } from '../User/UserClientInterface'
|
||||
import { CollaborationIDData, Version1CollaborationId } from './CollaborationID'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { ValidateItemSignerUseCase } from './UseCase/ValidateItemSigner'
|
||||
import { ValidateItemSignerResult } from './UseCase/ValidateItemSignerResult'
|
||||
import { FindTrustedContactUseCase } from './UseCase/FindTrustedContact'
|
||||
import { SelfContactManager } from './Managers/SelfContactManager'
|
||||
import { CreateOrEditTrustedContactUseCase } from './UseCase/CreateOrEditTrustedContact'
|
||||
import { UpdateTrustedContactUseCase } from './UseCase/UpdateTrustedContact'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { ValidateItemSigner } from './UseCase/ValidateItemSigner'
|
||||
import { ItemSignatureValidationResult } from './UseCase/Types/ItemSignatureValidationResult'
|
||||
import { FindContact } from './UseCase/FindContact'
|
||||
import { SelfContactManager } from './SelfContactManager'
|
||||
import { CreateOrEditContact } from './UseCase/CreateOrEditContact'
|
||||
import { EditContact } from './UseCase/EditContact'
|
||||
import { GetAllContacts } from './UseCase/GetAllContacts'
|
||||
import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface'
|
||||
|
||||
export class ContactService
|
||||
extends AbstractService<ContactServiceEvent>
|
||||
implements ContactServiceInterface, InternalEventHandlerInterface
|
||||
{
|
||||
private selfContactManager: SelfContactManager
|
||||
|
||||
constructor(
|
||||
private sync: SyncServiceInterface,
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private session: SessionsClientInterface,
|
||||
private crypto: PureCryptoInterface,
|
||||
private user: UserClientInterface,
|
||||
private selfContactManager: SelfContactManager,
|
||||
private encryption: EncryptionProviderInterface,
|
||||
singletons: SingletonManagerInterface,
|
||||
private _findContact: FindContact,
|
||||
private _getAllContacts: GetAllContacts,
|
||||
private _createOrEditContact: CreateOrEditContact,
|
||||
private _editContact: EditContact,
|
||||
private _validateItemSigner: ValidateItemSigner,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(eventBus)
|
||||
|
||||
this.selfContactManager = new SelfContactManager(sync, items, mutator, session, singletons)
|
||||
|
||||
eventBus.addEventHandler(this, SessionEvent.UserKeyPairChanged)
|
||||
}
|
||||
|
||||
public override async handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
await super.handleApplicationStage(stage)
|
||||
await this.selfContactManager.handleApplicationStage(stage)
|
||||
}
|
||||
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === SessionEvent.UserKeyPairChanged) {
|
||||
const data = event.payload as UserKeyPairChangedEventData
|
||||
|
||||
await this.selfContactManager.updateWithNewPublicKeySet({
|
||||
encryption: data.newKeyPair.publicKey,
|
||||
signing: data.newSigningKeyPair.publicKey,
|
||||
encryption: data.current.encryption.publicKey,
|
||||
signing: data.current.signing.publicKey,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private get userUuid(): string {
|
||||
return this.session.getSureUser().uuid
|
||||
}
|
||||
|
||||
getSelfContact(): TrustedContactInterface | undefined {
|
||||
return this.selfContactManager.selfContact
|
||||
}
|
||||
@@ -170,38 +151,11 @@ export class ContactService
|
||||
contact: TrustedContactInterface,
|
||||
params: { name: string; publicKey: string; signingPublicKey: string },
|
||||
): Promise<TrustedContactInterface> {
|
||||
const usecase = new UpdateTrustedContactUseCase(this.mutator, this.sync)
|
||||
const updatedContact = await usecase.execute(contact, params)
|
||||
const updatedContact = await this._editContact.execute(contact, params)
|
||||
|
||||
return updatedContact
|
||||
}
|
||||
|
||||
async createOrUpdateTrustedContactFromContactShare(
|
||||
data: TrustedContactContentSpecialized,
|
||||
): Promise<TrustedContactInterface> {
|
||||
if (data.contactUuid === this.userUuid) {
|
||||
throw new Error('Cannot receive self from contact share')
|
||||
}
|
||||
|
||||
let contact = this.findTrustedContact(data.contactUuid)
|
||||
if (contact) {
|
||||
contact = await this.mutator.changeItem<TrustedContactMutator, TrustedContactInterface>(contact, (mutator) => {
|
||||
mutator.name = data.name
|
||||
mutator.replacePublicKeySet(data.publicKeySet)
|
||||
})
|
||||
} else {
|
||||
contact = await this.mutator.createItem<TrustedContactInterface>(
|
||||
ContentType.TYPES.TrustedContact,
|
||||
FillItemContent<TrustedContactContent>(data),
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
await this.sync.sync()
|
||||
|
||||
return contact
|
||||
}
|
||||
|
||||
async createOrEditTrustedContact(params: {
|
||||
name?: string
|
||||
contactUuid: string
|
||||
@@ -209,8 +163,7 @@ export class ContactService
|
||||
signingPublicKey: string
|
||||
isMe?: boolean
|
||||
}): Promise<TrustedContactInterface | undefined> {
|
||||
const usecase = new CreateOrEditTrustedContactUseCase(this.items, this.mutator, this.sync)
|
||||
const contact = await usecase.execute(params)
|
||||
const contact = await this._createOrEditContact.execute(params)
|
||||
return contact
|
||||
}
|
||||
|
||||
@@ -224,12 +177,15 @@ export class ContactService
|
||||
}
|
||||
|
||||
getAllContacts(): TrustedContactInterface[] {
|
||||
return this.items.getItems(ContentType.TYPES.TrustedContact)
|
||||
return this._getAllContacts.execute().getValue()
|
||||
}
|
||||
|
||||
findTrustedContact(userUuid: string): TrustedContactInterface | undefined {
|
||||
const usecase = new FindTrustedContactUseCase(this.items)
|
||||
return usecase.execute({ userUuid })
|
||||
const result = this._findContact.execute({ userUuid })
|
||||
if (result.isFailed()) {
|
||||
return undefined
|
||||
}
|
||||
return result.getValue()
|
||||
}
|
||||
|
||||
findTrustedContactForServerUser(user: SharedVaultUserServerHash): TrustedContactInterface | undefined {
|
||||
@@ -249,16 +205,23 @@ export class ContactService
|
||||
})
|
||||
}
|
||||
|
||||
isItemAuthenticallySigned(item: DecryptedItemInterface): ValidateItemSignerResult {
|
||||
const usecase = new ValidateItemSignerUseCase(this.items)
|
||||
return usecase.execute(item)
|
||||
isItemAuthenticallySigned(item: DecryptedItemInterface): ItemSignatureValidationResult {
|
||||
return this._validateItemSigner.execute(item)
|
||||
}
|
||||
|
||||
override deinit(): void {
|
||||
super.deinit()
|
||||
this.selfContactManager.deinit()
|
||||
;(this.sync as unknown) = undefined
|
||||
;(this.items as unknown) = undefined
|
||||
;(this.mutator as unknown) = undefined
|
||||
;(this.session as unknown) = undefined
|
||||
;(this.crypto as unknown) = undefined
|
||||
;(this.user as unknown) = undefined
|
||||
;(this.selfContactManager as unknown) = undefined
|
||||
;(this.encryption as unknown) = undefined
|
||||
;(this._findContact as unknown) = undefined
|
||||
;(this._getAllContacts as unknown) = undefined
|
||||
;(this._createOrEditContact as unknown) = undefined
|
||||
;(this._editContact as unknown) = undefined
|
||||
;(this._validateItemSigner as unknown) = undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import {
|
||||
DecryptedItemInterface,
|
||||
TrustedContactContentSpecialized,
|
||||
TrustedContactInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { DecryptedItemInterface, TrustedContactInterface } from '@standardnotes/models'
|
||||
import { AbstractService } from '../Service/AbstractService'
|
||||
import { SharedVaultInviteServerHash, SharedVaultUserServerHash } from '@standardnotes/responses'
|
||||
import { ValidateItemSignerResult } from './UseCase/ValidateItemSignerResult'
|
||||
import { ItemSignatureValidationResult } from './UseCase/Types/ItemSignatureValidationResult'
|
||||
|
||||
export enum ContactServiceEvent {}
|
||||
|
||||
@@ -26,7 +22,6 @@ export interface ContactServiceInterface extends AbstractService<ContactServiceE
|
||||
publicKey: string
|
||||
signingPublicKey: string
|
||||
}): Promise<TrustedContactInterface | undefined>
|
||||
createOrUpdateTrustedContactFromContactShare(data: TrustedContactContentSpecialized): Promise<TrustedContactInterface>
|
||||
editTrustedContactFromCollaborationID(
|
||||
contact: TrustedContactInterface,
|
||||
params: { name: string; collaborationID: string },
|
||||
@@ -39,5 +34,5 @@ export interface ContactServiceInterface extends AbstractService<ContactServiceE
|
||||
findTrustedContactForServerUser(user: SharedVaultUserServerHash): TrustedContactInterface | undefined
|
||||
findTrustedContactForInvite(invite: SharedVaultInviteServerHash): TrustedContactInterface | undefined
|
||||
|
||||
isItemAuthenticallySigned(item: DecryptedItemInterface): ValidateItemSignerResult
|
||||
isItemAuthenticallySigned(item: DecryptedItemInterface): ItemSignatureValidationResult
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import { MutatorClientInterface } from './../../Mutator/MutatorClientInterface'
|
||||
import { InternalFeature } from './../../InternalFeatures/InternalFeature'
|
||||
import { InternalFeatureService } from '../../InternalFeatures/InternalFeatureService'
|
||||
import { ApplicationStage } from './../../Application/ApplicationStage'
|
||||
import { SingletonManagerInterface } from './../../Singleton/SingletonManagerInterface'
|
||||
import { SyncEvent } from './../../Event/SyncEvent'
|
||||
import { SessionsClientInterface } from '../../Session/SessionsClientInterface'
|
||||
import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
|
||||
import { SyncServiceInterface } from '../../Sync/SyncServiceInterface'
|
||||
import { ApplicationStageChangedEventPayload } from './../Event/ApplicationStageChangedEventPayload'
|
||||
import { ApplicationEvent } from './../Event/ApplicationEvent'
|
||||
import { InternalEventInterface } from './../Internal/InternalEventInterface'
|
||||
import { InternalEventHandlerInterface } from './../Internal/InternalEventHandlerInterface'
|
||||
import { InternalFeature } from '../InternalFeatures/InternalFeature'
|
||||
import { InternalFeatureService } from '../InternalFeatures/InternalFeatureService'
|
||||
import { ApplicationStage } from '../Application/ApplicationStage'
|
||||
import { SingletonManagerInterface } from '../Singleton/SingletonManagerInterface'
|
||||
import { SyncEvent } from '../Event/SyncEvent'
|
||||
import { SessionsClientInterface } from '../Session/SessionsClientInterface'
|
||||
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
|
||||
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
|
||||
import {
|
||||
ContactPublicKeySet,
|
||||
FillItemContent,
|
||||
@@ -14,23 +17,25 @@ import {
|
||||
TrustedContactContent,
|
||||
TrustedContactContentSpecialized,
|
||||
TrustedContactInterface,
|
||||
PortablePublicKeySet,
|
||||
} from '@standardnotes/models'
|
||||
import { CreateOrEditTrustedContactUseCase } from '../UseCase/CreateOrEditTrustedContact'
|
||||
import { PublicKeySet } from '@standardnotes/encryption'
|
||||
import { CreateOrEditContact } from './UseCase/CreateOrEditContact'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
export class SelfContactManager {
|
||||
const SelfContactName = 'Me'
|
||||
|
||||
export class SelfContactManager implements InternalEventHandlerInterface {
|
||||
public selfContact?: TrustedContactInterface
|
||||
|
||||
private isReloadingSelfContact = false
|
||||
private eventDisposers: (() => void)[] = []
|
||||
|
||||
constructor(
|
||||
private sync: SyncServiceInterface,
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
sync: SyncServiceInterface,
|
||||
items: ItemManagerInterface,
|
||||
private session: SessionsClientInterface,
|
||||
private singletons: SingletonManagerInterface,
|
||||
private createOrEditContact: CreateOrEditContact,
|
||||
) {
|
||||
this.eventDisposers.push(
|
||||
sync.addEventObserver((event) => {
|
||||
@@ -43,11 +48,26 @@ export class SelfContactManager {
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
this.eventDisposers.push(
|
||||
items.addObserver(ContentType.TYPES.TrustedContact, () => {
|
||||
const updatedReference = this.singletons.findSingleton<TrustedContact>(
|
||||
ContentType.TYPES.TrustedContact,
|
||||
TrustedContact.singletonPredicate,
|
||||
)
|
||||
if (updatedReference) {
|
||||
this.selfContact = updatedReference
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
public async handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
this.loadSelfContactFromDatabase()
|
||||
async handleEvent(event: InternalEventInterface): Promise<void> {
|
||||
if (event.type === ApplicationEvent.ApplicationStageChanged) {
|
||||
const stage = (event.payload as ApplicationStageChangedEventPayload).stage
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
this.loadSelfContactFromDatabase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +82,7 @@ export class SelfContactManager {
|
||||
)
|
||||
}
|
||||
|
||||
public async updateWithNewPublicKeySet(publicKeySet: PublicKeySet) {
|
||||
public async updateWithNewPublicKeySet(publicKeySet: PortablePublicKeySet) {
|
||||
if (!InternalFeatureService.get().isFeatureEnabled(InternalFeature.Vaults)) {
|
||||
return
|
||||
}
|
||||
@@ -71,9 +91,8 @@ export class SelfContactManager {
|
||||
return
|
||||
}
|
||||
|
||||
const usecase = new CreateOrEditTrustedContactUseCase(this.items, this.mutator, this.sync)
|
||||
await usecase.execute({
|
||||
name: 'Me',
|
||||
await this.createOrEditContact.execute({
|
||||
name: SelfContactName,
|
||||
contactUuid: this.selfContact.contactUuid,
|
||||
publicKey: publicKeySet.encryption,
|
||||
signingPublicKey: publicKeySet.signing,
|
||||
@@ -104,13 +123,12 @@ export class SelfContactManager {
|
||||
this.isReloadingSelfContact = true
|
||||
|
||||
const content: TrustedContactContentSpecialized = {
|
||||
name: 'Me',
|
||||
name: SelfContactName,
|
||||
isMe: true,
|
||||
contactUuid: this.session.getSureUser().uuid,
|
||||
publicKeySet: ContactPublicKeySet.FromJson({
|
||||
encryption: this.session.getPublicKey(),
|
||||
signing: this.session.getSigningPublicKey(),
|
||||
isRevoked: false,
|
||||
timestamp: new Date(),
|
||||
}),
|
||||
}
|
||||
@@ -126,10 +144,8 @@ export class SelfContactManager {
|
||||
|
||||
deinit() {
|
||||
this.eventDisposers.forEach((disposer) => disposer())
|
||||
;(this.sync as unknown) = undefined
|
||||
;(this.items as unknown) = undefined
|
||||
;(this.mutator as unknown) = undefined
|
||||
;(this.session as unknown) = undefined
|
||||
;(this.singletons as unknown) = undefined
|
||||
;(this.createOrEditContact as unknown) = undefined
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { SyncServiceInterface } from './../../Sync/SyncServiceInterface'
|
||||
import { MutatorClientInterface } from './../../Mutator/MutatorClientInterface'
|
||||
import { ItemManagerInterface } from './../../Item/ItemManagerInterface'
|
||||
import { SyncServiceInterface } from '../../Sync/SyncServiceInterface'
|
||||
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
|
||||
import {
|
||||
ContactPublicKeySet,
|
||||
FillItemContent,
|
||||
@@ -8,16 +7,17 @@ import {
|
||||
TrustedContactContentSpecialized,
|
||||
TrustedContactInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { FindTrustedContactUseCase } from './FindTrustedContact'
|
||||
import { FindContact } from './FindContact'
|
||||
import { UnknownContactName } from '../UnknownContactName'
|
||||
import { UpdateTrustedContactUseCase } from './UpdateTrustedContact'
|
||||
import { EditContact } from './EditContact'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
export class CreateOrEditTrustedContactUseCase {
|
||||
export class CreateOrEditContact {
|
||||
constructor(
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private findContact: FindContact,
|
||||
private editContact: EditContact,
|
||||
) {}
|
||||
|
||||
async execute(params: {
|
||||
@@ -27,13 +27,14 @@ export class CreateOrEditTrustedContactUseCase {
|
||||
signingPublicKey: string
|
||||
isMe?: boolean
|
||||
}): Promise<TrustedContactInterface | undefined> {
|
||||
const findUsecase = new FindTrustedContactUseCase(this.items)
|
||||
const existingContact = findUsecase.execute({ userUuid: params.contactUuid })
|
||||
const existingContact = this.findContact.execute({ userUuid: params.contactUuid })
|
||||
|
||||
if (existingContact) {
|
||||
const updateUsecase = new UpdateTrustedContactUseCase(this.mutator, this.sync)
|
||||
await updateUsecase.execute(existingContact, { ...params, name: params.name ?? existingContact.name })
|
||||
return existingContact
|
||||
if (!existingContact.isFailed()) {
|
||||
await this.editContact.execute(existingContact.getValue(), {
|
||||
...params,
|
||||
name: params.name ?? existingContact.getValue().name,
|
||||
})
|
||||
return existingContact.getValue()
|
||||
}
|
||||
|
||||
const content: TrustedContactContentSpecialized = {
|
||||
@@ -41,7 +42,6 @@ export class CreateOrEditTrustedContactUseCase {
|
||||
publicKeySet: ContactPublicKeySet.FromJson({
|
||||
encryption: params.publicKey,
|
||||
signing: params.signingPublicKey,
|
||||
isRevoked: false,
|
||||
timestamp: new Date(),
|
||||
}),
|
||||
contactUuid: params.contactUuid,
|
||||
@@ -1,8 +1,8 @@
|
||||
import { SyncServiceInterface } from './../../Sync/SyncServiceInterface'
|
||||
import { MutatorClientInterface } from './../../Mutator/MutatorClientInterface'
|
||||
import { SyncServiceInterface } from '../../Sync/SyncServiceInterface'
|
||||
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
|
||||
import { TrustedContactInterface, TrustedContactMutator } from '@standardnotes/models'
|
||||
|
||||
export class UpdateTrustedContactUseCase {
|
||||
export class EditContact {
|
||||
constructor(private mutator: MutatorClientInterface, private sync: SyncServiceInterface) {}
|
||||
|
||||
async execute(
|
||||
38
packages/services/src/Domain/Contacts/UseCase/FindContact.ts
Normal file
38
packages/services/src/Domain/Contacts/UseCase/FindContact.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Predicate, TrustedContactInterface } from '@standardnotes/models'
|
||||
import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
|
||||
import { FindContactQuery } from './Types/FindContactQuery'
|
||||
import { ContentType, Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class FindContact implements SyncUseCaseInterface<TrustedContactInterface> {
|
||||
constructor(private items: ItemManagerInterface) {}
|
||||
|
||||
execute(query: FindContactQuery): Result<TrustedContactInterface> {
|
||||
if ('userUuid' in query && query.userUuid) {
|
||||
const contact = this.items.itemsMatchingPredicate<TrustedContactInterface>(
|
||||
ContentType.TYPES.TrustedContact,
|
||||
new Predicate<TrustedContactInterface>('contactUuid', '=', query.userUuid),
|
||||
)[0]
|
||||
|
||||
if (contact) {
|
||||
return Result.ok(contact)
|
||||
} else {
|
||||
return Result.fail('Contact not found')
|
||||
}
|
||||
}
|
||||
|
||||
if ('signingPublicKey' in query && query.signingPublicKey) {
|
||||
const allContacts = this.items.getItems<TrustedContactInterface>(ContentType.TYPES.TrustedContact)
|
||||
const contact = allContacts.find((contact) =>
|
||||
contact.hasCurrentOrPreviousSigningPublicKey(query.signingPublicKey),
|
||||
)
|
||||
|
||||
if (contact) {
|
||||
return Result.ok(contact)
|
||||
} else {
|
||||
return Result.fail('Contact not found')
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Invalid query')
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Predicate, TrustedContactInterface } from '@standardnotes/models'
|
||||
import { ItemManagerInterface } from './../../Item/ItemManagerInterface'
|
||||
import { FindContactQuery } from './FindContactQuery'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
export class FindTrustedContactUseCase {
|
||||
constructor(private items: ItemManagerInterface) {}
|
||||
|
||||
execute(query: FindContactQuery): TrustedContactInterface | undefined {
|
||||
if ('userUuid' in query && query.userUuid) {
|
||||
return this.items.itemsMatchingPredicate<TrustedContactInterface>(
|
||||
ContentType.TYPES.TrustedContact,
|
||||
new Predicate<TrustedContactInterface>('contactUuid', '=', query.userUuid),
|
||||
)[0]
|
||||
}
|
||||
|
||||
if ('signingPublicKey' in query && query.signingPublicKey) {
|
||||
const allContacts = this.items.getItems<TrustedContactInterface>(ContentType.TYPES.TrustedContact)
|
||||
return allContacts.find((contact) => contact.isSigningKeyTrusted(query.signingPublicKey))
|
||||
}
|
||||
|
||||
if ('publicKey' in query && query.publicKey) {
|
||||
const allContacts = this.items.getItems<TrustedContactInterface>(ContentType.TYPES.TrustedContact)
|
||||
return allContacts.find((contact) => contact.isPublicKeyTrusted(query.publicKey))
|
||||
}
|
||||
|
||||
throw new Error('Invalid query')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { TrustedContactInterface } from '@standardnotes/models'
|
||||
import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
|
||||
import { ContentType, Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class GetAllContacts implements SyncUseCaseInterface<TrustedContactInterface[]> {
|
||||
constructor(private items: ItemManagerInterface) {}
|
||||
|
||||
execute(): Result<TrustedContactInterface[]> {
|
||||
return Result.ok(this.items.getItems(ContentType.TYPES.TrustedContact))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { PkcKeyPair } from '@standardnotes/sncrypto-common'
|
||||
import { ReuploadAllInvites } from '../../SharedVaults/UseCase/ReuploadAllInvites'
|
||||
import { ResendAllMessages } from '../../AsymmetricMessage/UseCase/ResendAllMessages'
|
||||
|
||||
export class HandleKeyPairChange implements UseCaseInterface<void> {
|
||||
constructor(private reuploadAllInvites: ReuploadAllInvites, private resendAllMessages: ResendAllMessages) {}
|
||||
|
||||
async execute(dto: {
|
||||
newKeys: {
|
||||
encryption: PkcKeyPair
|
||||
signing: PkcKeyPair
|
||||
}
|
||||
previousKeys?: {
|
||||
encryption: PkcKeyPair
|
||||
signing: PkcKeyPair
|
||||
}
|
||||
}): Promise<Result<void>> {
|
||||
await this.reuploadAllInvites.execute({
|
||||
keys: dto.newKeys,
|
||||
previousKeys: dto.previousKeys,
|
||||
})
|
||||
|
||||
await this.resendAllMessages.execute({
|
||||
keys: dto.newKeys,
|
||||
previousKeys: dto.previousKeys,
|
||||
})
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { SyncServiceInterface } from '../../Sync/SyncServiceInterface'
|
||||
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
|
||||
import {
|
||||
FillItemContent,
|
||||
MutationType,
|
||||
PayloadEmitSource,
|
||||
TrustedContactContent,
|
||||
TrustedContactContentSpecialized,
|
||||
TrustedContactInterface,
|
||||
TrustedContactMutator,
|
||||
} from '@standardnotes/models'
|
||||
import { FindContact } from './FindContact'
|
||||
import { ContentType, Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class ReplaceContactData implements UseCaseInterface<TrustedContactInterface> {
|
||||
constructor(
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private findContact: FindContact,
|
||||
) {}
|
||||
|
||||
async execute(data: TrustedContactContentSpecialized): Promise<Result<TrustedContactInterface>> {
|
||||
const contactResult = this.findContact.execute({ userUuid: data.contactUuid })
|
||||
if (contactResult.isFailed()) {
|
||||
const newContact = await this.mutator.createItem<TrustedContactInterface>(
|
||||
ContentType.TYPES.TrustedContact,
|
||||
FillItemContent<TrustedContactContent>(data),
|
||||
true,
|
||||
)
|
||||
|
||||
await this.sync.sync()
|
||||
|
||||
return Result.ok(newContact)
|
||||
}
|
||||
|
||||
const existingContact = contactResult.getValue()
|
||||
if (existingContact.isMe) {
|
||||
return Result.fail('Cannot replace data for me contact')
|
||||
}
|
||||
|
||||
const updatedContact = await this.mutator.changeItem<TrustedContactMutator, TrustedContactInterface>(
|
||||
existingContact,
|
||||
(mutator) => {
|
||||
mutator.name = data.name
|
||||
mutator.replacePublicKeySet(data.publicKeySet)
|
||||
},
|
||||
MutationType.UpdateUserTimestamps,
|
||||
PayloadEmitSource.RemoteRetrieved,
|
||||
)
|
||||
|
||||
await this.sync.sync()
|
||||
|
||||
return Result.ok(updatedContact)
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
export type FindContactQuery = { userUuid: string } | { signingPublicKey: string } | { publicKey: string }
|
||||
export type FindContactQuery = { userUuid: string } | { signingPublicKey: string }
|
||||
@@ -0,0 +1,6 @@
|
||||
export enum ItemSignatureValidationResult {
|
||||
NotApplicable = 'NotApplicable',
|
||||
Trusted = 'Trusted',
|
||||
SignedWithNonCurrentKey = 'SignedWithNonCurrentKey',
|
||||
NotTrusted = 'NotTrusted',
|
||||
}
|
||||
@@ -2,21 +2,26 @@ import {
|
||||
DecryptedItemInterface,
|
||||
PayloadSource,
|
||||
PersistentSignatureData,
|
||||
PublicKeyTrustStatus,
|
||||
TrustedContactInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { ItemManagerInterface } from './../../Item/ItemManagerInterface'
|
||||
import { ValidateItemSignerUseCase } from './ValidateItemSigner'
|
||||
import { ValidateItemSigner } from './ValidateItemSigner'
|
||||
import { FindContact } from './FindContact'
|
||||
import { ItemSignatureValidationResult } from './Types/ItemSignatureValidationResult'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
|
||||
describe('validate item signer use case', () => {
|
||||
let usecase: ValidateItemSignerUseCase
|
||||
let items: ItemManagerInterface
|
||||
let usecase: ValidateItemSigner
|
||||
let findContact: FindContact
|
||||
|
||||
const trustedContact = {} as jest.Mocked<TrustedContactInterface>
|
||||
trustedContact.isSigningKeyTrusted = jest.fn().mockReturnValue(true)
|
||||
trustedContact.getTrustStatusForSigningPublicKey = jest.fn().mockReturnValue(PublicKeyTrustStatus.Trusted)
|
||||
|
||||
beforeEach(() => {
|
||||
items = {} as jest.Mocked<ItemManagerInterface>
|
||||
usecase = new ValidateItemSignerUseCase(items)
|
||||
findContact = {} as jest.Mocked<FindContact>
|
||||
findContact.execute = jest.fn().mockReturnValue(Result.ok(trustedContact))
|
||||
|
||||
usecase = new ValidateItemSigner(findContact)
|
||||
})
|
||||
|
||||
const createItem = (params: {
|
||||
@@ -42,7 +47,7 @@ describe('validate item signer use case', () => {
|
||||
describe('has last edited by uuid', () => {
|
||||
describe('trusted contact not found', () => {
|
||||
beforeEach(() => {
|
||||
items.itemsMatchingPredicate = jest.fn().mockReturnValue([])
|
||||
findContact.execute = jest.fn().mockReturnValue(Result.fail('Not found'))
|
||||
})
|
||||
|
||||
it('should return invalid signing is required', () => {
|
||||
@@ -53,7 +58,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('no')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotTrusted)
|
||||
})
|
||||
|
||||
it('should return not applicable signing is not required', () => {
|
||||
@@ -64,15 +69,11 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('not-applicable')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
})
|
||||
|
||||
describe('trusted contact found for last editor', () => {
|
||||
beforeEach(() => {
|
||||
items.itemsMatchingPredicate = jest.fn().mockReturnValue([trustedContact])
|
||||
})
|
||||
|
||||
describe('does not have signature data', () => {
|
||||
it('should return not applicable if the item was just recently created', () => {
|
||||
const item = createItem({
|
||||
@@ -83,7 +84,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('not-applicable')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
|
||||
it('should return not applicable if the item was just recently saved', () => {
|
||||
@@ -95,7 +96,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('not-applicable')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
|
||||
it('should return invalid if signing is required', () => {
|
||||
@@ -106,7 +107,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('no')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotTrusted)
|
||||
})
|
||||
|
||||
it('should return not applicable if signing is not required', () => {
|
||||
@@ -117,7 +118,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('not-applicable')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -133,7 +134,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('no')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotTrusted)
|
||||
})
|
||||
|
||||
it('should return not applicable if signing is not required', () => {
|
||||
@@ -146,7 +147,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('not-applicable')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -164,7 +165,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('no')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotTrusted)
|
||||
})
|
||||
|
||||
it('should return invalid if signature result passes and a trusted contact is NOT found for signature public key', () => {
|
||||
@@ -180,10 +181,10 @@ describe('validate item signer use case', () => {
|
||||
} as jest.Mocked<PersistentSignatureData>,
|
||||
})
|
||||
|
||||
items.itemsMatchingPredicate = jest.fn().mockReturnValue([])
|
||||
findContact.execute = jest.fn().mockReturnValue(Result.fail('Not found'))
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('no')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotTrusted)
|
||||
})
|
||||
|
||||
it('should return valid if signature result passes and a trusted contact is found for signature public key', () => {
|
||||
@@ -199,10 +200,10 @@ describe('validate item signer use case', () => {
|
||||
} as jest.Mocked<PersistentSignatureData>,
|
||||
})
|
||||
|
||||
items.itemsMatchingPredicate = jest.fn().mockReturnValue([trustedContact])
|
||||
findContact.execute = jest.fn().mockReturnValue(Result.ok(trustedContact))
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('yes')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.Trusted)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -220,7 +221,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('not-applicable')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
|
||||
it('should return not applicable if the item was just recently saved', () => {
|
||||
@@ -232,7 +233,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('not-applicable')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
|
||||
it('should return invalid if signing is required', () => {
|
||||
@@ -243,7 +244,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('no')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotTrusted)
|
||||
})
|
||||
|
||||
it('should return not applicable if signing is not required', () => {
|
||||
@@ -254,7 +255,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('not-applicable')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -270,7 +271,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('no')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotTrusted)
|
||||
})
|
||||
|
||||
it('should return not applicable if signing is not required', () => {
|
||||
@@ -283,7 +284,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('not-applicable')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotApplicable)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -301,7 +302,7 @@ describe('validate item signer use case', () => {
|
||||
})
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('no')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotTrusted)
|
||||
})
|
||||
|
||||
it('should return invalid if signature result passes and a trusted contact is NOT found for signature public key', () => {
|
||||
@@ -317,10 +318,10 @@ describe('validate item signer use case', () => {
|
||||
} as jest.Mocked<PersistentSignatureData>,
|
||||
})
|
||||
|
||||
items.getItems = jest.fn().mockReturnValue([])
|
||||
findContact.execute = jest.fn().mockReturnValue(Result.fail('Not found'))
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('no')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.NotTrusted)
|
||||
})
|
||||
|
||||
it('should return valid if signature result passes and a trusted contact is found for signature public key', () => {
|
||||
@@ -336,10 +337,8 @@ describe('validate item signer use case', () => {
|
||||
} as jest.Mocked<PersistentSignatureData>,
|
||||
})
|
||||
|
||||
items.getItems = jest.fn().mockReturnValue([trustedContact])
|
||||
|
||||
const result = usecase.execute(item)
|
||||
expect(result).toEqual('yes')
|
||||
expect(result).toEqual(ItemSignatureValidationResult.Trusted)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { ItemManagerInterface } from './../../Item/ItemManagerInterface'
|
||||
import { doesPayloadRequireSigning } from '@standardnotes/encryption/src/Domain/Operator/004/V004AlgorithmHelpers'
|
||||
import { DecryptedItemInterface, PayloadSource } from '@standardnotes/models'
|
||||
import { ValidateItemSignerResult } from './ValidateItemSignerResult'
|
||||
import { FindTrustedContactUseCase } from './FindTrustedContact'
|
||||
import { DecryptedItemInterface, PayloadSource, PublicKeyTrustStatus } from '@standardnotes/models'
|
||||
import { ItemSignatureValidationResult } from './Types/ItemSignatureValidationResult'
|
||||
import { FindContact } from './FindContact'
|
||||
|
||||
export class ValidateItemSignerUseCase {
|
||||
private findContactUseCase = new FindTrustedContactUseCase(this.items)
|
||||
export class ValidateItemSigner {
|
||||
constructor(private findContact: FindContact) {}
|
||||
|
||||
constructor(private items: ItemManagerInterface) {}
|
||||
|
||||
execute(item: DecryptedItemInterface): ValidateItemSignerResult {
|
||||
execute(item: DecryptedItemInterface): ItemSignatureValidationResult {
|
||||
const uuidOfLastEditor = item.last_edited_by_uuid
|
||||
if (uuidOfLastEditor) {
|
||||
return this.validateSignatureWithLastEditedByUuid(item, uuidOfLastEditor)
|
||||
@@ -29,15 +26,15 @@ export class ValidateItemSignerUseCase {
|
||||
private validateSignatureWithLastEditedByUuid(
|
||||
item: DecryptedItemInterface,
|
||||
uuidOfLastEditor: string,
|
||||
): ValidateItemSignerResult {
|
||||
): ItemSignatureValidationResult {
|
||||
const requiresSignature = doesPayloadRequireSigning(item)
|
||||
|
||||
const trustedContact = this.findContactUseCase.execute({ userUuid: uuidOfLastEditor })
|
||||
if (!trustedContact) {
|
||||
const trustedContact = this.findContact.execute({ userUuid: uuidOfLastEditor })
|
||||
if (trustedContact.isFailed()) {
|
||||
if (requiresSignature) {
|
||||
return 'no'
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
} else {
|
||||
return 'not-applicable'
|
||||
return ItemSignatureValidationResult.NotApplicable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,38 +43,41 @@ export class ValidateItemSignerUseCase {
|
||||
this.isItemLocallyCreatedAndDoesNotRequireSignature(item) ||
|
||||
this.isItemResutOfRemoteSaveAndDoesNotRequireSignature(item)
|
||||
) {
|
||||
return 'not-applicable'
|
||||
return ItemSignatureValidationResult.NotApplicable
|
||||
}
|
||||
if (requiresSignature) {
|
||||
return 'no'
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
}
|
||||
return 'not-applicable'
|
||||
return ItemSignatureValidationResult.NotApplicable
|
||||
}
|
||||
|
||||
const signatureData = item.signatureData
|
||||
if (!signatureData.result) {
|
||||
if (signatureData.required) {
|
||||
return 'no'
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
}
|
||||
return 'not-applicable'
|
||||
return ItemSignatureValidationResult.NotApplicable
|
||||
}
|
||||
|
||||
const signatureResult = signatureData.result
|
||||
|
||||
if (!signatureResult.passes) {
|
||||
return 'no'
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
}
|
||||
|
||||
const signerPublicKey = signatureResult.publicKey
|
||||
|
||||
if (trustedContact.isSigningKeyTrusted(signerPublicKey)) {
|
||||
return 'yes'
|
||||
const trustStatus = trustedContact.getValue().getTrustStatusForSigningPublicKey(signerPublicKey)
|
||||
if (trustStatus === PublicKeyTrustStatus.Trusted) {
|
||||
return ItemSignatureValidationResult.Trusted
|
||||
} else if (trustStatus === PublicKeyTrustStatus.Previous) {
|
||||
return ItemSignatureValidationResult.SignedWithNonCurrentKey
|
||||
}
|
||||
|
||||
return 'no'
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
}
|
||||
|
||||
private validateSignatureWithNoLastEditedByUuid(item: DecryptedItemInterface): ValidateItemSignerResult {
|
||||
private validateSignatureWithNoLastEditedByUuid(item: DecryptedItemInterface): ItemSignatureValidationResult {
|
||||
const requiresSignature = doesPayloadRequireSigning(item)
|
||||
|
||||
if (!item.signatureData) {
|
||||
@@ -85,38 +85,45 @@ export class ValidateItemSignerUseCase {
|
||||
this.isItemLocallyCreatedAndDoesNotRequireSignature(item) ||
|
||||
this.isItemResutOfRemoteSaveAndDoesNotRequireSignature(item)
|
||||
) {
|
||||
return 'not-applicable'
|
||||
return ItemSignatureValidationResult.NotApplicable
|
||||
}
|
||||
|
||||
if (requiresSignature) {
|
||||
return 'no'
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
}
|
||||
|
||||
return 'not-applicable'
|
||||
return ItemSignatureValidationResult.NotApplicable
|
||||
}
|
||||
|
||||
const signatureData = item.signatureData
|
||||
if (!signatureData.result) {
|
||||
if (signatureData.required) {
|
||||
return 'no'
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
}
|
||||
return 'not-applicable'
|
||||
return ItemSignatureValidationResult.NotApplicable
|
||||
}
|
||||
|
||||
const signatureResult = signatureData.result
|
||||
|
||||
if (!signatureResult.passes) {
|
||||
return 'no'
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
}
|
||||
|
||||
const signerPublicKey = signatureResult.publicKey
|
||||
|
||||
const trustedContact = this.findContactUseCase.execute({ signingPublicKey: signerPublicKey })
|
||||
const trustedContact = this.findContact.execute({ signingPublicKey: signerPublicKey })
|
||||
|
||||
if (trustedContact) {
|
||||
return 'yes'
|
||||
if (trustedContact.isFailed()) {
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
}
|
||||
|
||||
return 'no'
|
||||
const trustStatus = trustedContact.getValue().getTrustStatusForSigningPublicKey(signerPublicKey)
|
||||
if (trustStatus === PublicKeyTrustStatus.Trusted) {
|
||||
return ItemSignatureValidationResult.Trusted
|
||||
} else if (trustStatus === PublicKeyTrustStatus.Previous) {
|
||||
return ItemSignatureValidationResult.SignedWithNonCurrentKey
|
||||
}
|
||||
|
||||
return ItemSignatureValidationResult.NotTrusted
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export type ValidateItemSignerResult = 'not-applicable' | 'yes' | 'no'
|
||||
Reference in New Issue
Block a user