From 80dd6efae389e1e9d2b167cbde66e86e331bce05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Thu, 1 Dec 2022 13:16:45 +0100 Subject: [PATCH] feat(settings): replace setting with a domain entity --- .pnp.cjs | 1 + packages/auth/bin/backup.ts | 28 ++++---- packages/auth/bin/user_email_backup.ts | 2 +- ...8504691-move_mfa_items_to_user_settings.ts | 2 +- .../auth/src/Controller/AdminController.ts | 4 +- .../SubscriptionTokensController.ts | 2 +- .../ExtensionKeyGrantedEventHandler.ts | 2 +- .../ListedAccountCreatedEventHandler.ts | 4 +- .../ListedAccountDeletedEventHandler.ts | 4 +- .../SubscriptionReassignedEventHandler.ts | 2 +- .../SubscriptionSyncRequestedEventHandler.ts | 2 +- .../auth/src/Domain/Session/SessionService.ts | 2 +- .../auth/src/Domain/Setting/FindSettingDTO.ts | 3 +- .../Domain/Setting/SettingInterpreter.spec.ts | 30 ++++---- .../src/Domain/Setting/SettingInterpreter.ts | 51 +++++++------- .../Setting/SettingRepositoryInterface.ts | 5 +- .../src/Domain/Setting/SettingService.spec.ts | 8 +-- .../auth/src/Domain/Setting/SettingService.ts | 3 +- .../SettingsAssociationService.spec.ts | 24 ++++--- .../Setting/SettingsAssociationService.ts | 68 +++++++++---------- .../SettingsAssociationServiceInterface.ts | 14 ++-- .../UseCase/GetSetting/GetSetting.spec.ts | 6 +- .../Domain/UseCase/GetSetting/GetSetting.ts | 3 +- .../UseCase/GetSettings/GetSettings.spec.ts | 2 +- .../UpdateSetting/UpdateSetting.spec.ts | 8 +-- .../UseCase/UpdateSetting/UpdateSetting.ts | 14 ++-- .../auth/src/Domain/UseCase/VerifyMFA.spec.ts | 6 +- packages/auth/src/Domain/UseCase/VerifyMFA.ts | 2 +- .../VerifyPredicate/VerifyPredicate.ts | 5 +- .../src/Infra/MySQL/MySQLSettingRepository.ts | 5 +- .../Domain/Common/RoleNameCollection.spec.ts | 13 ++++ .../src/Domain/Common/Timestamps.spec.ts | 21 ++++++ .../src/Domain/Common/Timestamps.ts | 30 ++++++++ .../src/Domain/Common/TimestampsProps.ts | 4 ++ .../Encryption/EncryptionVersion.spec.ts | 28 ++++++++ .../Domain/Encryption/EncryptionVersion.ts | 33 +++++++++ .../Encryption/EncryptionVersionProps.ts | 3 + packages/domain-core/src/Domain/index.ts | 5 ++ packages/settings/package.json | 1 + .../Domain/Setting/SensitiveSettingName.ts | 3 - .../settings/src/Domain/Setting/Setting.ts | 17 +++++ .../src/Domain/Setting/SettingName.ts | 59 +++++++++++----- .../src/Domain/Setting/SettingNameProps.ts | 3 + .../src/Domain/Setting/SettingProps.ts | 12 ++++ packages/settings/src/Domain/index.ts | 1 - .../src/Infra/HTTP/AuthHttpService.spec.ts | 4 +- yarn.lock | 1 + 47 files changed, 370 insertions(+), 180 deletions(-) create mode 100644 packages/domain-core/src/Domain/Common/Timestamps.spec.ts create mode 100644 packages/domain-core/src/Domain/Common/Timestamps.ts create mode 100644 packages/domain-core/src/Domain/Common/TimestampsProps.ts create mode 100644 packages/domain-core/src/Domain/Encryption/EncryptionVersion.spec.ts create mode 100644 packages/domain-core/src/Domain/Encryption/EncryptionVersion.ts create mode 100644 packages/domain-core/src/Domain/Encryption/EncryptionVersionProps.ts delete mode 100644 packages/settings/src/Domain/Setting/SensitiveSettingName.ts create mode 100644 packages/settings/src/Domain/Setting/Setting.ts create mode 100644 packages/settings/src/Domain/Setting/SettingNameProps.ts create mode 100644 packages/settings/src/Domain/Setting/SettingProps.ts diff --git a/.pnp.cjs b/.pnp.cjs index 6e143b2ed..99d650e3d 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -3137,6 +3137,7 @@ const RAW_RUNTIME_STATE = "packageLocation": "./packages/settings/",\ "packageDependencies": [\ ["@standardnotes/settings", "workspace:packages/settings"],\ + ["@standardnotes/domain-core", "workspace:packages/domain-core"],\ ["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.30.5"],\ ["eslint-plugin-prettier", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:4.2.1"],\ ["reflect-metadata", "npm:0.1.13"],\ diff --git a/packages/auth/bin/backup.ts b/packages/auth/bin/backup.ts index e0f6e524f..a6b5f14a9 100644 --- a/packages/auth/bin/backup.ts +++ b/packages/auth/bin/backup.ts @@ -30,38 +30,38 @@ const requestBackups = async ( domainEventFactory: DomainEventFactoryInterface, domainEventPublisher: DomainEventPublisherInterface, ): Promise => { - let settingName: SettingName, + let settingName: string, permissionName: PermissionName, - muteEmailsSettingName: SettingName, + muteEmailsSettingName: string, muteEmailsSettingValue: string, - providerTokenSettingName: SettingName + providerTokenSettingName: string switch (backupProvider) { case 'email': - settingName = SettingName.EmailBackupFrequency + settingName = SettingName.NAMES.EmailBackupFrequency permissionName = PermissionName.DailyEmailBackup - muteEmailsSettingName = SettingName.MuteFailedBackupsEmails + muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted break case 'dropbox': - settingName = SettingName.DropboxBackupFrequency + settingName = SettingName.NAMES.DropboxBackupFrequency permissionName = PermissionName.DailyDropboxBackup - muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails + muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted - providerTokenSettingName = SettingName.DropboxBackupToken + providerTokenSettingName = SettingName.NAMES.DropboxBackupToken break case 'one_drive': - settingName = SettingName.OneDriveBackupFrequency + settingName = SettingName.NAMES.OneDriveBackupFrequency permissionName = PermissionName.DailyOneDriveBackup - muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails + muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted - providerTokenSettingName = SettingName.OneDriveBackupToken + providerTokenSettingName = SettingName.NAMES.OneDriveBackupToken break case 'google_drive': - settingName = SettingName.GoogleDriveBackupFrequency + settingName = SettingName.NAMES.GoogleDriveBackupFrequency permissionName = PermissionName.DailyGDriveBackup - muteEmailsSettingName = SettingName.MuteFailedCloudBackupsEmails + muteEmailsSettingName = SettingName.NAMES.MuteFailedCloudBackupsEmails muteEmailsSettingValue = MuteFailedCloudBackupsEmailsOption.Muted - providerTokenSettingName = SettingName.GoogleDriveBackupToken + providerTokenSettingName = SettingName.NAMES.GoogleDriveBackupToken break default: throw new Error(`Not handled backup provider: ${backupProvider}`) diff --git a/packages/auth/bin/user_email_backup.ts b/packages/auth/bin/user_email_backup.ts index ce3288c14..f12ad4c75 100644 --- a/packages/auth/bin/user_email_backup.ts +++ b/packages/auth/bin/user_email_backup.ts @@ -28,7 +28,7 @@ const requestBackups = async ( domainEventPublisher: DomainEventPublisherInterface, ): Promise => { const permissionName = PermissionName.DailyEmailBackup - const muteEmailsSettingName = SettingName.MuteFailedBackupsEmails + const muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails const muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted if (!backupEmail) { diff --git a/packages/auth/migrations/1627638504691-move_mfa_items_to_user_settings.ts b/packages/auth/migrations/1627638504691-move_mfa_items_to_user_settings.ts index ae4f64b2a..ed2dd1dee 100644 --- a/packages/auth/migrations/1627638504691-move_mfa_items_to_user_settings.ts +++ b/packages/auth/migrations/1627638504691-move_mfa_items_to_user_settings.ts @@ -34,7 +34,7 @@ export class moveMfaItemsToUserSettings1627638504691 implements MigrationInterfa const setting = new Setting() setting.uuid = item['uuid'] - setting.name = SettingName.MfaSecret + setting.name = SettingName.NAMES.MfaSecret setting.value = item['content'] if (item['deleted']) { setting.value = null diff --git a/packages/auth/src/Controller/AdminController.ts b/packages/auth/src/Controller/AdminController.ts index 765ae1502..a9156f6a8 100644 --- a/packages/auth/src/Controller/AdminController.ts +++ b/packages/auth/src/Controller/AdminController.ts @@ -69,7 +69,7 @@ export class AdminController extends BaseHttpController { const result = await this.doDeleteSetting.execute({ uuid, userUuid, - settingName: SettingName.MfaSecret, + settingName: SettingName.NAMES.MfaSecret, timestamp: updatedAt, softDelete: true, }) @@ -115,7 +115,7 @@ export class AdminController extends BaseHttpController { const result = await this.doDeleteSetting.execute({ userUuid, - settingName: SettingName.EmailBackupFrequency, + settingName: SettingName.NAMES.EmailBackupFrequency, }) if (result.success) { diff --git a/packages/auth/src/Controller/SubscriptionTokensController.ts b/packages/auth/src/Controller/SubscriptionTokensController.ts index 106773db6..20022cefe 100644 --- a/packages/auth/src/Controller/SubscriptionTokensController.ts +++ b/packages/auth/src/Controller/SubscriptionTokensController.ts @@ -77,7 +77,7 @@ export class SubscriptionTokensController extends BaseHttpController { const user = authenticateTokenResponse.user as User let extensionKey = undefined const extensionKeySetting = await this.settingService.findSettingWithDecryptedValue({ - settingName: SettingName.ExtensionKey, + settingName: SettingName.NAMES.ExtensionKey, userUuid: user.uuid, }) if (extensionKeySetting !== null) { diff --git a/packages/auth/src/Domain/Handler/ExtensionKeyGrantedEventHandler.ts b/packages/auth/src/Domain/Handler/ExtensionKeyGrantedEventHandler.ts index 07c94d729..1da0c3ca3 100644 --- a/packages/auth/src/Domain/Handler/ExtensionKeyGrantedEventHandler.ts +++ b/packages/auth/src/Domain/Handler/ExtensionKeyGrantedEventHandler.ts @@ -54,7 +54,7 @@ export class ExtensionKeyGrantedEventHandler implements DomainEventHandlerInterf await this.settingService.createOrReplace({ user, props: { - name: SettingName.ExtensionKey, + name: SettingName.NAMES.ExtensionKey, unencryptedValue: event.payload.extensionKey, serverEncryptionVersion: EncryptionVersion.Default, sensitive: true, diff --git a/packages/auth/src/Domain/Handler/ListedAccountCreatedEventHandler.ts b/packages/auth/src/Domain/Handler/ListedAccountCreatedEventHandler.ts index 1ab394497..1772259e0 100644 --- a/packages/auth/src/Domain/Handler/ListedAccountCreatedEventHandler.ts +++ b/packages/auth/src/Domain/Handler/ListedAccountCreatedEventHandler.ts @@ -28,7 +28,7 @@ export class ListedAccountCreatedEventHandler implements DomainEventHandlerInter let authSecrets: ListedAuthorSecretsData = [newSecret] const listedAuthorSecretsSetting = await this.settingService.findSettingWithDecryptedValue({ - settingName: SettingName.ListedAuthorSecrets, + settingName: SettingName.NAMES.ListedAuthorSecrets, userUuid: user.uuid, }) if (listedAuthorSecretsSetting !== null) { @@ -40,7 +40,7 @@ export class ListedAccountCreatedEventHandler implements DomainEventHandlerInter await this.settingService.createOrReplace({ user, props: { - name: SettingName.ListedAuthorSecrets, + name: SettingName.NAMES.ListedAuthorSecrets, unencryptedValue: JSON.stringify(authSecrets), sensitive: false, }, diff --git a/packages/auth/src/Domain/Handler/ListedAccountDeletedEventHandler.ts b/packages/auth/src/Domain/Handler/ListedAccountDeletedEventHandler.ts index 945a73728..0bf54a9a1 100644 --- a/packages/auth/src/Domain/Handler/ListedAccountDeletedEventHandler.ts +++ b/packages/auth/src/Domain/Handler/ListedAccountDeletedEventHandler.ts @@ -24,7 +24,7 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter } const listedAuthorSecretsSetting = await this.settingService.findSettingWithDecryptedValue({ - settingName: SettingName.ListedAuthorSecrets, + settingName: SettingName.NAMES.ListedAuthorSecrets, userUuid: user.uuid, }) if (listedAuthorSecretsSetting === null) { @@ -43,7 +43,7 @@ export class ListedAccountDeletedEventHandler implements DomainEventHandlerInter await this.settingService.createOrReplace({ user, props: { - name: SettingName.ListedAuthorSecrets, + name: SettingName.NAMES.ListedAuthorSecrets, unencryptedValue: JSON.stringify(filteredSecrets), sensitive: false, }, diff --git a/packages/auth/src/Domain/Handler/SubscriptionReassignedEventHandler.ts b/packages/auth/src/Domain/Handler/SubscriptionReassignedEventHandler.ts index cb5abd69a..0bec79621 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionReassignedEventHandler.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionReassignedEventHandler.ts @@ -48,7 +48,7 @@ export class SubscriptionReassignedEventHandler implements DomainEventHandlerInt await this.settingService.createOrReplace({ user, props: { - name: SettingName.ExtensionKey, + name: SettingName.NAMES.ExtensionKey, unencryptedValue: event.payload.extensionKey, serverEncryptionVersion: EncryptionVersion.Default, sensitive: true, diff --git a/packages/auth/src/Domain/Handler/SubscriptionSyncRequestedEventHandler.ts b/packages/auth/src/Domain/Handler/SubscriptionSyncRequestedEventHandler.ts index fb0cfbbd4..f55b2c2da 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionSyncRequestedEventHandler.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionSyncRequestedEventHandler.ts @@ -95,7 +95,7 @@ export class SubscriptionSyncRequestedEventHandler implements DomainEventHandler await this.settingService.createOrReplace({ user, props: { - name: SettingName.ExtensionKey, + name: SettingName.NAMES.ExtensionKey, unencryptedValue: event.payload.extensionKey, serverEncryptionVersion: EncryptionVersion.Default, sensitive: true, diff --git a/packages/auth/src/Domain/Session/SessionService.ts b/packages/auth/src/Domain/Session/SessionService.ts index a392846c2..e0fea620f 100644 --- a/packages/auth/src/Domain/Session/SessionService.ts +++ b/packages/auth/src/Domain/Session/SessionService.ts @@ -291,7 +291,7 @@ export class SessionService implements SessionServiceInterface { private async isLoggingUserAgentEnabledOnSessions(user: User): Promise { const loggingSetting = await this.settingService.findSettingWithDecryptedValue({ - settingName: SettingName.LogSessionUserAgent, + settingName: SettingName.NAMES.LogSessionUserAgent, userUuid: user.uuid, }) diff --git a/packages/auth/src/Domain/Setting/FindSettingDTO.ts b/packages/auth/src/Domain/Setting/FindSettingDTO.ts index d341870bb..82f65b727 100644 --- a/packages/auth/src/Domain/Setting/FindSettingDTO.ts +++ b/packages/auth/src/Domain/Setting/FindSettingDTO.ts @@ -1,8 +1,7 @@ import { Uuid } from '@standardnotes/common' -import { SettingName } from '@standardnotes/settings' export type FindSettingDTO = { userUuid: string - settingName: SettingName + settingName: string settingUuid?: Uuid } diff --git a/packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts b/packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts index c3b0af950..f6ca416bc 100644 --- a/packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts +++ b/packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts @@ -71,7 +71,7 @@ describe('SettingInterpreter', () => { it('should trigger session cleanup if user is disabling session user agent logging', async () => { const setting = { - name: SettingName.LogSessionUserAgent, + name: SettingName.NAMES.LogSessionUserAgent, value: LogSessionUserAgentOption.Disabled, } as jest.Mocked @@ -86,7 +86,7 @@ describe('SettingInterpreter', () => { it('should trigger backup if email backup setting is created - emails not muted', async () => { const setting = { - name: SettingName.EmailBackupFrequency, + name: SettingName.NAMES.EmailBackupFrequency, value: EmailBackupFrequency.Daily, } as jest.Mocked @@ -98,11 +98,11 @@ describe('SettingInterpreter', () => { it('should trigger backup if email backup setting is created - emails muted', async () => { const setting = { - name: SettingName.EmailBackupFrequency, + name: SettingName.NAMES.EmailBackupFrequency, value: EmailBackupFrequency.Daily, } as jest.Mocked settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({ - name: SettingName.MuteFailedBackupsEmails, + name: SettingName.NAMES.MuteFailedBackupsEmails, uuid: '6-7-8', value: 'muted', } as jest.Mocked) @@ -115,7 +115,7 @@ describe('SettingInterpreter', () => { it('should not trigger backup if email backup setting is disabled', async () => { const setting = { - name: SettingName.EmailBackupFrequency, + name: SettingName.NAMES.EmailBackupFrequency, value: EmailBackupFrequency.Disabled, } as jest.Mocked settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null) @@ -128,7 +128,7 @@ describe('SettingInterpreter', () => { it('should trigger cloud backup if dropbox backup setting is created', async () => { const setting = { - name: SettingName.DropboxBackupToken, + name: SettingName.NAMES.DropboxBackupToken, value: 'test-token', } as jest.Mocked settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null) @@ -147,11 +147,11 @@ describe('SettingInterpreter', () => { it('should trigger cloud backup if dropbox backup setting is created - muted emails', async () => { const setting = { - name: SettingName.DropboxBackupToken, + name: SettingName.NAMES.DropboxBackupToken, value: 'test-token', } as jest.Mocked settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({ - name: SettingName.MuteFailedCloudBackupsEmails, + name: SettingName.NAMES.MuteFailedCloudBackupsEmails, uuid: '6-7-8', value: 'muted', } as jest.Mocked) @@ -170,7 +170,7 @@ describe('SettingInterpreter', () => { it('should trigger cloud backup if google drive backup setting is created', async () => { const setting = { - name: SettingName.GoogleDriveBackupToken, + name: SettingName.NAMES.GoogleDriveBackupToken, value: 'test-token', } as jest.Mocked settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null) @@ -189,7 +189,7 @@ describe('SettingInterpreter', () => { it('should trigger cloud backup if one drive backup setting is created', async () => { const setting = { - name: SettingName.OneDriveBackupToken, + name: SettingName.NAMES.OneDriveBackupToken, value: 'test-token', } as jest.Mocked settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null) @@ -225,13 +225,13 @@ describe('SettingInterpreter', () => { it('should trigger cloud backup if backup frequency setting is updated and a backup token setting is present', async () => { settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({ - name: SettingName.OneDriveBackupToken, + name: SettingName.NAMES.OneDriveBackupToken, serverEncryptionVersion: 1, value: 'encrypted-backup-token', sensitive: true, } as jest.Mocked) const setting = { - name: SettingName.OneDriveBackupFrequency, + name: SettingName.NAMES.OneDriveBackupFrequency, serverEncryptionVersion: 0, value: 'daily', sensitive: false, @@ -251,13 +251,13 @@ describe('SettingInterpreter', () => { it('should not trigger cloud backup if backup frequency setting is updated as disabled', async () => { settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce({ - name: SettingName.OneDriveBackupToken, + name: SettingName.NAMES.OneDriveBackupToken, serverEncryptionVersion: 1, value: 'encrypted-backup-token', sensitive: true, } as jest.Mocked) const setting = { - name: SettingName.OneDriveBackupFrequency, + name: SettingName.NAMES.OneDriveBackupFrequency, serverEncryptionVersion: 0, value: OneDriveBackupFrequency.Disabled, sensitive: false, @@ -272,7 +272,7 @@ describe('SettingInterpreter', () => { it('should not trigger cloud backup if backup frequency setting is updated and a backup token setting is not present', async () => { settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValueOnce(null) const setting = { - name: SettingName.OneDriveBackupFrequency, + name: SettingName.NAMES.OneDriveBackupFrequency, serverEncryptionVersion: 0, value: 'daily', sensitive: false, diff --git a/packages/auth/src/Domain/Setting/SettingInterpreter.ts b/packages/auth/src/Domain/Setting/SettingInterpreter.ts index 12df19e8d..1d9bd104a 100644 --- a/packages/auth/src/Domain/Setting/SettingInterpreter.ts +++ b/packages/auth/src/Domain/Setting/SettingInterpreter.ts @@ -23,15 +23,15 @@ import { SettingRepositoryInterface } from './SettingRepositoryInterface' @injectable() export class SettingInterpreter implements SettingInterpreterInterface { private readonly cloudBackupTokenSettings = [ - SettingName.DropboxBackupToken, - SettingName.GoogleDriveBackupToken, - SettingName.OneDriveBackupToken, + SettingName.NAMES.DropboxBackupToken, + SettingName.NAMES.GoogleDriveBackupToken, + SettingName.NAMES.OneDriveBackupToken, ] private readonly cloudBackupFrequencySettings = [ - SettingName.DropboxBackupFrequency, - SettingName.GoogleDriveBackupFrequency, - SettingName.OneDriveBackupFrequency, + SettingName.NAMES.DropboxBackupFrequency, + SettingName.NAMES.GoogleDriveBackupFrequency, + SettingName.NAMES.OneDriveBackupFrequency, ] private readonly cloudBackupFrequencyDisabledValues = [ @@ -77,7 +77,7 @@ export class SettingInterpreter implements SettingInterpreterInterface { let userHasEmailsMuted = false let muteEmailsSettingUuid = '' const muteFailedEmailsBackupSetting = await this.settingRepository.findOneByNameAndUserUuid( - SettingName.MuteFailedBackupsEmails, + SettingName.NAMES.MuteFailedBackupsEmails, userUuid, ) if (muteFailedEmailsBackupSetting !== null) { @@ -100,13 +100,13 @@ export class SettingInterpreter implements SettingInterpreterInterface { } private isEnablingEmailBackupSetting(setting: Setting): boolean { - return setting.name === SettingName.EmailBackupFrequency && setting.value !== EmailBackupFrequency.Disabled + return setting.name === SettingName.NAMES.EmailBackupFrequency && setting.value !== EmailBackupFrequency.Disabled } private isEnablingCloudBackupSetting(setting: Setting): boolean { return ( - (this.cloudBackupFrequencySettings.includes(setting.name as SettingName) || - this.cloudBackupTokenSettings.includes(setting.name as SettingName)) && + (this.cloudBackupFrequencySettings.includes(setting.name) || + this.cloudBackupTokenSettings.includes(setting.name)) && !this.cloudBackupFrequencyDisabledValues.includes( setting.value as DropboxBackupFrequency | OneDriveBackupFrequency | GoogleDriveBackupFrequency, ) @@ -114,7 +114,9 @@ export class SettingInterpreter implements SettingInterpreterInterface { } private isDisablingSessionUserAgentLogging(setting: Setting): boolean { - return SettingName.LogSessionUserAgent === setting.name && LogSessionUserAgentOption.Disabled === setting.value + return ( + SettingName.NAMES.LogSessionUserAgent === setting.name && LogSessionUserAgentOption.Disabled === setting.value + ) } private async triggerEmailSubscriptionChange( @@ -144,29 +146,26 @@ export class SettingInterpreter implements SettingInterpreterInterface { let cloudProvider let tokenSettingName switch (setting.name) { - case SettingName.DropboxBackupToken: - case SettingName.DropboxBackupFrequency: + case SettingName.NAMES.DropboxBackupToken: + case SettingName.NAMES.DropboxBackupFrequency: cloudProvider = 'DROPBOX' - tokenSettingName = SettingName.DropboxBackupToken + tokenSettingName = SettingName.NAMES.DropboxBackupToken break - case SettingName.GoogleDriveBackupToken: - case SettingName.GoogleDriveBackupFrequency: + case SettingName.NAMES.GoogleDriveBackupToken: + case SettingName.NAMES.GoogleDriveBackupFrequency: cloudProvider = 'GOOGLE_DRIVE' - tokenSettingName = SettingName.GoogleDriveBackupToken + tokenSettingName = SettingName.NAMES.GoogleDriveBackupToken break - case SettingName.OneDriveBackupToken: - case SettingName.OneDriveBackupFrequency: + case SettingName.NAMES.OneDriveBackupToken: + case SettingName.NAMES.OneDriveBackupFrequency: cloudProvider = 'ONE_DRIVE' - tokenSettingName = SettingName.OneDriveBackupToken + tokenSettingName = SettingName.NAMES.OneDriveBackupToken break } let backupToken = null - if (this.cloudBackupFrequencySettings.includes(setting.name as SettingName)) { - const tokenSetting = await this.settingRepository.findLastByNameAndUserUuid( - tokenSettingName as SettingName, - userUuid, - ) + if (this.cloudBackupFrequencySettings.includes(setting.name)) { + const tokenSetting = await this.settingRepository.findLastByNameAndUserUuid(tokenSettingName as string, userUuid) if (tokenSetting !== null) { backupToken = await this.settingDecrypter.decryptSettingValue(tokenSetting, userUuid) } @@ -183,7 +182,7 @@ export class SettingInterpreter implements SettingInterpreterInterface { let userHasEmailsMuted = false let muteEmailsSettingUuid = '' const muteFailedCloudBackupSetting = await this.settingRepository.findOneByNameAndUserUuid( - SettingName.MuteFailedCloudBackupsEmails, + SettingName.NAMES.MuteFailedCloudBackupsEmails, userUuid, ) if (muteFailedCloudBackupSetting !== null) { diff --git a/packages/auth/src/Domain/Setting/SettingRepositoryInterface.ts b/packages/auth/src/Domain/Setting/SettingRepositoryInterface.ts index b09e4e1e1..edecd808e 100644 --- a/packages/auth/src/Domain/Setting/SettingRepositoryInterface.ts +++ b/packages/auth/src/Domain/Setting/SettingRepositoryInterface.ts @@ -1,16 +1,15 @@ import { ReadStream } from 'fs' -import { SettingName } from '@standardnotes/settings' import { DeleteSettingDto } from '../UseCase/DeleteSetting/DeleteSettingDto' import { Setting } from './Setting' export interface SettingRepositoryInterface { findOneByUuid(uuid: string): Promise - findOneByUuidAndNames(uuid: string, names: SettingName[]): Promise + findOneByUuidAndNames(uuid: string, names: string[]): Promise findOneByNameAndUserUuid(name: string, userUuid: string): Promise findLastByNameAndUserUuid(name: string, userUuid: string): Promise findAllByUserUuid(userUuid: string): Promise - streamAllByNameAndValue(name: SettingName, value: string): Promise + streamAllByNameAndValue(name: string, value: string): Promise deleteByUserUuid(dto: DeleteSettingDto): Promise save(setting: Setting): Promise } diff --git a/packages/auth/src/Domain/Setting/SettingService.spec.ts b/packages/auth/src/Domain/Setting/SettingService.spec.ts index 6888ef895..1e6ed4ce2 100644 --- a/packages/auth/src/Domain/Setting/SettingService.spec.ts +++ b/packages/auth/src/Domain/Setting/SettingService.spec.ts @@ -54,7 +54,7 @@ describe('SettingService', () => { settingsAssociationService.getDefaultSettingsAndValuesForNewUser = jest.fn().mockReturnValue( new Map([ [ - SettingName.MuteSignInEmails, + SettingName.NAMES.MuteSignInEmails, { value: MuteSignInEmailsOption.NotMuted, sensitive: 0, @@ -67,7 +67,7 @@ describe('SettingService', () => { settingsAssociationService.getDefaultSettingsAndValuesForNewVaultAccount = jest.fn().mockReturnValue( new Map([ [ - SettingName.LogSessionUserAgent, + SettingName.NAMES.LogSessionUserAgent, { sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, @@ -173,9 +173,7 @@ describe('SettingService', () => { settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValue(setting) - expect( - await createService().findSettingWithDecryptedValue({ userUuid: '1-2-3', settingName: 'test' as SettingName }), - ).toEqual({ + expect(await createService().findSettingWithDecryptedValue({ userUuid: '1-2-3', settingName: 'test' })).toEqual({ serverEncryptionVersion: 1, value: 'decrypted', }) diff --git a/packages/auth/src/Domain/Setting/SettingService.ts b/packages/auth/src/Domain/Setting/SettingService.ts index 09a1b76e4..654929f47 100644 --- a/packages/auth/src/Domain/Setting/SettingService.ts +++ b/packages/auth/src/Domain/Setting/SettingService.ts @@ -1,4 +1,3 @@ -import { SettingName } from '@standardnotes/settings' import { inject, injectable } from 'inversify' import { Logger } from 'winston' import TYPES from '../../Bootstrap/Types' @@ -74,7 +73,7 @@ export class SettingService implements SettingServiceInterface { const existing = await this.findSettingWithDecryptedValue({ userUuid: user.uuid, - settingName: props.name as SettingName, + settingName: props.name, settingUuid: props.uuid, }) diff --git a/packages/auth/src/Domain/Setting/SettingsAssociationService.spec.ts b/packages/auth/src/Domain/Setting/SettingsAssociationService.spec.ts index 2748072a7..390fea339 100644 --- a/packages/auth/src/Domain/Setting/SettingsAssociationService.spec.ts +++ b/packages/auth/src/Domain/Setting/SettingsAssociationService.spec.ts @@ -11,52 +11,54 @@ describe('SettingsAssociationService', () => { const createService = () => new SettingsAssociationService() it('should tell if a setting is mutable by the client', () => { - expect(createService().isSettingMutableByClient(SettingName.DropboxBackupFrequency)).toBeTruthy() + expect(createService().isSettingMutableByClient(SettingName.NAMES.DropboxBackupFrequency)).toBeTruthy() }) it('should tell if a setting is immutable by the client', () => { - expect(createService().isSettingMutableByClient(SettingName.ListedAuthorSecrets)).toBeFalsy() + expect(createService().isSettingMutableByClient(SettingName.NAMES.ListedAuthorSecrets)).toBeFalsy() }) it('should return default encryption version for a setting which enecryption version is not strictly defined', () => { - expect(createService().getEncryptionVersionForSetting(SettingName.MfaSecret)).toEqual(EncryptionVersion.Default) + expect(createService().getEncryptionVersionForSetting(SettingName.NAMES.MfaSecret)).toEqual( + EncryptionVersion.Default, + ) }) it('should return a defined encryption version for a setting which enecryption version is strictly defined', () => { - expect(createService().getEncryptionVersionForSetting(SettingName.EmailBackupFrequency)).toEqual( + expect(createService().getEncryptionVersionForSetting(SettingName.NAMES.EmailBackupFrequency)).toEqual( EncryptionVersion.Unencrypted, ) }) it('should return default sensitivity for a setting which sensitivity is not strictly defined', () => { - expect(createService().getSensitivityForSetting(SettingName.DropboxBackupToken)).toBeTruthy() + expect(createService().getSensitivityForSetting(SettingName.NAMES.DropboxBackupToken)).toBeTruthy() }) it('should return a defined sensitivity for a setting which sensitivity is strictly defined', () => { - expect(createService().getSensitivityForSetting(SettingName.DropboxBackupFrequency)).toBeFalsy() + expect(createService().getSensitivityForSetting(SettingName.NAMES.DropboxBackupFrequency)).toBeFalsy() }) it('should return the default set of settings for a newly registered user', () => { const settings = createService().getDefaultSettingsAndValuesForNewUser() - const flatSettings = [...(settings as Map).keys()] + const flatSettings = [...(settings as Map).keys()] expect(flatSettings).toEqual(['MUTE_SIGN_IN_EMAILS', 'MUTE_MARKETING_EMAILS', 'LOG_SESSION_USER_AGENT']) }) it('should return the default set of settings for a newly registered vault account', () => { const settings = createService().getDefaultSettingsAndValuesForNewVaultAccount() - const flatSettings = [...(settings as Map).keys()] + const flatSettings = [...(settings as Map).keys()] expect(flatSettings).toEqual(['MUTE_SIGN_IN_EMAILS', 'MUTE_MARKETING_EMAILS', 'LOG_SESSION_USER_AGENT']) - expect(settings.get(SettingName.LogSessionUserAgent)?.value).toEqual('disabled') + expect(settings.get(SettingName.NAMES.LogSessionUserAgent)?.value).toEqual('disabled') }) it('should return a permission name associated to a given setting', () => { - expect(createService().getPermissionAssociatedWithSetting(SettingName.EmailBackupFrequency)).toEqual( + expect(createService().getPermissionAssociatedWithSetting(SettingName.NAMES.EmailBackupFrequency)).toEqual( PermissionName.DailyEmailBackup, ) }) it('should not return a permission name if not associated to a given setting', () => { - expect(createService().getPermissionAssociatedWithSetting(SettingName.ExtensionKey)).toBeUndefined() + expect(createService().getPermissionAssociatedWithSetting(SettingName.NAMES.ExtensionKey)).toBeUndefined() }) }) diff --git a/packages/auth/src/Domain/Setting/SettingsAssociationService.ts b/packages/auth/src/Domain/Setting/SettingsAssociationService.ts index 5894dbf38..e6b1fdcd1 100644 --- a/packages/auth/src/Domain/Setting/SettingsAssociationService.ts +++ b/packages/auth/src/Domain/Setting/SettingsAssociationService.ts @@ -15,39 +15,39 @@ import { SettingsAssociationServiceInterface } from './SettingsAssociationServic @injectable() export class SettingsAssociationService implements SettingsAssociationServiceInterface { private readonly UNENCRYPTED_SETTINGS = [ - SettingName.EmailBackupFrequency, - SettingName.MuteFailedBackupsEmails, - SettingName.MuteFailedCloudBackupsEmails, - SettingName.MuteSignInEmails, - SettingName.MuteMarketingEmails, - SettingName.DropboxBackupFrequency, - SettingName.GoogleDriveBackupFrequency, - SettingName.OneDriveBackupFrequency, - SettingName.LogSessionUserAgent, + SettingName.NAMES.EmailBackupFrequency, + SettingName.NAMES.MuteFailedBackupsEmails, + SettingName.NAMES.MuteFailedCloudBackupsEmails, + SettingName.NAMES.MuteSignInEmails, + SettingName.NAMES.MuteMarketingEmails, + SettingName.NAMES.DropboxBackupFrequency, + SettingName.NAMES.GoogleDriveBackupFrequency, + SettingName.NAMES.OneDriveBackupFrequency, + SettingName.NAMES.LogSessionUserAgent, ] private readonly UNSENSITIVE_SETTINGS = [ - SettingName.DropboxBackupFrequency, - SettingName.GoogleDriveBackupFrequency, - SettingName.OneDriveBackupFrequency, - SettingName.EmailBackupFrequency, - SettingName.MuteFailedBackupsEmails, - SettingName.MuteFailedCloudBackupsEmails, - SettingName.MuteSignInEmails, - SettingName.MuteMarketingEmails, - SettingName.ListedAuthorSecrets, - SettingName.LogSessionUserAgent, + SettingName.NAMES.DropboxBackupFrequency, + SettingName.NAMES.GoogleDriveBackupFrequency, + SettingName.NAMES.OneDriveBackupFrequency, + SettingName.NAMES.EmailBackupFrequency, + SettingName.NAMES.MuteFailedBackupsEmails, + SettingName.NAMES.MuteFailedCloudBackupsEmails, + SettingName.NAMES.MuteSignInEmails, + SettingName.NAMES.MuteMarketingEmails, + SettingName.NAMES.ListedAuthorSecrets, + SettingName.NAMES.LogSessionUserAgent, ] - private readonly CLIENT_IMMUTABLE_SETTINGS = [SettingName.ListedAuthorSecrets] + private readonly CLIENT_IMMUTABLE_SETTINGS = [SettingName.NAMES.ListedAuthorSecrets] - private readonly permissionsAssociatedWithSettings = new Map([ - [SettingName.EmailBackupFrequency, PermissionName.DailyEmailBackup], + private readonly permissionsAssociatedWithSettings = new Map([ + [SettingName.NAMES.EmailBackupFrequency, PermissionName.DailyEmailBackup], ]) - private readonly defaultSettings = new Map([ + private readonly defaultSettings = new Map([ [ - SettingName.MuteSignInEmails, + SettingName.NAMES.MuteSignInEmails, { sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, @@ -56,7 +56,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt }, ], [ - SettingName.MuteMarketingEmails, + SettingName.NAMES.MuteMarketingEmails, { sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, @@ -65,7 +65,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt }, ], [ - SettingName.LogSessionUserAgent, + SettingName.NAMES.LogSessionUserAgent, { sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, @@ -75,9 +75,9 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt ], ]) - private readonly vaultAccountDefaultSettingsOverwrites = new Map([ + private readonly vaultAccountDefaultSettingsOverwrites = new Map([ [ - SettingName.LogSessionUserAgent, + SettingName.NAMES.LogSessionUserAgent, { sensitive: false, serverEncryptionVersion: EncryptionVersion.Unencrypted, @@ -87,7 +87,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt ], ]) - isSettingMutableByClient(settingName: SettingName): boolean { + isSettingMutableByClient(settingName: string): boolean { if (this.CLIENT_IMMUTABLE_SETTINGS.includes(settingName)) { return false } @@ -95,7 +95,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt return true } - getSensitivityForSetting(settingName: SettingName): boolean { + getSensitivityForSetting(settingName: string): boolean { if (this.UNSENSITIVE_SETTINGS.includes(settingName)) { return false } @@ -103,7 +103,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt return true } - getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion { + getEncryptionVersionForSetting(settingName: string): EncryptionVersion { if (this.UNENCRYPTED_SETTINGS.includes(settingName)) { return EncryptionVersion.Unencrypted } @@ -111,7 +111,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt return EncryptionVersion.Default } - getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined { + getPermissionAssociatedWithSetting(settingName: string): PermissionName | undefined { if (!this.permissionsAssociatedWithSettings.has(settingName)) { return undefined } @@ -119,11 +119,11 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt return this.permissionsAssociatedWithSettings.get(settingName) } - getDefaultSettingsAndValuesForNewUser(): Map { + getDefaultSettingsAndValuesForNewUser(): Map { return this.defaultSettings } - getDefaultSettingsAndValuesForNewVaultAccount(): Map { + getDefaultSettingsAndValuesForNewVaultAccount(): Map { const defaultVaultSettings = new Map(this.defaultSettings) for (const vaultAccountDefaultSettingOverwriteKey of this.vaultAccountDefaultSettingsOverwrites.keys()) { diff --git a/packages/auth/src/Domain/Setting/SettingsAssociationServiceInterface.ts b/packages/auth/src/Domain/Setting/SettingsAssociationServiceInterface.ts index 3e7cce194..69c1f208d 100644 --- a/packages/auth/src/Domain/Setting/SettingsAssociationServiceInterface.ts +++ b/packages/auth/src/Domain/Setting/SettingsAssociationServiceInterface.ts @@ -1,13 +1,13 @@ import { PermissionName } from '@standardnotes/features' -import { SettingName, SubscriptionSettingName } from '@standardnotes/settings' +import { SubscriptionSettingName } from '@standardnotes/settings' import { EncryptionVersion } from '../Encryption/EncryptionVersion' import { SettingDescription } from './SettingDescription' export interface SettingsAssociationServiceInterface { - getDefaultSettingsAndValuesForNewUser(): Map - getDefaultSettingsAndValuesForNewVaultAccount(): Map - getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined - getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion - getSensitivityForSetting(settingName: SettingName): boolean - isSettingMutableByClient(settingName: SettingName | SubscriptionSettingName): boolean + getDefaultSettingsAndValuesForNewUser(): Map + getDefaultSettingsAndValuesForNewVaultAccount(): Map + getPermissionAssociatedWithSetting(settingName: string): PermissionName | undefined + getEncryptionVersionForSetting(settingName: string): EncryptionVersion + getSensitivityForSetting(settingName: string): boolean + isSettingMutableByClient(settingName: string | SubscriptionSettingName): boolean } diff --git a/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.spec.ts b/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.spec.ts index 0d7ec9540..ebfa3ba02 100644 --- a/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.spec.ts +++ b/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.spec.ts @@ -45,12 +45,12 @@ describe('GetSetting', () => { it('should not retrieve a sensitive setting for user', async () => { setting = { sensitive: true, - name: SettingName.MfaSecret, + name: SettingName.NAMES.MfaSecret, } as jest.Mocked settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(setting) - expect(await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.MfaSecret })).toEqual({ + expect(await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.MfaSecret })).toEqual({ success: true, sensitive: true, }) @@ -59,7 +59,7 @@ describe('GetSetting', () => { it('should retrieve a sensitive setting for user if explicitly told to', async () => { setting = { sensitive: true, - name: SettingName.MfaSecret, + name: SettingName.NAMES.MfaSecret, } as jest.Mocked settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(setting) diff --git a/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.ts b/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.ts index e78041fec..4a2cc15b7 100644 --- a/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.ts +++ b/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.ts @@ -1,4 +1,3 @@ -import { SettingName } from '@standardnotes/settings' import { inject, injectable } from 'inversify' import { GetSettingDto } from './GetSettingDto' import { GetSettingResponse } from './GetSettingResponse' @@ -19,7 +18,7 @@ export class GetSetting implements UseCaseInterface { const setting = await this.settingService.findSettingWithDecryptedValue({ userUuid, - settingName: settingName as SettingName, + settingName: settingName, }) if (setting === null) { diff --git a/packages/auth/src/Domain/UseCase/GetSettings/GetSettings.spec.ts b/packages/auth/src/Domain/UseCase/GetSettings/GetSettings.spec.ts index 60c92195e..22cd2b93b 100644 --- a/packages/auth/src/Domain/UseCase/GetSettings/GetSettings.spec.ts +++ b/packages/auth/src/Domain/UseCase/GetSettings/GetSettings.spec.ts @@ -31,7 +31,7 @@ describe('GetSettings', () => { } as jest.Mocked mfaSetting = { - name: SettingName.MfaSecret, + name: SettingName.NAMES.MfaSecret, updatedAt: 122, sensitive: true, } as jest.Mocked diff --git a/packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.spec.ts b/packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.spec.ts index 8caf2ab15..93a42c7aa 100644 --- a/packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.spec.ts +++ b/packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.spec.ts @@ -59,7 +59,7 @@ describe('UpdateSetting', () => { it('should create a setting', async () => { const props = { - name: SettingName.ExtensionKey, + name: SettingName.NAMES.ExtensionKey, unencryptedValue: 'test-setting-value', serverEncryptionVersion: EncryptionVersion.Default, sensitive: false, @@ -88,7 +88,7 @@ describe('UpdateSetting', () => { userRepository.findOneByUuid = jest.fn().mockReturnValue(null) const props = { - name: SettingName.ExtensionKey, + name: SettingName.NAMES.ExtensionKey, unencryptedValue: 'test-setting-value', serverEncryptionVersion: EncryptionVersion.Unencrypted, sensitive: false, @@ -136,7 +136,7 @@ describe('UpdateSetting', () => { roleService.userHasPermission = jest.fn().mockReturnValue(false) const props = { - name: SettingName.ExtensionKey, + name: SettingName.NAMES.ExtensionKey, unencryptedValue: 'test-setting-value', serverEncryptionVersion: EncryptionVersion.Unencrypted, sensitive: false, @@ -159,7 +159,7 @@ describe('UpdateSetting', () => { settingsAssociationService.isSettingMutableByClient = jest.fn().mockReturnValue(false) const props = { - name: SettingName.ExtensionKey, + name: SettingName.NAMES.ExtensionKey, unencryptedValue: 'test-setting-value', serverEncryptionVersion: EncryptionVersion.Unencrypted, sensitive: false, diff --git a/packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.ts b/packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.ts index b626703f7..6955290d4 100644 --- a/packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.ts +++ b/packages/auth/src/Domain/UseCase/UpdateSetting/UpdateSetting.ts @@ -25,7 +25,7 @@ export class UpdateSetting implements UseCaseInterface { ) {} async execute(dto: UpdateSettingDto): Promise { - if (!Object.values(SettingName).includes(dto.props.name as SettingName)) { + if (!Object.values(SettingName.NAMES).includes(dto.props.name)) { return { success: false, error: { @@ -51,7 +51,7 @@ export class UpdateSetting implements UseCaseInterface { } } - if (!(await this.userHasPermissionToUpdateSetting(user, props.name as SettingName))) { + if (!(await this.userHasPermissionToUpdateSetting(user, props.name))) { return { success: false, error: { @@ -61,10 +61,8 @@ export class UpdateSetting implements UseCaseInterface { } } - props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting( - props.name as SettingName, - ) - props.sensitive = this.settingsAssociationService.getSensitivityForSetting(props.name as SettingName) + props.serverEncryptionVersion = this.settingsAssociationService.getEncryptionVersionForSetting(props.name) + props.sensitive = this.settingsAssociationService.getSensitivityForSetting(props.name) const response = await this.settingService.createOrReplace({ user, @@ -91,8 +89,8 @@ export class UpdateSetting implements UseCaseInterface { throw new Error(`Unrecognized status: ${exhaustiveCheck}!`) } - private async userHasPermissionToUpdateSetting(user: User, settingName: SettingName): Promise { - const settingIsMutableByClient = await this.settingsAssociationService.isSettingMutableByClient(settingName) + private async userHasPermissionToUpdateSetting(user: User, settingName: string): Promise { + const settingIsMutableByClient = this.settingsAssociationService.isSettingMutableByClient(settingName) if (!settingIsMutableByClient) { return false } diff --git a/packages/auth/src/Domain/UseCase/VerifyMFA.spec.ts b/packages/auth/src/Domain/UseCase/VerifyMFA.spec.ts index a2a8f19be..4111cecfd 100644 --- a/packages/auth/src/Domain/UseCase/VerifyMFA.spec.ts +++ b/packages/auth/src/Domain/UseCase/VerifyMFA.spec.ts @@ -36,7 +36,7 @@ describe('VerifyMFA', () => { lockRepository.lockSuccessfullOTP = jest.fn() setting = { - name: SettingName.MfaSecret, + name: SettingName.NAMES.MfaSecret, value: 'shhhh', } as jest.Mocked @@ -58,7 +58,7 @@ describe('VerifyMFA', () => { it('should pass MFA verification if user has MFA deleted', async () => { setting = { - name: SettingName.MfaSecret, + name: SettingName.NAMES.MfaSecret, value: null, } as jest.Mocked @@ -148,7 +148,7 @@ describe('VerifyMFA', () => { it('should not pass MFA verification if mfa is not correct', async () => { setting = { - name: SettingName.MfaSecret, + name: SettingName.NAMES.MfaSecret, value: 'shhhh2', } as jest.Mocked diff --git a/packages/auth/src/Domain/UseCase/VerifyMFA.ts b/packages/auth/src/Domain/UseCase/VerifyMFA.ts index 0a2a4f7c6..30f819741 100644 --- a/packages/auth/src/Domain/UseCase/VerifyMFA.ts +++ b/packages/auth/src/Domain/UseCase/VerifyMFA.ts @@ -50,7 +50,7 @@ export class VerifyMFA implements UseCaseInterface { const mfaSecret = await this.settingService.findSettingWithDecryptedValue({ userUuid: user.uuid, - settingName: SettingName.MfaSecret, + settingName: SettingName.NAMES.MfaSecret, }) if (mfaSecret === null || mfaSecret.value === null) { return { diff --git a/packages/auth/src/Domain/UseCase/VerifyPredicate/VerifyPredicate.ts b/packages/auth/src/Domain/UseCase/VerifyPredicate/VerifyPredicate.ts index ebb5a10cb..b6b4a4269 100644 --- a/packages/auth/src/Domain/UseCase/VerifyPredicate/VerifyPredicate.ts +++ b/packages/auth/src/Domain/UseCase/VerifyPredicate/VerifyPredicate.ts @@ -40,7 +40,10 @@ export class VerifyPredicate implements UseCaseInterface { } private async hasUserEnabledEmailBackups(userUuid: Uuid): Promise { - const setting = await this.settingRepository.findOneByNameAndUserUuid(SettingName.EmailBackupFrequency, userUuid) + const setting = await this.settingRepository.findOneByNameAndUserUuid( + SettingName.NAMES.EmailBackupFrequency, + userUuid, + ) if (setting === null || setting.value === EmailBackupFrequency.Disabled) { return false diff --git a/packages/auth/src/Infra/MySQL/MySQLSettingRepository.ts b/packages/auth/src/Infra/MySQL/MySQLSettingRepository.ts index 0e1be9e81..1d4bca4ba 100644 --- a/packages/auth/src/Infra/MySQL/MySQLSettingRepository.ts +++ b/packages/auth/src/Infra/MySQL/MySQLSettingRepository.ts @@ -1,4 +1,3 @@ -import { SettingName } from '@standardnotes/settings' import { ReadStream } from 'fs' import { inject, injectable } from 'inversify' import { Repository } from 'typeorm' @@ -18,7 +17,7 @@ export class MySQLSettingRepository implements SettingRepositoryInterface { return this.ormRepository.save(setting) } - async findOneByUuidAndNames(uuid: string, names: SettingName[]): Promise { + async findOneByUuidAndNames(uuid: string, names: string[]): Promise { return this.ormRepository .createQueryBuilder('setting') .where('setting.uuid = :uuid AND setting.name IN (:...names)', { @@ -28,7 +27,7 @@ export class MySQLSettingRepository implements SettingRepositoryInterface { .getOne() } - async streamAllByNameAndValue(name: SettingName, value: string): Promise { + async streamAllByNameAndValue(name: string, value: string): Promise { return this.ormRepository .createQueryBuilder('setting') .where('setting.name = :name AND setting.value = :value', { diff --git a/packages/domain-core/src/Domain/Common/RoleNameCollection.spec.ts b/packages/domain-core/src/Domain/Common/RoleNameCollection.spec.ts index 484a5dfef..936c8d183 100644 --- a/packages/domain-core/src/Domain/Common/RoleNameCollection.spec.ts +++ b/packages/domain-core/src/Domain/Common/RoleNameCollection.spec.ts @@ -35,6 +35,19 @@ describe('RoleNameCollection', () => { expect(valueOrError.getValue().equals(roles2)).toBeFalsy() }) + it('should tell if collections are not equal', () => { + const roles1 = [RoleName.create('PRO_USER').getValue(), RoleName.create('PLUS_USER').getValue()] + + const roles2 = RoleNameCollection.create([ + RoleName.create('PRO_USER').getValue(), + RoleName.create('PLUS_USER').getValue(), + RoleName.create('CORE_USER').getValue(), + ]).getValue() + + const valueOrError = RoleNameCollection.create(roles1) + expect(valueOrError.getValue().equals(roles2)).toBeFalsy() + }) + it('should tell if collections are equal', () => { const roles1 = [ RoleName.create(RoleName.NAMES.ProUser).getValue(), diff --git a/packages/domain-core/src/Domain/Common/Timestamps.spec.ts b/packages/domain-core/src/Domain/Common/Timestamps.spec.ts new file mode 100644 index 000000000..21cf25893 --- /dev/null +++ b/packages/domain-core/src/Domain/Common/Timestamps.spec.ts @@ -0,0 +1,21 @@ +import { Timestamps } from './Timestamps' + +describe('Timestamps', () => { + it('should create a value object', () => { + const valueOrError = Timestamps.create(1, 2) + + expect(valueOrError.isFailed()).toBeFalsy() + expect(valueOrError.getValue().createdAt).toEqual(1) + expect(valueOrError.getValue().updatedAt).toEqual(2) + }) + + it('should not create an invalid value object', () => { + let valueOrError = Timestamps.create(null as unknown as number, 'b' as unknown as number) + + expect(valueOrError.isFailed()).toBeTruthy() + + valueOrError = Timestamps.create(2, 'a' as unknown as number) + + expect(valueOrError.isFailed()).toBeTruthy() + }) +}) diff --git a/packages/domain-core/src/Domain/Common/Timestamps.ts b/packages/domain-core/src/Domain/Common/Timestamps.ts new file mode 100644 index 000000000..4682f0517 --- /dev/null +++ b/packages/domain-core/src/Domain/Common/Timestamps.ts @@ -0,0 +1,30 @@ +import { Result } from '../Core/Result' +import { ValueObject } from '../Core/ValueObject' +import { TimestampsProps } from './TimestampsProps' + +export class Timestamps extends ValueObject { + get createdAt(): number { + return this.props.createdAt + } + + get updatedAt(): number { + return this.props.updatedAt + } + + private constructor(props: TimestampsProps) { + super(props) + } + + static create(createdAt: number, updatedAt: number): Result { + if (isNaN(createdAt) || createdAt === null || createdAt === undefined) { + return Result.fail( + `Could not create Timestamps. Creation date should be a number, given: ${createdAt}`, + ) + } + if (isNaN(updatedAt) || updatedAt === null || updatedAt === undefined) { + return Result.fail(`Could not create Timestamps. Update date should be a number, given: ${createdAt}`) + } + + return Result.ok(new Timestamps({ createdAt, updatedAt })) + } +} diff --git a/packages/domain-core/src/Domain/Common/TimestampsProps.ts b/packages/domain-core/src/Domain/Common/TimestampsProps.ts new file mode 100644 index 000000000..ea52e32e4 --- /dev/null +++ b/packages/domain-core/src/Domain/Common/TimestampsProps.ts @@ -0,0 +1,4 @@ +export interface TimestampsProps { + createdAt: number + updatedAt: number +} diff --git a/packages/domain-core/src/Domain/Encryption/EncryptionVersion.spec.ts b/packages/domain-core/src/Domain/Encryption/EncryptionVersion.spec.ts new file mode 100644 index 000000000..125a95f0a --- /dev/null +++ b/packages/domain-core/src/Domain/Encryption/EncryptionVersion.spec.ts @@ -0,0 +1,28 @@ +import { EncryptionVersion } from './EncryptionVersion' + +describe('EncryptionVersion', () => { + it('should create a value object', () => { + const valueOrError = EncryptionVersion.create(1) + + expect(valueOrError.isFailed()).toBeFalsy() + expect(valueOrError.getValue().value).toEqual(1) + }) + + it('should not create an invalid value object', () => { + let valueOrError = EncryptionVersion.create('asd' as unknown as number) + + expect(valueOrError.isFailed()).toBeTruthy() + + valueOrError = EncryptionVersion.create(null as unknown as number) + + expect(valueOrError.isFailed()).toBeTruthy() + + valueOrError = EncryptionVersion.create(undefined as unknown as number) + + expect(valueOrError.isFailed()).toBeTruthy() + + valueOrError = EncryptionVersion.create(754) + + expect(valueOrError.isFailed()).toBeTruthy() + }) +}) diff --git a/packages/domain-core/src/Domain/Encryption/EncryptionVersion.ts b/packages/domain-core/src/Domain/Encryption/EncryptionVersion.ts new file mode 100644 index 000000000..07e648211 --- /dev/null +++ b/packages/domain-core/src/Domain/Encryption/EncryptionVersion.ts @@ -0,0 +1,33 @@ +import { Result } from '../Core/Result' +import { ValueObject } from '../Core/ValueObject' +import { EncryptionVersionProps } from './EncryptionVersionProps' + +export class EncryptionVersion extends ValueObject { + static readonly VERSIONS = { + Unencrypted: 0, + Default: 1, + } + + get value(): number { + return this.props.value + } + + private constructor(props: EncryptionVersionProps) { + super(props) + } + + static create(version: number): Result { + if ( + isNaN(version) || + version === null || + version === undefined || + !Object.values(this.VERSIONS).includes(version) + ) { + return Result.fail( + `Could not create EncryptionVersion. Version should be a number, given: ${version}`, + ) + } + + return Result.ok(new EncryptionVersion({ value: version })) + } +} diff --git a/packages/domain-core/src/Domain/Encryption/EncryptionVersionProps.ts b/packages/domain-core/src/Domain/Encryption/EncryptionVersionProps.ts new file mode 100644 index 000000000..d7965f802 --- /dev/null +++ b/packages/domain-core/src/Domain/Encryption/EncryptionVersionProps.ts @@ -0,0 +1,3 @@ +export interface EncryptionVersionProps { + value: number +} diff --git a/packages/domain-core/src/Domain/index.ts b/packages/domain-core/src/Domain/index.ts index d50f9a794..af45a1c0c 100644 --- a/packages/domain-core/src/Domain/index.ts +++ b/packages/domain-core/src/Domain/index.ts @@ -8,6 +8,8 @@ export * from './Common/RoleNameCollection' export * from './Common/RoleNameCollectionProps' export * from './Common/Username' export * from './Common/UsernameProps' +export * from './Common/Timestamps' +export * from './Common/TimestampsProps' export * from './Common/Uuid' export * from './Common/UuidProps' @@ -23,6 +25,9 @@ export * from './Core/ValueObjectProps' export * from './Email/EmailLevel' export * from './Email/EmailLevelProps' +export * from './Encryption/EncryptionVersion' +export * from './Encryption/EncryptionVersionProps' + export * from './Mapping/MapperInterface' export * from './Subscription/SubscriptionPlanName' diff --git a/packages/settings/package.json b/packages/settings/package.json index bee9407f9..6d08b5b66 100644 --- a/packages/settings/package.json +++ b/packages/settings/package.json @@ -29,6 +29,7 @@ "typescript": "^4.8.4" }, "dependencies": { + "@standardnotes/domain-core": "workspace:^", "reflect-metadata": "^0.1.13" } } diff --git a/packages/settings/src/Domain/Setting/SensitiveSettingName.ts b/packages/settings/src/Domain/Setting/SensitiveSettingName.ts deleted file mode 100644 index de0a88455..000000000 --- a/packages/settings/src/Domain/Setting/SensitiveSettingName.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { SettingName } from './SettingName' - -export type SensitiveSettingName = SettingName.MfaSecret | SettingName.ExtensionKey diff --git a/packages/settings/src/Domain/Setting/Setting.ts b/packages/settings/src/Domain/Setting/Setting.ts new file mode 100644 index 000000000..fd8c359bb --- /dev/null +++ b/packages/settings/src/Domain/Setting/Setting.ts @@ -0,0 +1,17 @@ +import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core' + +import { SettingProps } from './SettingProps' + +export class Setting extends Entity { + get id(): UniqueEntityId { + return this._id + } + + private constructor(props: SettingProps, id?: UniqueEntityId) { + super(props, id) + } + + static create(props: SettingProps, id?: UniqueEntityId): Result { + return Result.ok(new Setting(props, id)) + } +} diff --git a/packages/settings/src/Domain/Setting/SettingName.ts b/packages/settings/src/Domain/Setting/SettingName.ts index f9451eb94..579f0cf0c 100644 --- a/packages/settings/src/Domain/Setting/SettingName.ts +++ b/packages/settings/src/Domain/Setting/SettingName.ts @@ -1,17 +1,44 @@ -export enum SettingName { - MfaSecret = 'MFA_SECRET', - ExtensionKey = 'EXTENSION_KEY', - EmailBackupFrequency = 'EMAIL_BACKUP_FREQUENCY', - DropboxBackupFrequency = 'DROPBOX_BACKUP_FREQUENCY', - DropboxBackupToken = 'DROPBOX_BACKUP_TOKEN', - OneDriveBackupFrequency = 'ONE_DRIVE_BACKUP_FREQUENCY', - OneDriveBackupToken = 'ONE_DRIVE_BACKUP_TOKEN', - GoogleDriveBackupFrequency = 'GOOGLE_DRIVE_BACKUP_FREQUENCY', - GoogleDriveBackupToken = 'GOOGLE_DRIVE_BACKUP_TOKEN', - MuteFailedBackupsEmails = 'MUTE_FAILED_BACKUPS_EMAILS', - MuteFailedCloudBackupsEmails = 'MUTE_FAILED_CLOUD_BACKUPS_EMAILS', - MuteSignInEmails = 'MUTE_SIGN_IN_EMAILS', - MuteMarketingEmails = 'MUTE_MARKETING_EMAILS', - ListedAuthorSecrets = 'LISTED_AUTHOR_SECRETS', - LogSessionUserAgent = 'LOG_SESSION_USER_AGENT', +import { Result, ValueObject } from '@standardnotes/domain-core' + +import { SettingNameProps } from './SettingNameProps' + +export class SettingName extends ValueObject { + static readonly NAMES = { + MfaSecret: 'MFA_SECRET', + ExtensionKey: 'EXTENSION_KEY', + EmailBackupFrequency: 'EMAIL_BACKUP_FREQUENCY', + DropboxBackupFrequency: 'DROPBOX_BACKUP_FREQUENCY', + DropboxBackupToken: 'DROPBOX_BACKUP_TOKEN', + OneDriveBackupFrequency: 'ONE_DRIVE_BACKUP_FREQUENCY', + OneDriveBackupToken: 'ONE_DRIVE_BACKUP_TOKEN', + GoogleDriveBackupFrequency: 'GOOGLE_DRIVE_BACKUP_FREQUENCY', + GoogleDriveBackupToken: 'GOOGLE_DRIVE_BACKUP_TOKEN', + MuteFailedBackupsEmails: 'MUTE_FAILED_BACKUPS_EMAILS', + MuteFailedCloudBackupsEmails: 'MUTE_FAILED_CLOUD_BACKUPS_EMAILS', + MuteSignInEmails: 'MUTE_SIGN_IN_EMAILS', + MuteMarketingEmails: 'MUTE_MARKETING_EMAILS', + ListedAuthorSecrets: 'LISTED_AUTHOR_SECRETS', + LogSessionUserAgent: 'LOG_SESSION_USER_AGENT', + } + + get value(): string { + return this.props.value + } + + isSensitive(): boolean { + return [SettingName.NAMES.MfaSecret, SettingName.NAMES.ExtensionKey].includes(this.value) + } + + private constructor(props: SettingNameProps) { + super(props) + } + + static create(name: string): Result { + const isValidName = Object.values(this.NAMES).includes(name) + if (!isValidName) { + return Result.fail(`Invalid setting name: ${name}`) + } else { + return Result.ok(new SettingName({ value: name })) + } + } } diff --git a/packages/settings/src/Domain/Setting/SettingNameProps.ts b/packages/settings/src/Domain/Setting/SettingNameProps.ts new file mode 100644 index 000000000..51f006672 --- /dev/null +++ b/packages/settings/src/Domain/Setting/SettingNameProps.ts @@ -0,0 +1,3 @@ +export interface SettingNameProps { + value: string +} diff --git a/packages/settings/src/Domain/Setting/SettingProps.ts b/packages/settings/src/Domain/Setting/SettingProps.ts new file mode 100644 index 000000000..edb4b38aa --- /dev/null +++ b/packages/settings/src/Domain/Setting/SettingProps.ts @@ -0,0 +1,12 @@ +import { EncryptionVersion, Timestamps, Uuid } from '@standardnotes/domain-core' + +import { SettingName } from './SettingName' + +export interface SettingProps { + userUuid: Uuid + name: SettingName + value: string | null + serverEncryptionVersion: EncryptionVersion + sensitive: boolean + timestamps: Timestamps +} diff --git a/packages/settings/src/Domain/index.ts b/packages/settings/src/Domain/index.ts index ef06eb721..9a7475300 100644 --- a/packages/settings/src/Domain/index.ts +++ b/packages/settings/src/Domain/index.ts @@ -9,6 +9,5 @@ export * from './MuteFailedCloudBackupsEmails/MuteFailedCloudBackupsEmailsOption export * from './MuteMarketingEmails/MuteMarketingEmailsOption' export * from './MuteSignInEmails/MuteSignInEmailsOption' export * from './OneDriveBackupFrequency/OneDriveBackupFrequency' -export * from './Setting/SensitiveSettingName' export * from './Setting/SettingName' export * from './Setting/SubscriptionSettingName' diff --git a/packages/syncing-server/src/Infra/HTTP/AuthHttpService.spec.ts b/packages/syncing-server/src/Infra/HTTP/AuthHttpService.spec.ts index 5e7fc84c4..55ce6a7f1 100644 --- a/packages/syncing-server/src/Infra/HTTP/AuthHttpService.spec.ts +++ b/packages/syncing-server/src/Infra/HTTP/AuthHttpService.spec.ts @@ -49,7 +49,7 @@ describe('AuthHttpService', () => { }, }) - await createService().getUserSetting('1-2-3', SettingName.MuteFailedBackupsEmails) + await createService().getUserSetting('1-2-3', SettingName.NAMES.MuteFailedBackupsEmails) expect(httpClient.request).toHaveBeenCalledWith({ method: 'GET', @@ -64,7 +64,7 @@ describe('AuthHttpService', () => { it('should throw an error if a request to auth service in order to get user setting fails', async () => { let error = null try { - await createService().getUserSetting('1-2-3', SettingName.MuteFailedCloudBackupsEmails) + await createService().getUserSetting('1-2-3', SettingName.NAMES.MuteFailedCloudBackupsEmails) } catch (caughtError) { error = caughtError } diff --git a/yarn.lock b/yarn.lock index 3748a9664..f7e5fb092 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2359,6 +2359,7 @@ __metadata: version: 0.0.0-use.local resolution: "@standardnotes/settings@workspace:packages/settings" dependencies: + "@standardnotes/domain-core": "workspace:^" "@typescript-eslint/eslint-plugin": "npm:^5.30.0" eslint-plugin-prettier: "npm:^4.2.1" reflect-metadata: "npm:^0.1.13"