mirror of
https://github.com/standardnotes/server
synced 2026-07-14 00:01:54 -04:00
feat(settings): replace setting with a domain entity
This commit is contained in:
@@ -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"],\
|
||||
|
||||
+14
-14
@@ -30,38 +30,38 @@ const requestBackups = async (
|
||||
domainEventFactory: DomainEventFactoryInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
): Promise<void> => {
|
||||
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}`)
|
||||
|
||||
@@ -28,7 +28,7 @@ const requestBackups = async (
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
): Promise<void> => {
|
||||
const permissionName = PermissionName.DailyEmailBackup
|
||||
const muteEmailsSettingName = SettingName.MuteFailedBackupsEmails
|
||||
const muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails
|
||||
const muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
||||
|
||||
if (!backupEmail) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -291,7 +291,7 @@ export class SessionService implements SessionServiceInterface {
|
||||
|
||||
private async isLoggingUserAgentEnabledOnSessions(user: User): Promise<boolean> {
|
||||
const loggingSetting = await this.settingService.findSettingWithDecryptedValue({
|
||||
settingName: SettingName.LogSessionUserAgent,
|
||||
settingName: SettingName.NAMES.LogSessionUserAgent,
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<Setting>
|
||||
|
||||
@@ -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<Setting>
|
||||
|
||||
@@ -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<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
||||
name: SettingName.MuteFailedBackupsEmails,
|
||||
name: SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
uuid: '6-7-8',
|
||||
value: 'muted',
|
||||
} as jest.Mocked<Setting>)
|
||||
@@ -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<Setting>
|
||||
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<Setting>
|
||||
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<Setting>
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue({
|
||||
name: SettingName.MuteFailedCloudBackupsEmails,
|
||||
name: SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
uuid: '6-7-8',
|
||||
value: 'muted',
|
||||
} as jest.Mocked<Setting>)
|
||||
@@ -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<Setting>
|
||||
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<Setting>
|
||||
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<Setting>)
|
||||
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<Setting>)
|
||||
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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<Setting | null>
|
||||
findOneByUuidAndNames(uuid: string, names: SettingName[]): Promise<Setting | null>
|
||||
findOneByUuidAndNames(uuid: string, names: string[]): Promise<Setting | null>
|
||||
findOneByNameAndUserUuid(name: string, userUuid: string): Promise<Setting | null>
|
||||
findLastByNameAndUserUuid(name: string, userUuid: string): Promise<Setting | null>
|
||||
findAllByUserUuid(userUuid: string): Promise<Setting[]>
|
||||
streamAllByNameAndValue(name: SettingName, value: string): Promise<ReadStream>
|
||||
streamAllByNameAndValue(name: string, value: string): Promise<ReadStream>
|
||||
deleteByUserUuid(dto: DeleteSettingDto): Promise<void>
|
||||
save(setting: Setting): Promise<Setting>
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
|
||||
@@ -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<SettingName, SettingDescription>).keys()]
|
||||
const flatSettings = [...(settings as Map<string, SettingDescription>).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<SettingName, SettingDescription>).keys()]
|
||||
const flatSettings = [...(settings as Map<string, SettingDescription>).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()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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, PermissionName>([
|
||||
[SettingName.EmailBackupFrequency, PermissionName.DailyEmailBackup],
|
||||
private readonly permissionsAssociatedWithSettings = new Map<string, PermissionName>([
|
||||
[SettingName.NAMES.EmailBackupFrequency, PermissionName.DailyEmailBackup],
|
||||
])
|
||||
|
||||
private readonly defaultSettings = new Map<SettingName, SettingDescription>([
|
||||
private readonly defaultSettings = new Map<string, SettingDescription>([
|
||||
[
|
||||
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<SettingName, SettingDescription>([
|
||||
private readonly vaultAccountDefaultSettingsOverwrites = new Map<string, SettingDescription>([
|
||||
[
|
||||
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<SettingName, SettingDescription> {
|
||||
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription> {
|
||||
return this.defaultSettings
|
||||
}
|
||||
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<SettingName, SettingDescription> {
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription> {
|
||||
const defaultVaultSettings = new Map(this.defaultSettings)
|
||||
|
||||
for (const vaultAccountDefaultSettingOverwriteKey of this.vaultAccountDefaultSettingsOverwrites.keys()) {
|
||||
|
||||
@@ -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<SettingName, SettingDescription>
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<SettingName, SettingDescription>
|
||||
getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined
|
||||
getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion
|
||||
getSensitivityForSetting(settingName: SettingName): boolean
|
||||
isSettingMutableByClient(settingName: SettingName | SubscriptionSettingName): boolean
|
||||
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription>
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription>
|
||||
getPermissionAssociatedWithSetting(settingName: string): PermissionName | undefined
|
||||
getEncryptionVersionForSetting(settingName: string): EncryptionVersion
|
||||
getSensitivityForSetting(settingName: string): boolean
|
||||
isSettingMutableByClient(settingName: string | SubscriptionSettingName): boolean
|
||||
}
|
||||
|
||||
@@ -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<Setting>
|
||||
|
||||
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<Setting>
|
||||
|
||||
settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(setting)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('GetSettings', () => {
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
mfaSetting = {
|
||||
name: SettingName.MfaSecret,
|
||||
name: SettingName.NAMES.MfaSecret,
|
||||
updatedAt: 122,
|
||||
sensitive: true,
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -25,7 +25,7 @@ export class UpdateSetting implements UseCaseInterface {
|
||||
) {}
|
||||
|
||||
async execute(dto: UpdateSettingDto): Promise<UpdateSettingResponse> {
|
||||
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<boolean> {
|
||||
const settingIsMutableByClient = await this.settingsAssociationService.isSettingMutableByClient(settingName)
|
||||
private async userHasPermissionToUpdateSetting(user: User, settingName: string): Promise<boolean> {
|
||||
const settingIsMutableByClient = this.settingsAssociationService.isSettingMutableByClient(settingName)
|
||||
if (!settingIsMutableByClient) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('VerifyMFA', () => {
|
||||
lockRepository.lockSuccessfullOTP = jest.fn()
|
||||
|
||||
setting = {
|
||||
name: SettingName.MfaSecret,
|
||||
name: SettingName.NAMES.MfaSecret,
|
||||
value: 'shhhh',
|
||||
} as jest.Mocked<Setting>
|
||||
|
||||
@@ -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<Setting>
|
||||
|
||||
@@ -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<Setting>
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -40,7 +40,10 @@ export class VerifyPredicate implements UseCaseInterface {
|
||||
}
|
||||
|
||||
private async hasUserEnabledEmailBackups(userUuid: Uuid): Promise<boolean> {
|
||||
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
|
||||
|
||||
@@ -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<Setting | null> {
|
||||
async findOneByUuidAndNames(uuid: string, names: string[]): Promise<Setting | null> {
|
||||
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<ReadStream> {
|
||||
async streamAllByNameAndValue(name: string, value: string): Promise<ReadStream> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('setting')
|
||||
.where('setting.name = :name AND setting.value = :value', {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { TimestampsProps } from './TimestampsProps'
|
||||
|
||||
export class Timestamps extends ValueObject<TimestampsProps> {
|
||||
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<Timestamps> {
|
||||
if (isNaN(createdAt) || createdAt === null || createdAt === undefined) {
|
||||
return Result.fail<Timestamps>(
|
||||
`Could not create Timestamps. Creation date should be a number, given: ${createdAt}`,
|
||||
)
|
||||
}
|
||||
if (isNaN(updatedAt) || updatedAt === null || updatedAt === undefined) {
|
||||
return Result.fail<Timestamps>(`Could not create Timestamps. Update date should be a number, given: ${createdAt}`)
|
||||
}
|
||||
|
||||
return Result.ok<Timestamps>(new Timestamps({ createdAt, updatedAt }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface TimestampsProps {
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
@@ -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()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { EncryptionVersionProps } from './EncryptionVersionProps'
|
||||
|
||||
export class EncryptionVersion extends ValueObject<EncryptionVersionProps> {
|
||||
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<EncryptionVersion> {
|
||||
if (
|
||||
isNaN(version) ||
|
||||
version === null ||
|
||||
version === undefined ||
|
||||
!Object.values(this.VERSIONS).includes(version)
|
||||
) {
|
||||
return Result.fail<EncryptionVersion>(
|
||||
`Could not create EncryptionVersion. Version should be a number, given: ${version}`,
|
||||
)
|
||||
}
|
||||
|
||||
return Result.ok<EncryptionVersion>(new EncryptionVersion({ value: version }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface EncryptionVersionProps {
|
||||
value: number
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import { SettingName } from './SettingName'
|
||||
|
||||
export type SensitiveSettingName = SettingName.MfaSecret | SettingName.ExtensionKey
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
import { SettingProps } from './SettingProps'
|
||||
|
||||
export class Setting extends Entity<SettingProps> {
|
||||
get id(): UniqueEntityId {
|
||||
return this._id
|
||||
}
|
||||
|
||||
private constructor(props: SettingProps, id?: UniqueEntityId) {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
static create(props: SettingProps, id?: UniqueEntityId): Result<Setting> {
|
||||
return Result.ok<Setting>(new Setting(props, id))
|
||||
}
|
||||
}
|
||||
@@ -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<SettingNameProps> {
|
||||
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<SettingName> {
|
||||
const isValidName = Object.values(this.NAMES).includes(name)
|
||||
if (!isValidName) {
|
||||
return Result.fail<SettingName>(`Invalid setting name: ${name}`)
|
||||
} else {
|
||||
return Result.ok<SettingName>(new SettingName({ value: name }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface SettingNameProps {
|
||||
value: string
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user