feat: add sending user requests to process (#1908)

* feat: add sending user requests to process

* fix(snjs): yarn lock

* fix(snjs): imports

* fix: specs
This commit is contained in:
Karol Sójko
2022-11-02 11:33:02 +01:00
committed by GitHub
parent f687334d7d
commit b2faa815e9
81 changed files with 766 additions and 325 deletions

View File

@@ -36,7 +36,7 @@
"typescript": "*" "typescript": "*"
}, },
"dependencies": { "dependencies": {
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/encryption": "workspace:*", "@standardnotes/encryption": "workspace:*",
"@standardnotes/models": "workspace:*", "@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*", "@standardnotes/responses": "workspace:*",

View File

@@ -0,0 +1,5 @@
export enum UserApiOperations {
Registering,
SubmittingRequest,
DeletingAccount,
}

View File

@@ -1,20 +1,35 @@
import { ProtocolVersion } from '@standardnotes/common' import { ProtocolVersion, UserRequestType } from '@standardnotes/common'
import { RootKeyParamsInterface } from '@standardnotes/models' import { RootKeyParamsInterface } from '@standardnotes/models'
import { UserDeletionResponse } from '../../Response/User/UserDeletionResponse'
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse' import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
import { UserRequestResponse } from '../../Response/UserRequest/UserRequestResponse'
import { UserServerInterface } from '../../Server' import { UserServerInterface } from '../../Server'
import { UserRequestServerInterface } from '../../Server/UserRequest/UserRequestServerInterface'
import { UserApiOperations } from './UserApiOperations'
import { UserApiService } from './UserApiService' import { UserApiService } from './UserApiService'
describe('UserApiService', () => { describe('UserApiService', () => {
let userServer: UserServerInterface let userServer: UserServerInterface
let userRequestServer: UserRequestServerInterface
let keyParams: RootKeyParamsInterface let keyParams: RootKeyParamsInterface
const createService = () => new UserApiService(userServer) const createService = () => new UserApiService(userServer, userRequestServer)
beforeEach(() => { beforeEach(() => {
userServer = {} as jest.Mocked<UserServerInterface> userServer = {} as jest.Mocked<UserServerInterface>
userServer.register = jest.fn().mockReturnValue({ userServer.register = jest.fn().mockReturnValue({
data: { user: { email: 'test@test.te', uuid: '1-2-3' } }, data: { user: { email: 'test@test.te', uuid: '1-2-3' } },
} as jest.Mocked<UserRegistrationResponse>) } as jest.Mocked<UserRegistrationResponse>)
userServer.deleteAccount = jest.fn().mockReturnValue({
data: { message: 'Success' },
} as jest.Mocked<UserDeletionResponse>)
userRequestServer = {} as jest.Mocked<UserRequestServerInterface>
userRequestServer.submitUserRequest = jest.fn().mockReturnValue({
data: { success: true },
} as jest.Mocked<UserRequestResponse>)
keyParams = {} as jest.Mocked<RootKeyParamsInterface> keyParams = {} as jest.Mocked<RootKeyParamsInterface>
keyParams.getPortableValue = jest.fn().mockReturnValue({ keyParams.getPortableValue = jest.fn().mockReturnValue({
@@ -51,8 +66,8 @@ describe('UserApiService', () => {
it('should not register a user if it is already registering', async () => { it('should not register a user if it is already registering', async () => {
const service = createService() const service = createService()
Object.defineProperty(service, 'registering', { Object.defineProperty(service, 'operationsInProgress', {
get: () => true, get: () => new Map([[UserApiOperations.Registering, true]]),
}) })
let error = null let error = null
@@ -84,4 +99,99 @@ describe('UserApiService', () => {
expect(error).not.toBeNull() expect(error).not.toBeNull()
}) })
it('should submit a user request', async () => {
const response = await createService().submitUserRequest({
userUuid: '1-2-3',
requestType: UserRequestType.ExitDiscount,
})
expect(response).toEqual({
data: {
success: true,
},
})
expect(userRequestServer.submitUserRequest).toHaveBeenCalledWith({
userUuid: '1-2-3',
requestType: UserRequestType.ExitDiscount,
})
})
it('should not submit a user request if it is already submitting', async () => {
const service = createService()
Object.defineProperty(service, 'operationsInProgress', {
get: () => new Map([[UserApiOperations.SubmittingRequest, true]]),
})
let error = null
try {
await service.submitUserRequest({ userUuid: '1-2-3', requestType: UserRequestType.ExitDiscount })
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should not submit a user request if the server fails', async () => {
userRequestServer.submitUserRequest = jest.fn().mockImplementation(() => {
throw new Error('Oops')
})
let error = null
try {
await createService().submitUserRequest({
userUuid: '1-2-3',
requestType: UserRequestType.ExitDiscount,
})
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should delete a user', async () => {
const response = await createService().deleteAccount('1-2-3')
expect(response).toEqual({
data: {
message: 'Success',
},
})
expect(userServer.deleteAccount).toHaveBeenCalledWith({
userUuid: '1-2-3',
})
})
it('should not delete a user if it is already deleting', async () => {
const service = createService()
Object.defineProperty(service, 'operationsInProgress', {
get: () => new Map([[UserApiOperations.DeletingAccount, true]]),
})
let error = null
try {
await service.deleteAccount('1-2-3')
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
it('should not delete a user if the server fails', async () => {
userServer.deleteAccount = jest.fn().mockImplementation(() => {
throw new Error('Oops')
})
let error = null
try {
await createService().deleteAccount('1-2-3')
} catch (caughtError) {
error = caughtError
}
expect(error).not.toBeNull()
})
}) })

View File

@@ -1,17 +1,57 @@
import { RootKeyParamsInterface } from '@standardnotes/models' import { RootKeyParamsInterface } from '@standardnotes/models'
import { UserRequestType } from '@standardnotes/common'
import { ErrorMessage } from '../../Error/ErrorMessage' import { ErrorMessage } from '../../Error/ErrorMessage'
import { ApiCallError } from '../../Error/ApiCallError' import { ApiCallError } from '../../Error/ApiCallError'
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse' import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
import { UserServerInterface } from '../../Server/User/UserServerInterface' import { UserServerInterface } from '../../Server/User/UserServerInterface'
import { ApiVersion } from '../../Api/ApiVersion' import { ApiVersion } from '../../Api/ApiVersion'
import { ApiEndpointParam } from '../../Request/ApiEndpointParam' import { ApiEndpointParam } from '../../Request/ApiEndpointParam'
import { UserRequestResponse } from '../../Response/UserRequest/UserRequestResponse'
import { UserDeletionResponse } from '../../Response/User/UserDeletionResponse'
import { UserRequestServerInterface } from '../../Server/UserRequest/UserRequestServerInterface'
import { UserApiOperations } from './UserApiOperations'
import { UserApiServiceInterface } from './UserApiServiceInterface' import { UserApiServiceInterface } from './UserApiServiceInterface'
export class UserApiService implements UserApiServiceInterface { export class UserApiService implements UserApiServiceInterface {
private registering: boolean private operationsInProgress: Map<UserApiOperations, boolean>
constructor(private userServer: UserServerInterface) { constructor(private userServer: UserServerInterface, private userRequestServer: UserRequestServerInterface) {
this.registering = false this.operationsInProgress = new Map()
}
async deleteAccount(userUuid: string): Promise<UserDeletionResponse> {
this.lockOperation(UserApiOperations.DeletingAccount)
try {
const response = await this.userServer.deleteAccount({
userUuid: userUuid,
})
this.unlockOperation(UserApiOperations.DeletingAccount)
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericRegistrationFail)
}
}
async submitUserRequest(dto: { userUuid: string; requestType: UserRequestType }): Promise<UserRequestResponse> {
this.lockOperation(UserApiOperations.SubmittingRequest)
try {
const response = await this.userRequestServer.submitUserRequest({
userUuid: dto.userUuid,
requestType: dto.requestType,
})
this.unlockOperation(UserApiOperations.SubmittingRequest)
return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericRegistrationFail)
}
} }
async register(registerDTO: { async register(registerDTO: {
@@ -20,10 +60,7 @@ export class UserApiService implements UserApiServiceInterface {
keyParams: RootKeyParamsInterface keyParams: RootKeyParamsInterface
ephemeral: boolean ephemeral: boolean
}): Promise<UserRegistrationResponse> { }): Promise<UserRegistrationResponse> {
if (this.registering) { this.lockOperation(UserApiOperations.Registering)
throw new ApiCallError(ErrorMessage.RegistrationInProgress)
}
this.registering = true
try { try {
const response = await this.userServer.register({ const response = await this.userServer.register({
@@ -34,11 +71,23 @@ export class UserApiService implements UserApiServiceInterface {
...registerDTO.keyParams.getPortableValue(), ...registerDTO.keyParams.getPortableValue(),
}) })
this.registering = false this.unlockOperation(UserApiOperations.Registering)
return response return response
} catch (error) { } catch (error) {
throw new ApiCallError(ErrorMessage.GenericRegistrationFail) throw new ApiCallError(ErrorMessage.GenericRegistrationFail)
} }
} }
private lockOperation(operation: UserApiOperations): void {
if (this.operationsInProgress.get(operation)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}
this.operationsInProgress.set(operation, true)
}
private unlockOperation(operation: UserApiOperations): void {
this.operationsInProgress.set(operation, false)
}
} }

View File

@@ -1,5 +1,9 @@
import { UserRequestType, Uuid } from '@standardnotes/common'
import { RootKeyParamsInterface } from '@standardnotes/models' import { RootKeyParamsInterface } from '@standardnotes/models'
import { UserDeletionResponse } from '../../Response/User/UserDeletionResponse'
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse' import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
import { UserRequestResponse } from '../../Response/UserRequest/UserRequestResponse'
export interface UserApiServiceInterface { export interface UserApiServiceInterface {
register(registerDTO: { register(registerDTO: {
@@ -8,4 +12,6 @@ export interface UserApiServiceInterface {
keyParams: RootKeyParamsInterface keyParams: RootKeyParamsInterface
ephemeral: boolean ephemeral: boolean
}): Promise<UserRegistrationResponse> }): Promise<UserRegistrationResponse>
submitUserRequest(dto: { userUuid: Uuid; requestType: UserRequestType }): Promise<UserRequestResponse>
deleteAccount(userUuid: string): Promise<UserDeletionResponse>
} }

View File

@@ -1,6 +1,7 @@
export * from './Subscription/SubscriptionApiOperations' export * from './Subscription/SubscriptionApiOperations'
export * from './Subscription/SubscriptionApiService' export * from './Subscription/SubscriptionApiService'
export * from './Subscription/SubscriptionApiServiceInterface' export * from './Subscription/SubscriptionApiServiceInterface'
export * from './User/UserApiOperations'
export * from './User/UserApiService' export * from './User/UserApiService'
export * from './User/UserApiServiceInterface' export * from './User/UserApiServiceInterface'
export * from './WebSocket/WebSocketApiService' export * from './WebSocket/WebSocketApiService'

View File

@@ -0,0 +1,6 @@
import { Uuid } from '@standardnotes/common'
export type UserDeletionRequestParams = {
userUuid: Uuid
[additionalParam: string]: unknown
}

View File

@@ -0,0 +1,7 @@
import { UserRequestType, Uuid } from '@standardnotes/common'
export type UserRequestRequestParams = {
userUuid: Uuid
requestType: UserRequestType
[additionalParam: string]: unknown
}

View File

@@ -5,6 +5,7 @@ export * from './Subscription/SubscriptionInviteDeclineRequestParams'
export * from './Subscription/SubscriptionInviteListRequestParams' export * from './Subscription/SubscriptionInviteListRequestParams'
export * from './Subscription/SubscriptionInviteRequestParams' export * from './Subscription/SubscriptionInviteRequestParams'
export * from './User/UserRegistrationRequestParams' export * from './User/UserRegistrationRequestParams'
export * from './UserRequest/UserRequestRequestParams'
export * from './WebSocket/WebSocketConnectionTokenRequestParams' export * from './WebSocket/WebSocketConnectionTokenRequestParams'
export * from './Workspace/WorkspaceCreationRequestParams' export * from './Workspace/WorkspaceCreationRequestParams'
export * from './Workspace/WorkspaceInvitationAcceptingRequestParams' export * from './Workspace/WorkspaceInvitationAcceptingRequestParams'

View File

@@ -0,0 +1,9 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { UserDeletionResponseBody } from './UserDeletionResponseBody'
export interface UserDeletionResponse extends HttpResponse {
data: Either<UserDeletionResponseBody, HttpErrorResponseBody>
}

View File

@@ -0,0 +1,3 @@
export type UserDeletionResponseBody = {
message: string
}

View File

@@ -0,0 +1,9 @@
import { Either } from '@standardnotes/common'
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'
import { UserRequestResponseBody } from './UserRequestResponseBody'
export interface UserRequestResponse extends HttpResponse {
data: Either<UserRequestResponseBody, HttpErrorResponseBody>
}

View File

@@ -0,0 +1,3 @@
export type UserRequestResponseBody = {
success: boolean
}

View File

@@ -8,8 +8,12 @@ export * from './Subscription/SubscriptionInviteListResponse'
export * from './Subscription/SubscriptionInviteListResponseBody' export * from './Subscription/SubscriptionInviteListResponseBody'
export * from './Subscription/SubscriptionInviteResponse' export * from './Subscription/SubscriptionInviteResponse'
export * from './Subscription/SubscriptionInviteResponseBody' export * from './Subscription/SubscriptionInviteResponseBody'
export * from './User/UserDeletionResponse'
export * from './User/UserDeletionResponseBody'
export * from './User/UserRegistrationResponse' export * from './User/UserRegistrationResponse'
export * from './User/UserRegistrationResponseBody' export * from './User/UserRegistrationResponseBody'
export * from './UserRequest/UserRequestResponse'
export * from './UserRequest/UserRequestResponseBody'
export * from './WebSocket/WebSocketConnectionTokenResponse' export * from './WebSocket/WebSocketConnectionTokenResponse'
export * from './WebSocket/WebSocketConnectionTokenResponseBody' export * from './WebSocket/WebSocketConnectionTokenResponseBody'
export * from './Workspace/WorkspaceCreationResponse' export * from './Workspace/WorkspaceCreationResponse'

View File

@@ -1,5 +1,8 @@
import { Uuid } from '@standardnotes/common'
const UserPaths = { const UserPaths = {
register: '/v1/users', register: '/v1/users',
deleteAccount: (userUuid: Uuid) => `/v1/users/${userUuid}`,
} }
export const Paths = { export const Paths = {

View File

@@ -1,7 +1,7 @@
import { ProtocolVersion } from '@standardnotes/common' import { ProtocolVersion } from '@standardnotes/common'
import { ApiVersion } from '../../Api' import { ApiVersion } from '../../Api'
import { HttpServiceInterface } from '../../Http' import { HttpServiceInterface } from '../../Http'
import { UserRegistrationResponse } from '../../Response' import { UserDeletionResponse, UserRegistrationResponse } from '../../Response'
import { UserServer } from './UserServer' import { UserServer } from './UserServer'
describe('UserServer', () => { describe('UserServer', () => {
@@ -14,6 +14,9 @@ describe('UserServer', () => {
httpService.post = jest.fn().mockReturnValue({ httpService.post = jest.fn().mockReturnValue({
data: { user: { email: 'test@test.te', uuid: '1-2-3' } }, data: { user: { email: 'test@test.te', uuid: '1-2-3' } },
} as jest.Mocked<UserRegistrationResponse>) } as jest.Mocked<UserRegistrationResponse>)
httpService.delete = jest.fn().mockReturnValue({
data: { message: 'Success' },
} as jest.Mocked<UserDeletionResponse>)
}) })
it('should register a user', async () => { it('should register a user', async () => {
@@ -36,4 +39,16 @@ describe('UserServer', () => {
}, },
}) })
}) })
it('should delete a user', async () => {
const response = await createServer().deleteAccount({
userUuid: '1-2-3',
})
expect(response).toEqual({
data: {
message: 'Success',
},
})
})
}) })

View File

@@ -1,5 +1,7 @@
import { HttpServiceInterface } from '../../Http/HttpServiceInterface' import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
import { UserDeletionRequestParams } from '../../Request/User/UserDeletionRequestParams'
import { UserRegistrationRequestParams } from '../../Request/User/UserRegistrationRequestParams' import { UserRegistrationRequestParams } from '../../Request/User/UserRegistrationRequestParams'
import { UserDeletionResponse } from '../../Response/User/UserDeletionResponse'
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse' import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
import { Paths } from './Paths' import { Paths } from './Paths'
import { UserServerInterface } from './UserServerInterface' import { UserServerInterface } from './UserServerInterface'
@@ -7,6 +9,12 @@ import { UserServerInterface } from './UserServerInterface'
export class UserServer implements UserServerInterface { export class UserServer implements UserServerInterface {
constructor(private httpService: HttpServiceInterface) {} constructor(private httpService: HttpServiceInterface) {}
async deleteAccount(params: UserDeletionRequestParams): Promise<UserDeletionResponse> {
const response = await this.httpService.delete(Paths.v1.deleteAccount(params.userUuid), params)
return response as UserDeletionResponse
}
async register(params: UserRegistrationRequestParams): Promise<UserRegistrationResponse> { async register(params: UserRegistrationRequestParams): Promise<UserRegistrationResponse> {
const response = await this.httpService.post(Paths.v1.register, params) const response = await this.httpService.post(Paths.v1.register, params)

View File

@@ -1,6 +1,9 @@
import { UserDeletionRequestParams } from '../../Request/User/UserDeletionRequestParams'
import { UserRegistrationRequestParams } from '../../Request/User/UserRegistrationRequestParams' import { UserRegistrationRequestParams } from '../../Request/User/UserRegistrationRequestParams'
import { UserDeletionResponse } from '../../Response/User/UserDeletionResponse'
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse' import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
export interface UserServerInterface { export interface UserServerInterface {
register(params: UserRegistrationRequestParams): Promise<UserRegistrationResponse> register(params: UserRegistrationRequestParams): Promise<UserRegistrationResponse>
deleteAccount(params: UserDeletionRequestParams): Promise<UserDeletionResponse>
} }

View File

@@ -0,0 +1,11 @@
import { Uuid } from '@standardnotes/common'
const UserRequestPaths = {
submitUserRequest: (userUuid: Uuid) => `/v1/users/${userUuid}/requests`,
}
export const Paths = {
v1: {
...UserRequestPaths,
},
}

View File

@@ -0,0 +1,32 @@
import { UserRequestType } from '@standardnotes/common'
import { HttpServiceInterface } from '../../Http'
import { UserRequestResponse } from '../../Response/UserRequest/UserRequestResponse'
import { UserRequestServer } from './UserRequestServer'
describe('UserRequestServer', () => {
let httpService: HttpServiceInterface
const createServer = () => new UserRequestServer(httpService)
beforeEach(() => {
httpService = {} as jest.Mocked<HttpServiceInterface>
httpService.post = jest.fn().mockReturnValue({
data: { success: true },
} as jest.Mocked<UserRequestResponse>)
})
it('should submit a user request', async () => {
const response = await createServer().submitUserRequest({
userUuid: '1-2-3',
requestType: UserRequestType.ExitDiscount,
})
expect(response).toEqual({
data: {
success: true,
},
})
})
})

View File

@@ -0,0 +1,16 @@
import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
import { UserRequestRequestParams } from '../../Request/UserRequest/UserRequestRequestParams'
import { UserRequestResponse } from '../../Response/UserRequest/UserRequestResponse'
import { Paths } from './Paths'
import { UserRequestServerInterface } from './UserRequestServerInterface'
export class UserRequestServer implements UserRequestServerInterface {
constructor(private httpService: HttpServiceInterface) {}
async submitUserRequest(params: UserRequestRequestParams): Promise<UserRequestResponse> {
const response = await this.httpService.post(Paths.v1.submitUserRequest(params.userUuid), params)
return response as UserRequestResponse
}
}

View File

@@ -0,0 +1,6 @@
import { UserRequestRequestParams } from '../../Request/UserRequest/UserRequestRequestParams'
import { UserRequestResponse } from '../../Response/UserRequest/UserRequestResponse'
export interface UserRequestServerInterface {
submitUserRequest(params: UserRequestRequestParams): Promise<UserRequestResponse>
}

View File

@@ -2,6 +2,8 @@ export * from './Subscription/SubscriptionServer'
export * from './Subscription/SubscriptionServerInterface' export * from './Subscription/SubscriptionServerInterface'
export * from './User/UserServer' export * from './User/UserServer'
export * from './User/UserServerInterface' export * from './User/UserServerInterface'
export * from './UserRequest/UserRequestServer'
export * from './UserRequest/UserRequestServerInterface'
export * from './WebSocket/WebSocketServer' export * from './WebSocket/WebSocketServer'
export * from './WebSocket/WebSocketServerInterface' export * from './WebSocket/WebSocketServerInterface'
export * from './Workspace/WorkspaceServer' export * from './Workspace/WorkspaceServer'

View File

@@ -35,7 +35,7 @@
"typescript": "*" "typescript": "*"
}, },
"dependencies": { "dependencies": {
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/models": "workspace:*", "@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*", "@standardnotes/responses": "workspace:*",
"@standardnotes/sncrypto-common": "workspace:*", "@standardnotes/sncrypto-common": "workspace:*",

View File

@@ -1,54 +1,67 @@
import { ProtocolVersion } from '@standardnotes/common' import { KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
import { import {
BackupFile, BackupFile,
DecryptedPayloadInterface, DecryptedPayloadInterface,
EncryptedPayloadInterface, EncryptedPayloadInterface,
ItemContent, ItemContent,
ItemsKeyInterface,
RootKeyInterface, RootKeyInterface,
} from '@standardnotes/models' } from '@standardnotes/models'
import { ClientDisplayableError } from '@standardnotes/responses' import { ClientDisplayableError } from '@standardnotes/responses'
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams' import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
import { KeyedDecryptionSplit } from '../../Split/KeyedDecryptionSplit' import { KeyedDecryptionSplit } from '../../Split/KeyedDecryptionSplit'
import { KeyedEncryptionSplit } from '../../Split/KeyedEncryptionSplit' import { KeyedEncryptionSplit } from '../../Split/KeyedEncryptionSplit'
export interface EncryptionProviderInterface { export interface EncryptionProviderInterface {
encryptSplitSingle(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface> encryptSplitSingle(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface>
encryptSplit(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface[]> encryptSplit(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface[]>
decryptSplitSingle< decryptSplitSingle<
C extends ItemContent = ItemContent, C extends ItemContent = ItemContent,
P extends DecryptedPayloadInterface<C> = DecryptedPayloadInterface<C>, P extends DecryptedPayloadInterface<C> = DecryptedPayloadInterface<C>,
>( >(
split: KeyedDecryptionSplit, split: KeyedDecryptionSplit,
): Promise<P | EncryptedPayloadInterface> ): Promise<P | EncryptedPayloadInterface>
decryptSplit< decryptSplit<
C extends ItemContent = ItemContent, C extends ItemContent = ItemContent,
P extends DecryptedPayloadInterface<C> = DecryptedPayloadInterface<C>, P extends DecryptedPayloadInterface<C> = DecryptedPayloadInterface<C>,
>( >(
split: KeyedDecryptionSplit, split: KeyedDecryptionSplit,
): Promise<(P | EncryptedPayloadInterface)[]> ): Promise<(P | EncryptedPayloadInterface)[]>
hasRootKeyEncryptionSource(): boolean hasRootKeyEncryptionSource(): boolean
getKeyEmbeddedKeyParams(key: EncryptedPayloadInterface): SNRootKeyParams | undefined getKeyEmbeddedKeyParams(key: EncryptedPayloadInterface): SNRootKeyParams | undefined
computeRootKey(password: string, keyParams: SNRootKeyParams): Promise<RootKeyInterface> computeRootKey(password: string, keyParams: SNRootKeyParams): Promise<RootKeyInterface>
/**
* @returns The versions that this library supports.
*/
supportedVersions(): ProtocolVersion[] supportedVersions(): ProtocolVersion[]
getUserVersion(): ProtocolVersion | undefined getUserVersion(): ProtocolVersion | undefined
/**
* Decrypts a backup file using user-inputted password
* @param password - The raw user password associated with this backup file
*/
decryptBackupFile( decryptBackupFile(
file: BackupFile, file: BackupFile,
password?: string, password?: string,
): Promise<ClientDisplayableError | (EncryptedPayloadInterface | DecryptedPayloadInterface)[]> ): Promise<ClientDisplayableError | (EncryptedPayloadInterface | DecryptedPayloadInterface)[]>
hasAccount(): boolean
decryptErroredPayloads(): Promise<void>
deleteWorkspaceSpecificKeyStateFromDevice(): Promise<void>
hasPasscode(): boolean
createRootKey(
identifier: string,
password: string,
origination: KeyParamsOrigination,
version?: ProtocolVersion,
): Promise<RootKeyInterface>
setNewRootKeyWrapper(wrappingKey: RootKeyInterface): Promise<void>
removePasscode(): Promise<void>
validateAccountPassword(password: string): Promise<
| {
valid: true
artifacts: {
rootKey: RootKeyInterface
}
}
| {
valid: boolean
}
>
createNewItemsKeyWithRollback(): Promise<() => Promise<void>>
reencryptItemsKeys(): Promise<void>
getSureDefaultItemsKey(): ItemsKeyInterface
getRootKeyParams(): Promise<SNRootKeyParams | undefined>
} }

View File

@@ -26,7 +26,7 @@
}, },
"dependencies": { "dependencies": {
"@standardnotes/auth": "^3.19.4", "@standardnotes/auth": "^3.19.4",
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/security": "^1.2.0", "@standardnotes/security": "^1.2.0",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13"
}, },

View File

@@ -32,7 +32,7 @@
"typescript": "*" "typescript": "*"
}, },
"dependencies": { "dependencies": {
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/files": "workspace:*", "@standardnotes/files": "workspace:*",
"@standardnotes/utils": "workspace:*", "@standardnotes/utils": "workspace:*",
"@types/wicg-file-system-access": "^2020.9.5", "@types/wicg-file-system-access": "^2020.9.5",

View File

@@ -30,7 +30,7 @@
"ts-jest": "^28.0.5" "ts-jest": "^28.0.5"
}, },
"dependencies": { "dependencies": {
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/encryption": "workspace:*", "@standardnotes/encryption": "workspace:*",
"@standardnotes/models": "workspace:*", "@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*", "@standardnotes/responses": "workspace:*",

View File

@@ -32,7 +32,7 @@
"typescript": "*" "typescript": "*"
}, },
"dependencies": { "dependencies": {
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/features": "workspace:*", "@standardnotes/features": "workspace:*",
"@standardnotes/responses": "workspace:*", "@standardnotes/responses": "workspace:*",
"@standardnotes/utils": "workspace:*", "@standardnotes/utils": "workspace:*",

View File

@@ -31,7 +31,7 @@
"ts-jest": "^28.0.5" "ts-jest": "^28.0.5"
}, },
"dependencies": { "dependencies": {
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/features": "workspace:*", "@standardnotes/features": "workspace:*",
"@standardnotes/security": "^1.1.0", "@standardnotes/security": "^1.1.0",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13"

View File

@@ -10,10 +10,10 @@ module.exports = {
}, },
coverageThreshold: { coverageThreshold: {
global: { global: {
branches: 9, branches: 6,
functions: 10, functions: 9,
lines: 16, lines: 13,
statements: 16 statements: 13
} }
} }
}; };

View File

@@ -26,12 +26,13 @@
"dependencies": { "dependencies": {
"@standardnotes/api": "workspace:^", "@standardnotes/api": "workspace:^",
"@standardnotes/auth": "^3.19.4", "@standardnotes/auth": "^3.19.4",
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/encryption": "workspace:^", "@standardnotes/encryption": "workspace:^",
"@standardnotes/files": "workspace:^", "@standardnotes/files": "workspace:^",
"@standardnotes/models": "workspace:^", "@standardnotes/models": "workspace:^",
"@standardnotes/responses": "workspace:*", "@standardnotes/responses": "workspace:*",
"@standardnotes/security": "^1.2.0", "@standardnotes/security": "^1.2.0",
"@standardnotes/sncrypto-common": "workspace:^",
"@standardnotes/utils": "workspace:*", "@standardnotes/utils": "workspace:*",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13"
}, },

View File

@@ -16,7 +16,7 @@ import { StorageValueModes } from '../Storage/StorageTypes'
import { DeinitMode } from './DeinitMode' import { DeinitMode } from './DeinitMode'
import { DeinitSource } from './DeinitSource' import { DeinitSource } from './DeinitSource'
import { UserClientInterface } from './UserClientInterface' import { UserClientInterface } from '../User/UserClientInterface'
export interface ApplicationInterface { export interface ApplicationInterface {
deinit(mode: DeinitMode, source: DeinitSource): void deinit(mode: DeinitMode, source: DeinitSource): void

View File

@@ -1,6 +1,9 @@
import { ChallengeModalTitle, ChallengeStrings } from '../Api/Messages'
import { assertUnreachable } from '@standardnotes/utils' import { assertUnreachable } from '@standardnotes/utils'
import { ChallengeValidation, ChallengeReason, ChallengeInterface, ChallengePrompt } from '@standardnotes/services' import { ChallengeModalTitle, ChallengeStrings } from '../Strings/Messages'
import { ChallengeInterface } from './ChallengeInterface'
import { ChallengePrompt } from './Prompt/ChallengePrompt'
import { ChallengeReason } from './Types/ChallengeReason'
import { ChallengeValidation } from './Types/ChallengeValidation'
/** /**
* A challenge is a stateless description of what the client needs to provide * A challenge is a stateless description of what the client needs to provide

View File

@@ -1,3 +1,5 @@
import { RootKeyInterface } from '@standardnotes/models'
import { AbstractService } from '../Service/AbstractService' import { AbstractService } from '../Service/AbstractService'
import { ChallengeInterface } from './ChallengeInterface' import { ChallengeInterface } from './ChallengeInterface'
import { ChallengePromptInterface } from './Prompt/ChallengePromptInterface' import { ChallengePromptInterface } from './Prompt/ChallengePromptInterface'
@@ -10,7 +12,6 @@ export interface ChallengeServiceInterface extends AbstractService {
* For non-validated challenges, will resolve when the first value is submitted. * For non-validated challenges, will resolve when the first value is submitted.
*/ */
promptForChallengeResponse(challenge: ChallengeInterface): Promise<ChallengeResponseInterface | undefined> promptForChallengeResponse(challenge: ChallengeInterface): Promise<ChallengeResponseInterface | undefined>
createChallenge( createChallenge(
prompts: ChallengePromptInterface[], prompts: ChallengePromptInterface[],
reason: ChallengeReason, reason: ChallengeReason,
@@ -18,6 +19,19 @@ export interface ChallengeServiceInterface extends AbstractService {
heading?: string, heading?: string,
subheading?: string, subheading?: string,
): ChallengeInterface ): ChallengeInterface
completeChallenge(challenge: ChallengeInterface): void completeChallenge(challenge: ChallengeInterface): void
getWrappingKeyIfApplicable(passcode?: string): Promise<
| {
canceled?: undefined
wrappingKey?: undefined
}
| {
canceled: boolean
wrappingKey?: undefined
}
| {
wrappingKey: RootKeyInterface
canceled?: undefined
}
>
} }

View File

@@ -1,3 +1,4 @@
export * from './Challenge'
export * from './ChallengeInterface' export * from './ChallengeInterface'
export * from './ChallengeResponseInterface' export * from './ChallengeResponseInterface'
export * from './ChallengeServiceInterface' export * from './ChallengeServiceInterface'

View File

@@ -44,50 +44,22 @@ export interface ItemManagerInterface extends AbstractService {
contentType: ContentType | ContentType[], contentType: ContentType | ContentType[],
callback: ItemManagerChangeObserverCallback<I>, callback: ItemManagerChangeObserverCallback<I>,
): () => void ): () => void
/**
* Marks the item as deleted and needing sync.
*/
setItemToBeDeleted(itemToLookupUuidFor: DecryptedItemInterface, source?: PayloadEmitSource): Promise<void> setItemToBeDeleted(itemToLookupUuidFor: DecryptedItemInterface, source?: PayloadEmitSource): Promise<void>
setItemsToBeDeleted(itemsToLookupUuidsFor: DecryptedItemInterface[]): Promise<void> setItemsToBeDeleted(itemsToLookupUuidsFor: DecryptedItemInterface[]): Promise<void>
setItemsDirty( setItemsDirty(
itemsToLookupUuidsFor: DecryptedItemInterface[], itemsToLookupUuidsFor: DecryptedItemInterface[],
isUserModified?: boolean, isUserModified?: boolean,
): Promise<DecryptedItemInterface[]> ): Promise<DecryptedItemInterface[]>
get items(): DecryptedItemInterface[] get items(): DecryptedItemInterface[]
/**
* Inserts the item as-is by reading its payload value. This function will not
* modify item in any way (such as marking it as dirty). It is up to the caller
* to pass in a dirtied item if that is their intention.
*/
insertItem(item: DecryptedItemInterface): Promise<DecryptedItemInterface> insertItem(item: DecryptedItemInterface): Promise<DecryptedItemInterface>
emitItemFromPayload(payload: DecryptedPayloadInterface, source: PayloadEmitSource): Promise<DecryptedItemInterface> emitItemFromPayload(payload: DecryptedPayloadInterface, source: PayloadEmitSource): Promise<DecryptedItemInterface>
getItems<T extends DecryptedItemInterface>(contentType: ContentType | ContentType[]): T[] getItems<T extends DecryptedItemInterface>(contentType: ContentType | ContentType[]): T[]
/**
* Returns all non-deleted items keys
*/
getDisplayableItemsKeys(): ItemsKeyInterface[] getDisplayableItemsKeys(): ItemsKeyInterface[]
/**
* Creates an item and conditionally maps it and marks it as dirty.
* @param needsSync - Whether to mark the item as needing sync
*/
createItem<T extends DecryptedItemInterface, C extends ItemContent = ItemContent>( createItem<T extends DecryptedItemInterface, C extends ItemContent = ItemContent>(
contentType: ContentType, contentType: ContentType,
content: C, content: C,
needsSync?: boolean, needsSync?: boolean,
): Promise<T> ): Promise<T>
/**
* Create an unmanaged item that can later be inserted via `insertItem`
*/
createTemplateItem< createTemplateItem<
C extends ItemContent = ItemContent, C extends ItemContent = ItemContent,
I extends DecryptedItemInterface<C> = DecryptedItemInterface<C>, I extends DecryptedItemInterface<C> = DecryptedItemInterface<C>,
@@ -96,12 +68,6 @@ export interface ItemManagerInterface extends AbstractService {
content?: C, content?: C,
override?: Partial<DecryptedPayload<C>>, override?: Partial<DecryptedPayload<C>>,
): I ): I
/**
* Consumers wanting to modify an item should run it through this block,
* so that data is properly mapped through our function, and latest state
* is properly reconciled.
*/
changeItem< changeItem<
M extends DecryptedItemMutator = DecryptedItemMutator, M extends DecryptedItemMutator = DecryptedItemMutator,
I extends DecryptedItemInterface = DecryptedItemInterface, I extends DecryptedItemInterface = DecryptedItemInterface,
@@ -112,7 +78,6 @@ export interface ItemManagerInterface extends AbstractService {
emitSource?: PayloadEmitSource, emitSource?: PayloadEmitSource,
payloadSourceKey?: string, payloadSourceKey?: string,
): Promise<I> ): Promise<I>
changeItemsKey( changeItemsKey(
itemToLookupUuidFor: ItemsKeyInterface, itemToLookupUuidFor: ItemsKeyInterface,
mutate: (mutator: ItemsKeyMutatorInterface) => void, mutate: (mutator: ItemsKeyMutatorInterface) => void,
@@ -120,16 +85,15 @@ export interface ItemManagerInterface extends AbstractService {
emitSource?: PayloadEmitSource, emitSource?: PayloadEmitSource,
payloadSourceKey?: string, payloadSourceKey?: string,
): Promise<ItemsKeyInterface> ): Promise<ItemsKeyInterface>
itemsMatchingPredicate<T extends DecryptedItemInterface>( itemsMatchingPredicate<T extends DecryptedItemInterface>(
contentType: ContentType, contentType: ContentType,
predicate: PredicateInterface<T>, predicate: PredicateInterface<T>,
): T[] ): T[]
itemsMatchingPredicates<T extends DecryptedItemInterface>( itemsMatchingPredicates<T extends DecryptedItemInterface>(
contentType: ContentType, contentType: ContentType,
predicates: PredicateInterface<T>[], predicates: PredicateInterface<T>[],
): T[] ): T[]
subItemsMatchingPredicates<T extends DecryptedItemInterface>(items: T[], predicates: PredicateInterface<T>[]): T[] subItemsMatchingPredicates<T extends DecryptedItemInterface>(items: T[], predicates: PredicateInterface<T>[]): T[]
removeAllItemsFromMemory(): Promise<void>
getDirtyItems(): (DecryptedItemInterface | DeletedItemInterface)[]
} }

View File

@@ -0,0 +1,4 @@
export enum MobileUnlockTiming {
Immediately = 'immediately',
OnQuit = 'on-quit',
}

View File

@@ -1,6 +1,7 @@
import { ChallengeReason } from '@standardnotes/services'
import { DecryptedItem } from '@standardnotes/models' import { DecryptedItem } from '@standardnotes/models'
import { TimingDisplayOption, MobileUnlockTiming } from './MobileUnlockTiming' import { ChallengeReason } from '../Challenge'
import { MobileUnlockTiming } from './MobileUnlockTiming'
import { TimingDisplayOption } from './TimingDisplayOption'
export interface ProtectionsClientInterface { export interface ProtectionsClientInterface {
authorizeProtectedActionForItems<T extends DecryptedItem>(files: T[], challengeReason: ChallengeReason): Promise<T[]> authorizeProtectedActionForItems<T extends DecryptedItem>(files: T[], challengeReason: ChallengeReason): Promise<T[]>
@@ -16,4 +17,11 @@ export interface ProtectionsClientInterface {
hasBiometricsEnabled(): boolean hasBiometricsEnabled(): boolean
enableBiometrics(): boolean enableBiometrics(): boolean
disableBiometrics(): Promise<boolean> disableBiometrics(): Promise<boolean>
authorizeAction(
reason: ChallengeReason,
dto: { fallBackToAccountPassword: boolean; requireAccountPassword: boolean; forcePrompt: boolean },
): Promise<boolean>
authorizeAddingPasscode(): Promise<boolean>
authorizeRemovingPasscode(): Promise<boolean>
authorizeChangingPasscode(): Promise<boolean>
} }

View File

@@ -1,7 +1,4 @@
export enum MobileUnlockTiming { import { MobileUnlockTiming } from './MobileUnlockTiming'
Immediately = 'immediately',
OnQuit = 'on-quit',
}
export type TimingDisplayOption = { export type TimingDisplayOption = {
title: string title: string

View File

@@ -0,0 +1,9 @@
import { AnyKeyParamsContent } from '@standardnotes/common'
import { RootKeyInterface } from '@standardnotes/models'
import { HttpResponse } from '@standardnotes/responses'
export type SessionManagerResponse = {
response: HttpResponse
rootKey?: RootKeyInterface
keyParams?: AnyKeyParamsContent
}

View File

@@ -0,0 +1,34 @@
import { UserRegistrationResponseBody } from '@standardnotes/api'
import { ProtocolVersion } from '@standardnotes/common'
import { RootKeyInterface } from '@standardnotes/models'
import { ClientDisplayableError, HttpResponse, SignInResponse, User } from '@standardnotes/responses'
import { Base64String } from '@standardnotes/sncrypto-common'
import { SessionManagerResponse } from './SessionManagerResponse'
export interface SessionsClientInterface {
createDemoShareToken(): Promise<Base64String | ClientDisplayableError>
populateSessionFromDemoShareToken(token: Base64String): Promise<void>
getUser(): User | undefined
register(email: string, password: string, ephemeral: boolean): Promise<UserRegistrationResponseBody>
signIn(
email: string,
password: string,
strict: boolean,
ephemeral: boolean,
minAllowedVersion?: ProtocolVersion,
): Promise<SessionManagerResponse>
getSureUser(): User
bypassChecksAndSignInWithRootKey(
email: string,
rootKey: RootKeyInterface,
ephemeral: boolean,
): Promise<SignInResponse | HttpResponse>
signOut(): Promise<void>
changeCredentials(parameters: {
currentServerPassword: string
newRootKey: RootKeyInterface
wrappingKey?: RootKeyInterface
newEmail?: string
}): Promise<SessionManagerResponse>
}

View File

@@ -1,16 +1,15 @@
import { PayloadInterface, RootKeyInterface } from '@standardnotes/models' import { FullyFormedPayloadInterface, PayloadInterface, RootKeyInterface } from '@standardnotes/models'
import { StorageValueModes } from './StorageTypes' import { StoragePersistencePolicies, StorageValueModes } from './StorageTypes'
export interface StorageServiceInterface { export interface StorageServiceInterface {
getValue<T>(key: string, mode?: StorageValueModes, defaultValue?: T): T getValue<T>(key: string, mode?: StorageValueModes, defaultValue?: T): T
canDecryptWithKey(key: RootKeyInterface): Promise<boolean> canDecryptWithKey(key: RootKeyInterface): Promise<boolean>
savePayload(payload: PayloadInterface): Promise<void> savePayload(payload: PayloadInterface): Promise<void>
savePayloads(decryptedPayloads: PayloadInterface[]): Promise<void> savePayloads(decryptedPayloads: PayloadInterface[]): Promise<void>
setValue(key: string, value: unknown, mode?: StorageValueModes): void setValue(key: string, value: unknown, mode?: StorageValueModes): void
removeValue(key: string, mode?: StorageValueModes): Promise<void> removeValue(key: string, mode?: StorageValueModes): Promise<void>
setPersistencePolicy(persistencePolicy: StoragePersistencePolicies): Promise<void>
clearAllData(): Promise<void>
forceDeletePayloads(payloads: FullyFormedPayloadInterface[]): Promise<void>
clearAllPayloads(): Promise<void>
} }

View File

@@ -1,7 +1,14 @@
/* istanbul ignore file */ /* istanbul ignore file */
import { FullyFormedPayloadInterface } from '@standardnotes/models'
import { SyncOptions } from './SyncOptions' import { SyncOptions } from './SyncOptions'
export interface SyncServiceInterface { export interface SyncServiceInterface {
sync(options?: Partial<SyncOptions>): Promise<unknown> sync(options?: Partial<SyncOptions>): Promise<unknown>
resetSyncState(): void
markAllItemsAsNeedingSyncAndPersist(): Promise<void>
downloadFirstSync(waitTimeOnFailureMs: number, otherSyncOptions?: Partial<SyncOptions>): Promise<void>
persistPayloads(payloads: FullyFormedPayloadInterface[]): Promise<void>
lockSyncing(): void
unlockSyncing(): void
} }

View File

@@ -1,9 +1,9 @@
import { DeinitSource } from './DeinitSource' import { DeinitSource } from '../Application/DeinitSource'
export interface UserClientInterface { export interface UserClientInterface {
deleteAccount(): Promise<{ deleteAccount(): Promise<{
error: boolean error: boolean
message?: string message?: string
}> }>
signOut(force?: boolean, source?: DeinitSource): Promise<void> signOut(force?: boolean, source?: DeinitSource): Promise<void>
} }

View File

@@ -1,32 +1,29 @@
import { Challenge } from '../Challenge' import { EncryptionProviderInterface, SNRootKey, SNRootKeyParams } from '@standardnotes/encryption'
import { ChallengeService } from '../Challenge/ChallengeService'
import { SNRootKey, SNRootKeyParams } from '@standardnotes/encryption'
import { HttpResponse, SignInResponse, User } from '@standardnotes/responses' import { HttpResponse, SignInResponse, User } from '@standardnotes/responses'
import { ItemManager } from '@Lib/Services/Items/ItemManager'
import { KeyParamsOrigination } from '@standardnotes/common' import { KeyParamsOrigination } from '@standardnotes/common'
import { UuidGenerator } from '@standardnotes/utils'
import { UserApiServiceInterface, UserRegistrationResponseBody } from '@standardnotes/api'
import * as Messages from '../Strings/Messages'
import { InfoStrings } from '../Strings/InfoStrings'
import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
import { StorageServiceInterface } from '../Storage/StorageServiceInterface'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { AlertService } from '../Alert/AlertService'
import { import {
AbstractService, Challenge,
AlertService,
ChallengePrompt, ChallengePrompt,
ChallengeReason, ChallengeReason,
ChallengeServiceInterface,
ChallengeValidation, ChallengeValidation,
DeinitSource, } from '../Challenge'
InternalEventBusInterface, import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
UserClientInterface, import { AbstractService } from '../Service/AbstractService'
StoragePersistencePolicies, import { UserClientInterface } from './UserClientInterface'
EncryptionService, import { DeinitSource } from '../Application/DeinitSource'
} from '@standardnotes/services' import { StoragePersistencePolicies } from '../Storage/StorageTypes'
import { SNApiService } from './../Api/ApiService' import { SessionsClientInterface } from '../Session/SessionsClientInterface'
import { SNProtectionService } from '../Protection/ProtectionService' import { ProtectionsClientInterface } from '../Protection/ProtectionClientInterface'
import { SNSessionManager, MINIMUM_PASSWORD_LENGTH } from '../Session/SessionManager'
import { DiskStorageService } from '@Lib/Services/Storage/DiskStorageService'
import { SNSyncService } from '../Sync/SyncService'
import { Strings } from '../../Strings/index'
import { UuidGenerator } from '@standardnotes/utils'
import * as Messages from '../Api/Messages'
import { UserRegistrationResponseBody } from '@standardnotes/api'
const MINIMUM_PASSCODE_LENGTH = 1
export type CredentialsChangeFunctionResponse = { error?: { message: string } } export type CredentialsChangeFunctionResponse = { error?: { message: string } }
export type AccountServiceResponse = HttpResponse export type AccountServiceResponse = HttpResponse
@@ -44,16 +41,19 @@ export class UserService extends AbstractService<AccountEvent, AccountEventData>
private signingIn = false private signingIn = false
private registering = false private registering = false
private readonly MINIMUM_PASSCODE_LENGTH = 1
private readonly MINIMUM_PASSWORD_LENGTH = 8
constructor( constructor(
private sessionManager: SNSessionManager, private sessionManager: SessionsClientInterface,
private syncService: SNSyncService, private syncService: SyncServiceInterface,
private storageService: DiskStorageService, private storageService: StorageServiceInterface,
private itemManager: ItemManager, private itemManager: ItemManagerInterface,
private protocolService: EncryptionService, private protocolService: EncryptionProviderInterface,
private alertService: AlertService, private alertService: AlertService,
private challengeService: ChallengeService, private challengeService: ChallengeServiceInterface,
private protectionService: SNProtectionService, private protectionService: ProtectionsClientInterface,
private apiService: SNApiService, private userApiService: UserApiServiceInterface,
protected override internalEventBus: InternalEventBusInterface, protected override internalEventBus: InternalEventBusInterface,
) { ) {
super(internalEventBus) super(internalEventBus)
@@ -69,7 +69,7 @@ export class UserService extends AbstractService<AccountEvent, AccountEventData>
;(this.alertService as unknown) = undefined ;(this.alertService as unknown) = undefined
;(this.challengeService as unknown) = undefined ;(this.challengeService as unknown) = undefined
;(this.protectionService as unknown) = undefined ;(this.protectionService as unknown) = undefined
;(this.apiService as unknown) = undefined ;(this.userApiService as unknown) = undefined
} }
/** /**
@@ -204,7 +204,9 @@ export class UserService extends AbstractService<AccountEvent, AccountEventData>
}> { }> {
if ( if (
!(await this.protectionService.authorizeAction(ChallengeReason.DeleteAccount, { !(await this.protectionService.authorizeAction(ChallengeReason.DeleteAccount, {
fallBackToAccountPassword: true,
requireAccountPassword: true, requireAccountPassword: true,
forcePrompt: false,
})) }))
) { ) {
return { return {
@@ -214,17 +216,17 @@ export class UserService extends AbstractService<AccountEvent, AccountEventData>
} }
const uuid = this.sessionManager.getSureUser().uuid const uuid = this.sessionManager.getSureUser().uuid
const response = await this.apiService.deleteAccount(uuid) const response = await this.userApiService.deleteAccount(uuid)
if (response.error) { if (response.data.error) {
return { return {
error: true, error: true,
message: response.error.message, message: response.data.error.message,
} }
} }
await this.signOut(true) await this.signOut(true)
void this.alertService.alert(Strings.Info.AccountDeleted) void this.alertService.alert(InfoStrings.AccountDeleted)
return { return {
error: false, error: false,
@@ -239,7 +241,11 @@ export class UserService extends AbstractService<AccountEvent, AccountEventData>
public async correctiveSignIn(rootKey: SNRootKey): Promise<HttpResponse | SignInResponse> { public async correctiveSignIn(rootKey: SNRootKey): Promise<HttpResponse | SignInResponse> {
this.lockSyncing() this.lockSyncing()
const response = await this.sessionManager.bypassChecksAndSignInWithRootKey(rootKey.keyParams.identifier, rootKey) const response = await this.sessionManager.bypassChecksAndSignInWithRootKey(
rootKey.keyParams.identifier,
rootKey,
false,
)
if (!response.error) { if (!response.error) {
await this.notifyEvent(AccountEvent.SignedInOrRegistered) await this.notifyEvent(AccountEvent.SignedInOrRegistered)
@@ -381,7 +387,7 @@ export class UserService extends AbstractService<AccountEvent, AccountEventData>
} }
public async addPasscode(passcode: string): Promise<boolean> { public async addPasscode(passcode: string): Promise<boolean> {
if (passcode.length < MINIMUM_PASSCODE_LENGTH) { if (passcode.length < this.MINIMUM_PASSCODE_LENGTH) {
return false return false
} }
if (!(await this.protectionService.authorizeAddingPasscode())) { if (!(await this.protectionService.authorizeAddingPasscode())) {
@@ -424,7 +430,7 @@ export class UserService extends AbstractService<AccountEvent, AccountEventData>
newPasscode: string, newPasscode: string,
origination = KeyParamsOrigination.PasscodeChange, origination = KeyParamsOrigination.PasscodeChange,
): Promise<boolean> { ): Promise<boolean> {
if (newPasscode.length < MINIMUM_PASSCODE_LENGTH) { if (newPasscode.length < this.MINIMUM_PASSCODE_LENGTH) {
return false return false
} }
if (!(await this.protectionService.authorizeChangingPasscode())) { if (!(await this.protectionService.authorizeChangingPasscode())) {
@@ -501,9 +507,9 @@ export class UserService extends AbstractService<AccountEvent, AccountEventData>
} }
if (parameters.newPassword !== undefined && parameters.validateNewPasswordStrength) { if (parameters.newPassword !== undefined && parameters.validateNewPasswordStrength) {
if (parameters.newPassword.length < MINIMUM_PASSWORD_LENGTH) { if (parameters.newPassword.length < this.MINIMUM_PASSWORD_LENGTH) {
return { return {
error: Error(Messages.InsufficientPasswordMessage(MINIMUM_PASSWORD_LENGTH)), error: Error(Messages.InsufficientPasswordMessage(this.MINIMUM_PASSWORD_LENGTH)),
} }
} }
} }

View File

@@ -6,7 +6,7 @@ export * from './Application/ApplicationStage'
export * from './Application/DeinitCallback' export * from './Application/DeinitCallback'
export * from './Application/DeinitSource' export * from './Application/DeinitSource'
export * from './Application/DeinitMode' export * from './Application/DeinitMode'
export * from './Application/UserClientInterface' export * from './User/UserClientInterface'
export * from './Application/WebApplicationInterface' export * from './Application/WebApplicationInterface'
export * from './Backups/BackupService' export * from './Backups/BackupService'
export * from './Challenge' export * from './Challenge'
@@ -58,8 +58,13 @@ export * from './Item/ItemRelationshipDirection'
export * from './Mutator/MutatorClientInterface' export * from './Mutator/MutatorClientInterface'
export * from './Payloads/PayloadManagerInterface' export * from './Payloads/PayloadManagerInterface'
export * from './Preferences/PreferenceServiceInterface' export * from './Preferences/PreferenceServiceInterface'
export * from './Protection/MobileUnlockTiming'
export * from './Protection/ProtectionClientInterface'
export * from './Protection/TimingDisplayOption'
export * from './Service/AbstractService' export * from './Service/AbstractService'
export * from './Service/ServiceInterface' export * from './Service/ServiceInterface'
export * from './Session/SessionManagerResponse'
export * from './Session/SessionsClientInterface'
export * from './Status/StatusService' export * from './Status/StatusService'
export * from './Status/StatusServiceInterface' export * from './Status/StatusServiceInterface'
export * from './Storage/StorageKeys' export * from './Storage/StorageKeys'
@@ -67,6 +72,8 @@ export * from './Storage/InMemoryStore'
export * from './Storage/KeyValueStoreInterface' export * from './Storage/KeyValueStoreInterface'
export * from './Storage/StorageServiceInterface' export * from './Storage/StorageServiceInterface'
export * from './Storage/StorageTypes' export * from './Storage/StorageTypes'
export * from './Strings/InfoStrings'
export * from './Strings/Messages'
export * from './Subscription/SubscriptionClientInterface' export * from './Subscription/SubscriptionClientInterface'
export * from './Subscription/SubscriptionManager' export * from './Subscription/SubscriptionManager'
export * from './Sync/SyncMode' export * from './Sync/SyncMode'
@@ -74,5 +81,7 @@ export * from './Sync/SyncOptions'
export * from './Sync/SyncQueueStrategy' export * from './Sync/SyncQueueStrategy'
export * from './Sync/SyncServiceInterface' export * from './Sync/SyncServiceInterface'
export * from './Sync/SyncSource' export * from './Sync/SyncSource'
export * from './User/UserClientInterface'
export * from './User/UserService'
export * from './Workspace/WorkspaceClientInterface' export * from './Workspace/WorkspaceClientInterface'
export * from './Workspace/WorkspaceManager' export * from './Workspace/WorkspaceManager'

View File

@@ -8,6 +8,8 @@ import {
UserApiService, UserApiService,
UserApiServiceInterface, UserApiServiceInterface,
UserRegistrationResponseBody, UserRegistrationResponseBody,
UserRequestServer,
UserRequestServerInterface,
UserServer, UserServer,
UserServerInterface, UserServerInterface,
WebSocketApiService, WebSocketApiService,
@@ -52,6 +54,15 @@ import {
WorkspaceClientInterface, WorkspaceClientInterface,
WorkspaceManager, WorkspaceManager,
ChallengePrompt, ChallengePrompt,
Challenge,
ErrorAlertStrings,
SessionsClientInterface,
ProtectionsClientInterface,
UserService,
ProtocolUpgradeStrings,
CredentialsChangeFunctionResponse,
SessionStrings,
AccountEvent,
} from '@standardnotes/services' } from '@standardnotes/services'
import { FilesClientInterface } from '@standardnotes/files' import { FilesClientInterface } from '@standardnotes/files'
import { ComputePrivateUsername } from '@standardnotes/encryption' import { ComputePrivateUsername } from '@standardnotes/encryption'
@@ -68,7 +79,7 @@ import { ClientDisplayableError } from '@standardnotes/responses'
import { SnjsVersion } from './../Version' import { SnjsVersion } from './../Version'
import { SNLog } from '../Log' import { SNLog } from '../Log'
import { Challenge, ChallengeResponse, ListedClientInterface } from '../Services' import { ChallengeResponse, ListedClientInterface } from '../Services'
import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions' import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions'
import { ApplicationOptionsDefaults } from './Options/Defaults' import { ApplicationOptionsDefaults } from './Options/Defaults'
@@ -112,6 +123,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
private apiService!: InternalServices.SNApiService private apiService!: InternalServices.SNApiService
private declare userApiService: UserApiServiceInterface private declare userApiService: UserApiServiceInterface
private declare userServer: UserServerInterface private declare userServer: UserServerInterface
private declare userRequestServer: UserRequestServerInterface
private declare subscriptionApiService: SubscriptionApiServiceInterface private declare subscriptionApiService: SubscriptionApiServiceInterface
private declare subscriptionServer: SubscriptionServerInterface private declare subscriptionServer: SubscriptionServerInterface
private declare subscriptionManager: SubscriptionClientInterface private declare subscriptionManager: SubscriptionClientInterface
@@ -132,7 +144,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
private keyRecoveryService!: InternalServices.SNKeyRecoveryService private keyRecoveryService!: InternalServices.SNKeyRecoveryService
private preferencesService!: InternalServices.SNPreferencesService private preferencesService!: InternalServices.SNPreferencesService
private featuresService!: InternalServices.SNFeaturesService private featuresService!: InternalServices.SNFeaturesService
private userService!: InternalServices.UserService private userService!: UserService
private webSocketsService!: InternalServices.SNWebSocketsService private webSocketsService!: InternalServices.SNWebSocketsService
private settingsService!: InternalServices.SNSettingsService private settingsService!: InternalServices.SNSettingsService
private mfaService!: InternalServices.SNMfaService private mfaService!: InternalServices.SNMfaService
@@ -235,7 +247,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.itemManager return this.itemManager
} }
public get protections(): InternalServices.ProtectionsClientInterface { public get protections(): ProtectionsClientInterface {
return this.protectionService return this.protectionService
} }
@@ -255,7 +267,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.mutatorService return this.mutatorService
} }
public get sessions(): InternalServices.SessionsClientInterface { public get sessions(): SessionsClientInterface {
return this.sessionManager return this.sessionManager
} }
@@ -347,8 +359,8 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
await this.diskStorageService.decryptStorage() await this.diskStorageService.decryptStorage()
} catch (_error) { } catch (_error) {
void this.alertService.alert( void this.alertService.alert(
InternalServices.ErrorAlertStrings.StorageDecryptErrorBody, ErrorAlertStrings.StorageDecryptErrorBody,
InternalServices.ErrorAlertStrings.StorageDecryptErrorTitle, ErrorAlertStrings.StorageDecryptErrorTitle,
) )
} }
} }
@@ -628,12 +640,12 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
const result = await this.userService.performProtocolUpgrade() const result = await this.userService.performProtocolUpgrade()
if (result.success) { if (result.success) {
if (this.hasAccount()) { if (this.hasAccount()) {
void this.alertService.alert(InternalServices.ProtocolUpgradeStrings.SuccessAccount) void this.alertService.alert(ProtocolUpgradeStrings.SuccessAccount)
} else { } else {
void this.alertService.alert(InternalServices.ProtocolUpgradeStrings.SuccessPasscodeOnly) void this.alertService.alert(ProtocolUpgradeStrings.SuccessPasscodeOnly)
} }
} else if (result.error) { } else if (result.error) {
void this.alertService.alert(InternalServices.ProtocolUpgradeStrings.Fail) void this.alertService.alert(ProtocolUpgradeStrings.Fail)
} }
return result return result
} }
@@ -848,7 +860,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
currentPassword: string, currentPassword: string,
passcode?: string, passcode?: string,
origination = Common.KeyParamsOrigination.EmailChange, origination = Common.KeyParamsOrigination.EmailChange,
): Promise<InternalServices.CredentialsChangeFunctionResponse> { ): Promise<CredentialsChangeFunctionResponse> {
return this.userService.changeCredentials({ return this.userService.changeCredentials({
currentPassword, currentPassword,
newEmail, newEmail,
@@ -864,7 +876,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
passcode?: string, passcode?: string,
origination = Common.KeyParamsOrigination.PasswordChange, origination = Common.KeyParamsOrigination.PasswordChange,
validateNewPasswordStrength = true, validateNewPasswordStrength = true,
): Promise<InternalServices.CredentialsChangeFunctionResponse> { ): Promise<CredentialsChangeFunctionResponse> {
return this.userService.changeCredentials({ return this.userService.changeCredentials({
currentPassword, currentPassword,
newPassword, newPassword,
@@ -886,7 +898,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
/** Keep a reference to the soon-to-be-cleared alertService */ /** Keep a reference to the soon-to-be-cleared alertService */
const alertService = this.alertService const alertService = this.alertService
await this.user.signOut(true) await this.user.signOut(true)
void alertService.alert(InternalServices.SessionStrings.CurrentSessionRevoked) void alertService.alert(SessionStrings.CurrentSessionRevoked)
} }
public async validateAccountPassword(password: string): Promise<boolean> { public async validateAccountPassword(password: string): Promise<boolean> {
@@ -1064,6 +1076,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.createApiService() this.createApiService()
this.createHttpService() this.createHttpService()
this.createUserServer() this.createUserServer()
this.createUserRequestServer()
this.createUserApiService() this.createUserApiService()
this.createSubscriptionServer() this.createSubscriptionServer()
this.createSubscriptionApiService() this.createSubscriptionApiService()
@@ -1111,6 +1124,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
;(this.apiService as unknown) = undefined ;(this.apiService as unknown) = undefined
;(this.userApiService as unknown) = undefined ;(this.userApiService as unknown) = undefined
;(this.userServer as unknown) = undefined ;(this.userServer as unknown) = undefined
;(this.userRequestServer as unknown) = undefined
;(this.subscriptionApiService as unknown) = undefined ;(this.subscriptionApiService as unknown) = undefined
;(this.subscriptionServer as unknown) = undefined ;(this.subscriptionServer as unknown) = undefined
;(this.subscriptionManager as unknown) = undefined ;(this.subscriptionManager as unknown) = undefined
@@ -1261,7 +1275,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
} }
private createUserService(): void { private createUserService(): void {
this.userService = new InternalServices.UserService( this.userService = new UserService(
this.sessionManager, this.sessionManager,
this.syncService, this.syncService,
this.diskStorageService, this.diskStorageService,
@@ -1270,17 +1284,17 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
this.alertService, this.alertService,
this.challengeService, this.challengeService,
this.protectionService, this.protectionService,
this.apiService, this.userApiService,
this.internalEventBus, this.internalEventBus,
) )
this.serviceObservers.push( this.serviceObservers.push(
this.userService.addEventObserver(async (event, data) => { this.userService.addEventObserver(async (event, data) => {
switch (event) { switch (event) {
case InternalServices.AccountEvent.SignedInOrRegistered: { case AccountEvent.SignedInOrRegistered: {
void this.notifyEvent(ApplicationEvent.SignedIn) void this.notifyEvent(ApplicationEvent.SignedIn)
break break
} }
case InternalServices.AccountEvent.SignedOut: { case AccountEvent.SignedOut: {
await this.notifyEvent(ApplicationEvent.SignedOut) await this.notifyEvent(ApplicationEvent.SignedOut)
await this.prepareForDeinit() await this.prepareForDeinit()
this.deinit(this.getDeinitMode(), data?.source || DeinitSource.SignOut) this.deinit(this.getDeinitMode(), data?.source || DeinitSource.SignOut)
@@ -1308,13 +1322,17 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
} }
private createUserApiService() { private createUserApiService() {
this.userApiService = new UserApiService(this.userServer) this.userApiService = new UserApiService(this.userServer, this.userRequestServer)
} }
private createUserServer() { private createUserServer() {
this.userServer = new UserServer(this.httpService) this.userServer = new UserServer(this.httpService)
} }
private createUserRequestServer() {
this.userRequestServer = new UserRequestServer(this.httpService)
}
private createSubscriptionApiService() { private createSubscriptionApiService() {
this.subscriptionApiService = new SubscriptionApiService(this.subscriptionServer) this.subscriptionApiService = new SubscriptionApiService(this.subscriptionServer)
} }

View File

@@ -1,5 +1,5 @@
import { InfoStrings } from '../Strings/Info'
import { NoteType } from '@standardnotes/features' import { NoteType } from '@standardnotes/features'
import { InfoStrings } from '@standardnotes/services'
import { import {
NoteMutator, NoteMutator,
SNNote, SNNote,

View File

@@ -1,8 +1,6 @@
import { AnyKeyParamsContent } from '@standardnotes/common' import { AnyKeyParamsContent } from '@standardnotes/common'
import { SNLog } from '@Lib/Log' import { SNLog } from '@Lib/Log'
import { EncryptedPayload, EncryptedTransferPayload, isErrorDecryptingPayload } from '@standardnotes/models' import { EncryptedPayload, EncryptedTransferPayload, isErrorDecryptingPayload } from '@standardnotes/models'
import { Challenge } from '../Services/Challenge'
import { KeychainRecoveryStrings, SessionStrings } from '../Services/Api/Messages'
import { PreviousSnjsVersion1_0_0, PreviousSnjsVersion2_0_0, SnjsVersion } from '../Version' import { PreviousSnjsVersion1_0_0, PreviousSnjsVersion2_0_0, SnjsVersion } from '../Version'
import { Migration } from '@Lib/Migrations/Migration' import { Migration } from '@Lib/Migrations/Migration'
import { import {
@@ -12,6 +10,9 @@ import {
ChallengeValidation, ChallengeValidation,
ChallengeReason, ChallengeReason,
ChallengePrompt, ChallengePrompt,
KeychainRecoveryStrings,
SessionStrings,
Challenge,
} from '@standardnotes/services' } from '@standardnotes/services'
import { isNullOrUndefined } from '@standardnotes/utils' import { isNullOrUndefined } from '@standardnotes/utils'
import { CreateReader } from './StorageReaders/Functions' import { CreateReader } from './StorageReaders/Functions'

View File

@@ -1,6 +1,11 @@
import { Challenge } from '../Services/Challenge'
import { MigrationServices } from './MigrationServices' import { MigrationServices } from './MigrationServices'
import { ApplicationStage, ChallengeValidation, ChallengeReason, ChallengePrompt } from '@standardnotes/services' import {
ApplicationStage,
ChallengeValidation,
ChallengeReason,
ChallengePrompt,
Challenge,
} from '@standardnotes/services'
type StageHandler = () => Promise<void> type StageHandler = () => Promise<void>

View File

@@ -1,5 +1,5 @@
import { SNRootKey } from '@standardnotes/encryption' import { SNRootKey } from '@standardnotes/encryption'
import { Challenge, ChallengeService } from '../Challenge' import { ChallengeService } from '../Challenge'
import { ListedService } from '../Listed/ListedService' import { ListedService } from '../Listed/ListedService'
import { ActionResponse, HttpResponse } from '@standardnotes/responses' import { ActionResponse, HttpResponse } from '@standardnotes/responses'
import { ContentType } from '@standardnotes/common' import { ContentType } from '@standardnotes/common'
@@ -31,6 +31,7 @@ import {
ChallengeReason, ChallengeReason,
ChallengePrompt, ChallengePrompt,
EncryptionService, EncryptionService,
Challenge,
} from '@standardnotes/services' } from '@standardnotes/services'
/** /**

View File

@@ -13,28 +13,44 @@ import {
MetaReceivedData, MetaReceivedData,
DiagnosticInfo, DiagnosticInfo,
KeyValueStoreInterface, KeyValueStoreInterface,
API_MESSAGE_GENERIC_SYNC_FAIL,
API_MESSAGE_GENERIC_TOKEN_REFRESH_FAIL,
API_MESSAGE_CHANGE_CREDENTIALS_IN_PROGRESS,
API_MESSAGE_FAILED_ACCESS_PURCHASE,
API_MESSAGE_FAILED_CREATE_FILE_TOKEN,
API_MESSAGE_FAILED_DELETE_REVISION,
API_MESSAGE_FAILED_GET_SETTINGS,
API_MESSAGE_FAILED_LISTED_REGISTRATION,
API_MESSAGE_FAILED_OFFLINE_ACTIVATION,
API_MESSAGE_FAILED_OFFLINE_FEATURES,
API_MESSAGE_FAILED_SUBSCRIPTION_INFO,
API_MESSAGE_FAILED_UPDATE_SETTINGS,
API_MESSAGE_GENERIC_CHANGE_CREDENTIALS_FAIL,
API_MESSAGE_GENERIC_INTEGRITY_CHECK_FAIL,
API_MESSAGE_GENERIC_INVALID_LOGIN,
API_MESSAGE_GENERIC_SINGLE_ITEM_SYNC_FAIL,
API_MESSAGE_INVALID_SESSION,
API_MESSAGE_LOGIN_IN_PROGRESS,
API_MESSAGE_TOKEN_REFRESH_IN_PROGRESS,
} from '@standardnotes/services' } from '@standardnotes/services'
import { FilesApiInterface } from '@standardnotes/files' import { FilesApiInterface } from '@standardnotes/files'
import { ServerSyncPushContextualPayload, SNFeatureRepo, FileContent } from '@standardnotes/models' import { ServerSyncPushContextualPayload, SNFeatureRepo, FileContent } from '@standardnotes/models'
import * as Responses from '@standardnotes/responses' import * as Responses from '@standardnotes/responses'
import { API_MESSAGE_FAILED_OFFLINE_ACTIVATION } from '@Lib/Services/Api/Messages'
import { HttpParams, HttpRequest, HttpVerb, SNHttpService } from './HttpService' import { HttpParams, HttpRequest, HttpVerb, SNHttpService } from './HttpService'
import { isUrlFirstParty, TRUSTED_FEATURE_HOSTS } from '@Lib/Hosts' import { isUrlFirstParty, TRUSTED_FEATURE_HOSTS } from '@Lib/Hosts'
import { Paths } from './Paths' import { Paths } from './Paths'
import { Session } from '../Session/Sessions/Session' import { Session } from '../Session/Sessions/Session'
import { TokenSession } from '../Session/Sessions/TokenSession' import { TokenSession } from '../Session/Sessions/TokenSession'
import { DiskStorageService } from '../Storage/DiskStorageService' import { DiskStorageService } from '../Storage/DiskStorageService'
import { UserServerInterface } from '../User/UserServerInterface' import { HttpResponseMeta } from '@standardnotes/api'
import { UuidString } from '../../Types/UuidString' import { UuidString } from '../../Types/UuidString'
import * as messages from '@Lib/Services/Api/Messages'
import merge from 'lodash/merge' import merge from 'lodash/merge'
import { SettingsServerInterface } from '../Settings/SettingsServerInterface' import { SettingsServerInterface } from '../Settings/SettingsServerInterface'
import { Strings } from '@Lib/Strings' import { Strings } from '@Lib/Strings'
import { SNRootKeyParams } from '@standardnotes/encryption' import { SNRootKeyParams } from '@standardnotes/encryption'
import { ApiEndpointParam, ClientDisplayableError, CreateValetTokenPayload } from '@standardnotes/responses' import { ApiEndpointParam, ClientDisplayableError, CreateValetTokenPayload } from '@standardnotes/responses'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common' import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { HttpResponseMeta } from '@standardnotes/api'
/** Legacy api version field to be specified in params when calling v0 APIs. */ /** Legacy api version field to be specified in params when calling v0 APIs. */
const V0_API_VERSION = '20200115' const V0_API_VERSION = '20200115'
@@ -48,7 +64,6 @@ export class SNApiService
FilesApiInterface, FilesApiInterface,
IntegrityApiInterface, IntegrityApiInterface,
ItemsServerInterface, ItemsServerInterface,
UserServerInterface,
SettingsServerInterface SettingsServerInterface
{ {
private session?: Session private session?: Session
@@ -232,7 +247,7 @@ export class SNApiService
return this.request({ return this.request({
verb: HttpVerb.Post, verb: HttpVerb.Post,
url: joinPaths(this.host, Paths.v2.keyParams), url: joinPaths(this.host, Paths.v2.keyParams),
fallbackErrorMessage: messages.API_MESSAGE_GENERIC_INVALID_LOGIN, fallbackErrorMessage: API_MESSAGE_GENERIC_INVALID_LOGIN,
params, params,
/** A session is optional here, if valid, endpoint bypasses 2FA and returns additional params */ /** A session is optional here, if valid, endpoint bypasses 2FA and returns additional params */
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
@@ -245,7 +260,7 @@ export class SNApiService
ephemeral: boolean ephemeral: boolean
}): Promise<Responses.SignInResponse | Responses.HttpResponse> { }): Promise<Responses.SignInResponse | Responses.HttpResponse> {
if (this.authenticating) { if (this.authenticating) {
return this.createErrorResponse(messages.API_MESSAGE_LOGIN_IN_PROGRESS) as Responses.SignInResponse return this.createErrorResponse(API_MESSAGE_LOGIN_IN_PROGRESS) as Responses.SignInResponse
} }
this.authenticating = true this.authenticating = true
const url = joinPaths(this.host, Paths.v2.signIn) const url = joinPaths(this.host, Paths.v2.signIn)
@@ -260,7 +275,7 @@ export class SNApiService
verb: HttpVerb.Post, verb: HttpVerb.Post,
url, url,
params, params,
fallbackErrorMessage: messages.API_MESSAGE_GENERIC_INVALID_LOGIN, fallbackErrorMessage: API_MESSAGE_GENERIC_INVALID_LOGIN,
}) })
this.authenticating = false this.authenticating = false
@@ -285,7 +300,7 @@ export class SNApiService
newEmail?: string newEmail?: string
}): Promise<Responses.ChangeCredentialsResponse | Responses.HttpResponse> { }): Promise<Responses.ChangeCredentialsResponse | Responses.HttpResponse> {
if (this.changing) { if (this.changing) {
return this.createErrorResponse(messages.API_MESSAGE_CHANGE_CREDENTIALS_IN_PROGRESS) return this.createErrorResponse(API_MESSAGE_CHANGE_CREDENTIALS_IN_PROGRESS)
} }
const preprocessingError = this.preprocessingError() const preprocessingError = this.preprocessingError()
if (preprocessingError) { if (preprocessingError) {
@@ -309,10 +324,7 @@ export class SNApiService
params, params,
}) })
} }
return this.errorResponseWithFallbackMessage( return this.errorResponseWithFallbackMessage(errorResponse, API_MESSAGE_GENERIC_CHANGE_CREDENTIALS_FAIL)
errorResponse,
messages.API_MESSAGE_GENERIC_CHANGE_CREDENTIALS_FAIL,
)
}) })
this.processResponse(response) this.processResponse(response)
@@ -321,17 +333,6 @@ export class SNApiService
return response return response
} }
public async deleteAccount(userUuid: string): Promise<Responses.HttpResponse | Responses.MinimalHttpResponse> {
const url = joinPaths(this.host, Paths.v1.deleteAccount(userUuid))
const response = await this.request({
verb: HttpVerb.Delete,
url,
authentication: this.session?.authorizationValue,
fallbackErrorMessage: messages.ServerErrorStrings.DeleteAccountError,
})
return response
}
async sync( async sync(
payloads: ServerSyncPushContextualPayload[], payloads: ServerSyncPushContextualPayload[],
lastSyncToken: string, lastSyncToken: string,
@@ -360,7 +361,7 @@ export class SNApiService
params, params,
}) })
} }
return this.errorResponseWithFallbackMessage(errorResponse, messages.API_MESSAGE_GENERIC_SYNC_FAIL) return this.errorResponseWithFallbackMessage(errorResponse, API_MESSAGE_GENERIC_SYNC_FAIL)
}) })
this.processResponse(response) this.processResponse(response)
@@ -405,7 +406,7 @@ export class SNApiService
}) })
.catch((errorResponse) => { .catch((errorResponse) => {
this.preprocessAuthenticatedErrorResponse(errorResponse) this.preprocessAuthenticatedErrorResponse(errorResponse)
return this.errorResponseWithFallbackMessage(errorResponse, messages.API_MESSAGE_GENERIC_TOKEN_REFRESH_FAIL) return this.errorResponseWithFallbackMessage(errorResponse, API_MESSAGE_GENERIC_TOKEN_REFRESH_FAIL)
}) })
this.refreshingSession = false this.refreshingSession = false
return result return result
@@ -427,7 +428,7 @@ export class SNApiService
url, url,
}) })
} }
return this.errorResponseWithFallbackMessage(errorResponse, messages.API_MESSAGE_GENERIC_SYNC_FAIL) return this.errorResponseWithFallbackMessage(errorResponse, API_MESSAGE_GENERIC_SYNC_FAIL)
}) })
this.processResponse(response) this.processResponse(response)
@@ -451,7 +452,7 @@ export class SNApiService
url, url,
}) })
} }
return this.errorResponseWithFallbackMessage(errorResponse, messages.API_MESSAGE_GENERIC_SYNC_FAIL) return this.errorResponseWithFallbackMessage(errorResponse, API_MESSAGE_GENERIC_SYNC_FAIL)
}) })
this.processResponse(response) this.processResponse(response)
return response return response
@@ -473,7 +474,7 @@ export class SNApiService
url, url,
}) })
} }
return this.errorResponseWithFallbackMessage(errorResponse, messages.API_MESSAGE_GENERIC_SYNC_FAIL) return this.errorResponseWithFallbackMessage(errorResponse, API_MESSAGE_GENERIC_SYNC_FAIL)
}) })
this.processResponse(response) this.processResponse(response)
return response return response
@@ -498,7 +499,7 @@ export class SNApiService
url, url,
}) })
} }
return this.errorResponseWithFallbackMessage(errorResponse, messages.API_MESSAGE_GENERIC_SYNC_FAIL) return this.errorResponseWithFallbackMessage(errorResponse, API_MESSAGE_GENERIC_SYNC_FAIL)
}) })
this.processResponse(response) this.processResponse(response)
return response return response
@@ -516,7 +517,7 @@ export class SNApiService
url, url,
}) })
} }
return this.errorResponseWithFallbackMessage(errorResponse, messages.API_MESSAGE_GENERIC_SYNC_FAIL) return this.errorResponseWithFallbackMessage(errorResponse, API_MESSAGE_GENERIC_SYNC_FAIL)
}) })
this.processResponse(response) this.processResponse(response)
return response return response
@@ -546,7 +547,7 @@ export class SNApiService
return await this.tokenRefreshableRequest<Responses.ListSettingsResponse>({ return await this.tokenRefreshableRequest<Responses.ListSettingsResponse>({
verb: HttpVerb.Get, verb: HttpVerb.Get,
url: joinPaths(this.host, Paths.v1.settings(userUuid)), url: joinPaths(this.host, Paths.v1.settings(userUuid)),
fallbackErrorMessage: messages.API_MESSAGE_FAILED_GET_SETTINGS, fallbackErrorMessage: API_MESSAGE_FAILED_GET_SETTINGS,
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
}) })
} }
@@ -566,7 +567,7 @@ export class SNApiService
verb: HttpVerb.Put, verb: HttpVerb.Put,
url: joinPaths(this.host, Paths.v1.settings(userUuid)), url: joinPaths(this.host, Paths.v1.settings(userUuid)),
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_UPDATE_SETTINGS, fallbackErrorMessage: API_MESSAGE_FAILED_UPDATE_SETTINGS,
params, params,
}) })
} }
@@ -576,7 +577,7 @@ export class SNApiService
verb: HttpVerb.Get, verb: HttpVerb.Get,
url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName.toLowerCase() as SettingName)), url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName.toLowerCase() as SettingName)),
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_GET_SETTINGS, fallbackErrorMessage: API_MESSAGE_FAILED_GET_SETTINGS,
}) })
} }
@@ -591,7 +592,7 @@ export class SNApiService
Paths.v1.subscriptionSetting(userUuid, settingName.toLowerCase() as SubscriptionSettingName), Paths.v1.subscriptionSetting(userUuid, settingName.toLowerCase() as SubscriptionSettingName),
), ),
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_GET_SETTINGS, fallbackErrorMessage: API_MESSAGE_FAILED_GET_SETTINGS,
}) })
} }
@@ -600,7 +601,7 @@ export class SNApiService
verb: HttpVerb.Delete, verb: HttpVerb.Delete,
url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName)), url: joinPaths(this.host, Paths.v1.setting(userUuid, settingName)),
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_UPDATE_SETTINGS, fallbackErrorMessage: API_MESSAGE_FAILED_UPDATE_SETTINGS,
}) })
} }
@@ -612,7 +613,7 @@ export class SNApiService
const response = await this.tokenRefreshableRequest({ const response = await this.tokenRefreshableRequest({
verb: HttpVerb.Delete, verb: HttpVerb.Delete,
url, url,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_DELETE_REVISION, fallbackErrorMessage: API_MESSAGE_FAILED_DELETE_REVISION,
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
}) })
return response return response
@@ -623,7 +624,7 @@ export class SNApiService
verb: HttpVerb.Get, verb: HttpVerb.Get,
url, url,
external: true, external: true,
fallbackErrorMessage: messages.API_MESSAGE_GENERIC_INVALID_LOGIN, fallbackErrorMessage: API_MESSAGE_GENERIC_INVALID_LOGIN,
}) })
} }
@@ -633,7 +634,7 @@ export class SNApiService
verb: HttpVerb.Get, verb: HttpVerb.Get,
url, url,
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_SUBSCRIPTION_INFO, fallbackErrorMessage: API_MESSAGE_FAILED_SUBSCRIPTION_INFO,
}) })
return response return response
} }
@@ -645,7 +646,7 @@ export class SNApiService
const response = await this.request({ const response = await this.request({
verb: HttpVerb.Get, verb: HttpVerb.Get,
url, url,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_SUBSCRIPTION_INFO, fallbackErrorMessage: API_MESSAGE_FAILED_SUBSCRIPTION_INFO,
}) })
return response return response
} }
@@ -656,7 +657,7 @@ export class SNApiService
verb: HttpVerb.Post, verb: HttpVerb.Post,
url, url,
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_ACCESS_PURCHASE, fallbackErrorMessage: API_MESSAGE_FAILED_ACCESS_PURCHASE,
}) })
return (response as Responses.PostSubscriptionTokensResponse).data?.token return (response as Responses.PostSubscriptionTokensResponse).data?.token
} }
@@ -680,7 +681,7 @@ export class SNApiService
const response: Responses.HttpResponse | Responses.GetOfflineFeaturesResponse = await this.request({ const response: Responses.HttpResponse | Responses.GetOfflineFeaturesResponse = await this.request({
verb: HttpVerb.Get, verb: HttpVerb.Get,
url: featuresUrl, url: featuresUrl,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_OFFLINE_FEATURES, fallbackErrorMessage: API_MESSAGE_FAILED_OFFLINE_FEATURES,
customHeaders: [{ key: 'x-offline-token', value: extensionKey }], customHeaders: [{ key: 'x-offline-token', value: extensionKey }],
}) })
@@ -702,7 +703,7 @@ export class SNApiService
return await this.tokenRefreshableRequest<Responses.ListedRegistrationResponse>({ return await this.tokenRefreshableRequest<Responses.ListedRegistrationResponse>({
verb: HttpVerb.Post, verb: HttpVerb.Post,
url: joinPaths(this.host, Paths.v1.listedRegistration(this.user.uuid)), url: joinPaths(this.host, Paths.v1.listedRegistration(this.user.uuid)),
fallbackErrorMessage: messages.API_MESSAGE_FAILED_LISTED_REGISTRATION, fallbackErrorMessage: API_MESSAGE_FAILED_LISTED_REGISTRATION,
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
}) })
} }
@@ -723,7 +724,7 @@ export class SNApiService
verb: HttpVerb.Post, verb: HttpVerb.Post,
url: url, url: url,
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
fallbackErrorMessage: messages.API_MESSAGE_FAILED_CREATE_FILE_TOKEN, fallbackErrorMessage: API_MESSAGE_FAILED_CREATE_FILE_TOKEN,
params, params,
}) })
@@ -856,7 +857,7 @@ export class SNApiService
params: { params: {
integrityPayloads, integrityPayloads,
}, },
fallbackErrorMessage: messages.API_MESSAGE_GENERIC_INTEGRITY_CHECK_FAIL, fallbackErrorMessage: API_MESSAGE_GENERIC_INTEGRITY_CHECK_FAIL,
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
}) })
} }
@@ -865,17 +866,17 @@ export class SNApiService
return await this.tokenRefreshableRequest<Responses.GetSingleItemResponse>({ return await this.tokenRefreshableRequest<Responses.GetSingleItemResponse>({
verb: HttpVerb.Get, verb: HttpVerb.Get,
url: joinPaths(this.host, Paths.v1.getSingleItem(itemUuid)), url: joinPaths(this.host, Paths.v1.getSingleItem(itemUuid)),
fallbackErrorMessage: messages.API_MESSAGE_GENERIC_SINGLE_ITEM_SYNC_FAIL, fallbackErrorMessage: API_MESSAGE_GENERIC_SINGLE_ITEM_SYNC_FAIL,
authentication: this.session?.authorizationValue, authentication: this.session?.authorizationValue,
}) })
} }
private preprocessingError() { private preprocessingError() {
if (this.refreshingSession) { if (this.refreshingSession) {
return this.createErrorResponse(messages.API_MESSAGE_TOKEN_REFRESH_IN_PROGRESS) return this.createErrorResponse(API_MESSAGE_TOKEN_REFRESH_IN_PROGRESS)
} }
if (!this.session) { if (!this.session) {
return this.createErrorResponse(messages.API_MESSAGE_INVALID_SESSION) return this.createErrorResponse(API_MESSAGE_INVALID_SESSION)
} }
return undefined return undefined
} }

View File

@@ -1,8 +1,12 @@
import { API_MESSAGE_RATE_LIMITED, UNKNOWN_ERROR } from './Messages'
import { HttpResponse, StatusCode } from '@standardnotes/responses' import { HttpResponse, StatusCode } from '@standardnotes/responses'
import { isString } from '@standardnotes/utils' import { isString } from '@standardnotes/utils'
import { SnjsVersion } from '@Lib/Version' import { SnjsVersion } from '@Lib/Version'
import { AbstractService, InternalEventBusInterface } from '@standardnotes/services' import {
AbstractService,
API_MESSAGE_RATE_LIMITED,
InternalEventBusInterface,
UNKNOWN_ERROR,
} from '@standardnotes/services'
import { Environment } from '@standardnotes/models' import { Environment } from '@standardnotes/models'
export enum HttpVerb { export enum HttpVerb {

View File

@@ -1,6 +1,5 @@
export * from './ApiService' export * from './ApiService'
export * from './HttpService' export * from './HttpService'
export * from './Messages'
export * from './Paths' export * from './Paths'
export * from '../Session/Sessions/Session' export * from '../Session/Sessions/Session'
export * from '../Session/SessionManager' export * from '../Session/SessionManager'

View File

@@ -1,8 +1,7 @@
import { Challenge } from './Challenge' import { Challenge, ChallengeValue, ChallengeArtifacts } from '@standardnotes/services'
import { ChallengeResponse } from './ChallengeResponse' import { ChallengeResponse } from './ChallengeResponse'
import { removeFromArray } from '@standardnotes/utils' import { removeFromArray } from '@standardnotes/utils'
import { ValueCallback } from './ChallengeService' import { ValueCallback } from './ChallengeService'
import { ChallengeValue, ChallengeArtifacts } from '@standardnotes/services'
/** /**
* A challenge operation stores user-submitted values and callbacks. * A challenge operation stores user-submitted values and callbacks.

View File

@@ -1,6 +1,6 @@
import { isNullOrUndefined } from '@standardnotes/utils' import { isNullOrUndefined } from '@standardnotes/utils'
import { Challenge } from './Challenge'
import { import {
Challenge,
ChallengeResponseInterface, ChallengeResponseInterface,
ChallengeValidation, ChallengeValidation,
ChallengeValue, ChallengeValue,

View File

@@ -6,6 +6,7 @@ import {
AbstractService, AbstractService,
ChallengeServiceInterface, ChallengeServiceInterface,
InternalEventBusInterface, InternalEventBusInterface,
Challenge,
ChallengeArtifacts, ChallengeArtifacts,
ChallengeReason, ChallengeReason,
ChallengeValidation, ChallengeValidation,
@@ -17,7 +18,6 @@ import {
} from '@standardnotes/services' } from '@standardnotes/services'
import { ChallengeResponse } from './ChallengeResponse' import { ChallengeResponse } from './ChallengeResponse'
import { ChallengeOperation } from './ChallengeOperation' import { ChallengeOperation } from './ChallengeOperation'
import { Challenge } from './Challenge'
type ChallengeValidationResponse = { type ChallengeValidationResponse = {
valid: boolean valid: boolean

View File

@@ -1,4 +1,3 @@
export * from './Challenge'
export * from './ChallengeOperation' export * from './ChallengeOperation'
export * from './ChallengeResponse' export * from './ChallengeResponse'
export * from './ChallengeService' export * from './ChallengeService'

View File

@@ -1,15 +1,6 @@
import { ItemInterface, SNComponent, SNFeatureRepo } from '@standardnotes/models' import { ItemInterface, SNComponent, SNFeatureRepo } from '@standardnotes/models'
import { SNSyncService } from '../Sync/SyncService' import { SNSyncService } from '../Sync/SyncService'
import { SettingName } from '@standardnotes/settings' import { SettingName } from '@standardnotes/settings'
import {
ItemManager,
AlertService,
SNApiService,
UserService,
SNSessionManager,
DiskStorageService,
StorageKey,
} from '@Lib/index'
import { SNFeaturesService } from '@Lib/Services/Features' import { SNFeaturesService } from '@Lib/Services/Features'
import { ContentType, RoleName } from '@standardnotes/common' import { ContentType, RoleName } from '@standardnotes/common'
import { FeatureDescription, FeatureIdentifier, GetFeatures } from '@standardnotes/features' import { FeatureDescription, FeatureIdentifier, GetFeatures } from '@standardnotes/features'
@@ -17,7 +8,16 @@ import { SNWebSocketsService } from '../Api/WebsocketsService'
import { SNSettingsService } from '../Settings' import { SNSettingsService } from '../Settings'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common' import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { convertTimestampToMilliseconds } from '@standardnotes/utils' import { convertTimestampToMilliseconds } from '@standardnotes/utils'
import { FeatureStatus, InternalEventBusInterface } from '@standardnotes/services' import {
AlertService,
FeatureStatus,
InternalEventBusInterface,
StorageKey,
UserService,
} from '@standardnotes/services'
import { SNApiService, SNSessionManager } from '../Api'
import { ItemManager } from '../Items'
import { DiskStorageService } from '../Storage/DiskStorageService'
describe('featuresService', () => { describe('featuresService', () => {
let storageService: DiskStorageService let storageService: DiskStorageService

View File

@@ -1,4 +1,3 @@
import { AccountEvent, UserService } from '../User/UserService'
import { SNApiService } from '../Api/ApiService' import { SNApiService } from '../Api/ApiService'
import { import {
arraysEqual, arraysEqual,
@@ -24,12 +23,15 @@ import { TRUSTED_CUSTOM_EXTENSIONS_HOSTS, TRUSTED_FEATURE_HOSTS } from '@Lib/Hos
import { UserRolesChangedEvent } from '@standardnotes/domain-events' import { UserRolesChangedEvent } from '@standardnotes/domain-events'
import { UuidString } from '@Lib/Types/UuidString' import { UuidString } from '@Lib/Types/UuidString'
import * as FeaturesImports from '@standardnotes/features' import * as FeaturesImports from '@standardnotes/features'
import * as Messages from '@Lib/Services/Api/Messages'
import * as Models from '@standardnotes/models' import * as Models from '@standardnotes/models'
import { import {
AbstractService, AbstractService,
AccountEvent,
AlertService, AlertService,
ApiServiceEvent, ApiServiceEvent,
API_MESSAGE_FAILED_DOWNLOADING_EXTENSION,
API_MESSAGE_FAILED_OFFLINE_ACTIVATION,
API_MESSAGE_UNTRUSTED_EXTENSIONS_WARNING,
ApplicationStage, ApplicationStage,
ButtonType, ButtonType,
DiagnosticInfo, DiagnosticInfo,
@@ -39,10 +41,12 @@ import {
InternalEventBusInterface, InternalEventBusInterface,
InternalEventHandlerInterface, InternalEventHandlerInterface,
InternalEventInterface, InternalEventInterface,
INVALID_EXTENSION_URL,
MetaReceivedData, MetaReceivedData,
OfflineSubscriptionEntitlements, OfflineSubscriptionEntitlements,
SetOfflineFeaturesFunctionResponse, SetOfflineFeaturesFunctionResponse,
StorageKey, StorageKey,
UserService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { FeatureIdentifier } from '@standardnotes/features' import { FeatureIdentifier } from '@standardnotes/features'
@@ -250,7 +254,7 @@ export class SNFeaturesService
void this.syncService.sync() void this.syncService.sync()
return this.downloadOfflineFeatures(offlineRepo) return this.downloadOfflineFeatures(offlineRepo)
} catch (err) { } catch (err) {
return new ClientDisplayableError(Messages.API_MESSAGE_FAILED_OFFLINE_ACTIVATION) return new ClientDisplayableError(API_MESSAGE_FAILED_OFFLINE_ACTIVATION)
} }
} }
@@ -280,7 +284,7 @@ export class SNFeaturesService
extensionKey, extensionKey,
} }
} catch (error) { } catch (error) {
return new ClientDisplayableError(Messages.API_MESSAGE_FAILED_OFFLINE_ACTIVATION) return new ClientDisplayableError(API_MESSAGE_FAILED_OFFLINE_ACTIVATION)
} }
} }
@@ -631,7 +635,7 @@ export class SNFeaturesService
const { host } = new URL(url) const { host } = new URL(url)
if (!trustedCustomExtensionsUrls.includes(host)) { if (!trustedCustomExtensionsUrls.includes(host)) {
const didConfirm = await this.alertService.confirm( const didConfirm = await this.alertService.confirm(
Messages.API_MESSAGE_UNTRUSTED_EXTENSIONS_WARNING, API_MESSAGE_UNTRUSTED_EXTENSIONS_WARNING,
'Install extension from an untrusted source?', 'Install extension from an untrusted source?',
'Proceed to install', 'Proceed to install',
ButtonType.Danger, ButtonType.Danger,
@@ -644,7 +648,7 @@ export class SNFeaturesService
return this.performDownloadExternalFeature(url) return this.performDownloadExternalFeature(url)
} }
} catch (err) { } catch (err) {
void this.alertService.alert(Messages.INVALID_EXTENSION_URL) void this.alertService.alert(INVALID_EXTENSION_URL)
} }
return undefined return undefined
@@ -653,7 +657,7 @@ export class SNFeaturesService
private async performDownloadExternalFeature(url: string): Promise<Models.SNComponent | undefined> { private async performDownloadExternalFeature(url: string): Promise<Models.SNComponent | undefined> {
const response = await this.apiService.downloadFeatureUrl(url) const response = await this.apiService.downloadFeatureUrl(url)
if (response.error) { if (response.error) {
await this.alertService.alert(Messages.API_MESSAGE_FAILED_DOWNLOADING_EXTENSION) await this.alertService.alert(API_MESSAGE_FAILED_DOWNLOADING_EXTENSION)
return undefined return undefined
} }
@@ -683,14 +687,14 @@ export class SNFeaturesService
const nativeFeature = FeaturesImports.FindNativeFeature(rawFeature.identifier) const nativeFeature = FeaturesImports.FindNativeFeature(rawFeature.identifier)
if (nativeFeature) { if (nativeFeature) {
await this.alertService.alert(Messages.API_MESSAGE_FAILED_DOWNLOADING_EXTENSION) await this.alertService.alert(API_MESSAGE_FAILED_DOWNLOADING_EXTENSION)
return return
} }
if (rawFeature.url) { if (rawFeature.url) {
for (const nativeFeature of FeaturesImports.GetFeatures()) { for (const nativeFeature of FeaturesImports.GetFeatures()) {
if (rawFeature.url.includes(nativeFeature.identifier)) { if (rawFeature.url.includes(nativeFeature.identifier)) {
await this.alertService.alert(Messages.API_MESSAGE_FAILED_DOWNLOADING_EXTENSION) await this.alertService.alert(API_MESSAGE_FAILED_DOWNLOADING_EXTENSION)
return return
} }
} }

View File

@@ -1,6 +1,5 @@
import { KeyRecoveryOperation } from './KeyRecoveryOperation' import { KeyRecoveryOperation } from './KeyRecoveryOperation'
import { SNRootKeyParams, SNRootKey, KeyParamsFromApiResponse, KeyRecoveryStrings } from '@standardnotes/encryption' import { SNRootKeyParams, SNRootKey, KeyParamsFromApiResponse, KeyRecoveryStrings } from '@standardnotes/encryption'
import { UserService } from '../User/UserService'
import { import {
isErrorDecryptingPayload, isErrorDecryptingPayload,
EncryptedPayloadInterface, EncryptedPayloadInterface,
@@ -14,7 +13,7 @@ import {
import { SNSyncService } from '../Sync/SyncService' import { SNSyncService } from '../Sync/SyncService'
import { DiskStorageService } from '../Storage/DiskStorageService' import { DiskStorageService } from '../Storage/DiskStorageService'
import { PayloadManager } from '../Payloads/PayloadManager' import { PayloadManager } from '../Payloads/PayloadManager'
import { Challenge, ChallengeService } from '../Challenge' import { ChallengeService } from '../Challenge'
import { SNApiService } from '@Lib/Services/Api/ApiService' import { SNApiService } from '@Lib/Services/Api/ApiService'
import { ContentType, Uuid } from '@standardnotes/common' import { ContentType, Uuid } from '@standardnotes/common'
import { ItemManager } from '../Items/ItemManager' import { ItemManager } from '../Items/ItemManager'
@@ -32,6 +31,8 @@ import {
ChallengeReason, ChallengeReason,
ChallengePrompt, ChallengePrompt,
EncryptionService, EncryptionService,
Challenge,
UserService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { import {
UndecryptableItemsStorage, UndecryptableItemsStorage,

View File

@@ -1,11 +1,10 @@
import { SettingName } from '@standardnotes/settings' import { SettingName } from '@standardnotes/settings'
import { SNSettingsService } from '../Settings' import { SNSettingsService } from '../Settings'
import * as messages from '../Api/Messages'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common' import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
import { SNFeaturesService } from '../Features/FeaturesService' import { SNFeaturesService } from '../Features/FeaturesService'
import { FeatureIdentifier } from '@standardnotes/features' import { FeatureIdentifier } from '@standardnotes/features'
import { AbstractService, InternalEventBusInterface } from '@standardnotes/services' import { AbstractService, InternalEventBusInterface, SignInStrings } from '@standardnotes/services'
export class SNMfaService extends AbstractService { export class SNMfaService extends AbstractService {
constructor( constructor(
@@ -38,7 +37,7 @@ export class SNMfaService extends AbstractService {
const otpTokenValid = otpToken != undefined && otpToken === (await this.getOtpToken(secret)) const otpTokenValid = otpToken != undefined && otpToken === (await this.getOtpToken(secret))
if (!otpTokenValid) { if (!otpTokenValid) {
throw new Error(messages.SignInStrings.IncorrectMfa) throw new Error(SignInStrings.IncorrectMfa)
} }
return this.saveMfaSetting(secret) return this.saveMfaSetting(secret)

View File

@@ -7,6 +7,8 @@ import {
ChallengePrompt, ChallengePrompt,
ChallengeReason, ChallengeReason,
MutatorClientInterface, MutatorClientInterface,
Challenge,
InfoStrings,
} from '@standardnotes/services' } from '@standardnotes/services'
import { EncryptionProviderInterface } from '@standardnotes/encryption' import { EncryptionProviderInterface } from '@standardnotes/encryption'
import { ClientDisplayableError } from '@standardnotes/responses' import { ClientDisplayableError } from '@standardnotes/responses'
@@ -18,7 +20,7 @@ import { SNProtectionService } from '../Protection/ProtectionService'
import { SNSyncService } from '../Sync' import { SNSyncService } from '../Sync'
import { Strings } from '../../Strings' import { Strings } from '../../Strings'
import { TagsToFoldersMigrationApplicator } from '@Lib/Migrations/Applicators/TagsToFolders' import { TagsToFoldersMigrationApplicator } from '@Lib/Migrations/Applicators/TagsToFolders'
import { Challenge, ChallengeService } from '../Challenge' import { ChallengeService } from '../Challenge'
import { import {
BackupFile, BackupFile,
BackupFileDecryptedContextualPayload, BackupFileDecryptedContextualPayload,
@@ -170,7 +172,13 @@ export class MutatorService extends AbstractService implements MutatorClientInte
items: I[], items: I[],
reason: ChallengeReason, reason: ChallengeReason,
): Promise<I[] | undefined> { ): Promise<I[] | undefined> {
if (!(await this.protectionService.authorizeAction(reason))) { if (
!(await this.protectionService.authorizeAction(reason, {
fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: false,
}))
) {
return undefined return undefined
} }
@@ -314,13 +322,13 @@ export class MutatorService extends AbstractService implements MutatorClientInte
const supportedVersions = this.encryption.supportedVersions() const supportedVersions = this.encryption.supportedVersions()
if (!supportedVersions.includes(version)) { if (!supportedVersions.includes(version)) {
return { error: new ClientDisplayableError(Strings.Info.UnsupportedBackupFileVersion) } return { error: new ClientDisplayableError(InfoStrings.UnsupportedBackupFileVersion) }
} }
const userVersion = this.encryption.getUserVersion() const userVersion = this.encryption.getUserVersion()
if (userVersion && compareVersions(version, userVersion) === 1) { if (userVersion && compareVersions(version, userVersion) === 1) {
/** File was made with a greater version than the user's account */ /** File was made with a greater version than the user's account */
return { error: new ClientDisplayableError(Strings.Info.BackupFileMoreRecentThanAccount) } return { error: new ClientDisplayableError(InfoStrings.BackupFileMoreRecentThanAccount) }
} }
} }

View File

@@ -1,4 +1,3 @@
import { Challenge } from './../Challenge/Challenge'
import { ChallengeService } from './../Challenge/ChallengeService' import { ChallengeService } from './../Challenge/ChallengeService'
import { SNLog } from '@Lib/Log' import { SNLog } from '@Lib/Log'
import { DecryptedItem } from '@standardnotes/models' import { DecryptedItem } from '@standardnotes/models'
@@ -11,14 +10,16 @@ import {
ApplicationStage, ApplicationStage,
StorageKey, StorageKey,
DiagnosticInfo, DiagnosticInfo,
Challenge,
ChallengeReason, ChallengeReason,
ChallengePrompt, ChallengePrompt,
ChallengeValidation, ChallengeValidation,
EncryptionService, EncryptionService,
MobileUnlockTiming,
TimingDisplayOption,
ProtectionsClientInterface,
} from '@standardnotes/services' } from '@standardnotes/services'
import { ProtectionsClientInterface } from './ClientInterface'
import { ContentType } from '@standardnotes/common' import { ContentType } from '@standardnotes/common'
import { MobileUnlockTiming, TimingDisplayOption } from './MobileUnlockTiming'
export enum ProtectionEvent { export enum ProtectionEvent {
UnprotectedSessionBegan = 'UnprotectedSessionBegan', UnprotectedSessionBegan = 'UnprotectedSessionBegan',
@@ -176,62 +177,95 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
item.content_type === ContentType.Note item.content_type === ContentType.Note
? ChallengeReason.AccessProtectedNote ? ChallengeReason.AccessProtectedNote
: ChallengeReason.AccessProtectedFile, : ChallengeReason.AccessProtectedFile,
{ fallBackToAccountPassword: true, requireAccountPassword: false, forcePrompt: false },
) )
} }
authorizeAddingPasscode(): Promise<boolean> { authorizeAddingPasscode(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.AddPasscode) return this.authorizeAction(ChallengeReason.AddPasscode, {
fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: false,
})
} }
authorizeChangingPasscode(): Promise<boolean> { authorizeChangingPasscode(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.ChangePasscode) return this.authorizeAction(ChallengeReason.ChangePasscode, {
fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: false,
})
} }
authorizeRemovingPasscode(): Promise<boolean> { authorizeRemovingPasscode(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.RemovePasscode) return this.authorizeAction(ChallengeReason.RemovePasscode, {
fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: false,
})
} }
authorizeSearchingProtectedNotesText(): Promise<boolean> { authorizeSearchingProtectedNotesText(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.SearchProtectedNotesText) return this.authorizeAction(ChallengeReason.SearchProtectedNotesText, {
fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: false,
})
} }
authorizeFileImport(): Promise<boolean> { authorizeFileImport(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.ImportFile) return this.authorizeAction(ChallengeReason.ImportFile, {
fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: false,
})
} }
async authorizeBackupCreation(): Promise<boolean> { async authorizeBackupCreation(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.ExportBackup, { return this.authorizeAction(ChallengeReason.ExportBackup, {
fallBackToAccountPassword: true, fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: false,
}) })
} }
async authorizeMfaDisable(): Promise<boolean> { async authorizeMfaDisable(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.DisableMfa, { return this.authorizeAction(ChallengeReason.DisableMfa, {
fallBackToAccountPassword: true,
requireAccountPassword: true, requireAccountPassword: true,
forcePrompt: false,
}) })
} }
async authorizeAutolockIntervalChange(): Promise<boolean> { async authorizeAutolockIntervalChange(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.ChangeAutolockInterval) return this.authorizeAction(ChallengeReason.ChangeAutolockInterval, {
fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: false,
})
} }
async authorizeSessionRevoking(): Promise<boolean> { async authorizeSessionRevoking(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.RevokeSession) return this.authorizeAction(ChallengeReason.RevokeSession, {
fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: false,
})
} }
async authorizeListedPublishing(): Promise<boolean> { async authorizeListedPublishing(): Promise<boolean> {
return this.authorizeAction(ChallengeReason.AuthorizeNoteForListed, { forcePrompt: true }) return this.authorizeAction(ChallengeReason.AuthorizeNoteForListed, {
fallBackToAccountPassword: true,
requireAccountPassword: false,
forcePrompt: true,
})
} }
async authorizeAction( async authorizeAction(
reason: ChallengeReason, reason: ChallengeReason,
{ fallBackToAccountPassword = true, requireAccountPassword = false, forcePrompt = false } = {}, dto: { fallBackToAccountPassword: boolean; requireAccountPassword: boolean; forcePrompt: boolean },
): Promise<boolean> { ): Promise<boolean> {
return this.validateOrRenewSession(reason, { return this.validateOrRenewSession(reason, dto)
requireAccountPassword,
fallBackToAccountPassword,
forcePrompt,
})
} }
getMobilePasscodeTimingOptions(): TimingDisplayOption[] { getMobilePasscodeTimingOptions(): TimingDisplayOption[] {

View File

@@ -1,3 +1 @@
export * from './ClientInterface'
export * from './ProtectionService' export * from './ProtectionService'
export * from './MobileUnlockTiming'

View File

@@ -10,6 +10,18 @@ import {
ChallengeReason, ChallengeReason,
ChallengePromptTitle, ChallengePromptTitle,
EncryptionService, EncryptionService,
SessionsClientInterface,
SessionManagerResponse,
SessionStrings,
SignInStrings,
INVALID_PASSWORD_COST,
API_MESSAGE_FALLBACK_LOGIN_FAIL,
API_MESSAGE_GENERIC_SYNC_FAIL,
EXPIRED_PROTOCOL_VERSION,
StrictSignInFailed,
UNSUPPORTED_KEY_DERIVATION,
UNSUPPORTED_PROTOCOL_VERSION,
Challenge,
} from '@standardnotes/services' } from '@standardnotes/services'
import { Base64String } from '@standardnotes/sncrypto-common' import { Base64String } from '@standardnotes/sncrypto-common'
import { ClientDisplayableError } from '@standardnotes/responses' import { ClientDisplayableError } from '@standardnotes/responses'
@@ -17,11 +29,9 @@ import { CopyPayloadWithContentOverride } from '@standardnotes/models'
import { isNullOrUndefined } from '@standardnotes/utils' import { isNullOrUndefined } from '@standardnotes/utils'
import { JwtSession } from './Sessions/JwtSession' import { JwtSession } from './Sessions/JwtSession'
import { KeyParamsFromApiResponse, SNRootKeyParams, SNRootKey, CreateNewRootKey } from '@standardnotes/encryption' import { KeyParamsFromApiResponse, SNRootKeyParams, SNRootKey, CreateNewRootKey } from '@standardnotes/encryption'
import { SessionStrings, SignInStrings } from '../Api/Messages'
import { RemoteSession, RawStorageValue } from './Sessions/Types' import { RemoteSession, RawStorageValue } from './Sessions/Types'
import { Session } from './Sessions/Session' import { Session } from './Sessions/Session'
import { SessionFromRawStorageValue } from './Sessions/Generator' import { SessionFromRawStorageValue } from './Sessions/Generator'
import { SessionsClientInterface } from './SessionsClientInterface'
import { ShareToken } from './ShareToken' import { ShareToken } from './ShareToken'
import { SNApiService } from '../Api/ApiService' import { SNApiService } from '../Api/ApiService'
import { DiskStorageService } from '../Storage/DiskStorageService' import { DiskStorageService } from '../Storage/DiskStorageService'
@@ -31,9 +41,8 @@ import { Subscription } from '@standardnotes/security'
import { TokenSession } from './Sessions/TokenSession' import { TokenSession } from './Sessions/TokenSession'
import { UuidString } from '@Lib/Types/UuidString' import { UuidString } from '@Lib/Types/UuidString'
import * as Common from '@standardnotes/common' import * as Common from '@standardnotes/common'
import * as Messages from '../Api/Messages'
import * as Responses from '@standardnotes/responses' import * as Responses from '@standardnotes/responses'
import { Challenge, ChallengeService } from '../Challenge' import { ChallengeService } from '../Challenge'
import { import {
ApiCallError, ApiCallError,
ErrorMessage, ErrorMessage,
@@ -46,12 +55,6 @@ import {
export const MINIMUM_PASSWORD_LENGTH = 8 export const MINIMUM_PASSWORD_LENGTH = 8
export const MissingAccountParams = 'missing-params' export const MissingAccountParams = 'missing-params'
type SessionManagerResponse = {
response: Responses.HttpResponse
rootKey?: SNRootKey
keyParams?: Common.AnyKeyParamsContent
}
const cleanedEmailString = (email: string) => { const cleanedEmailString = (email: string) => {
return email.trim().toLowerCase() return email.trim().toLowerCase()
} }
@@ -338,7 +341,7 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
const keyParams = KeyParamsFromApiResponse(response as Responses.KeyParamsResponse, email) const keyParams = KeyParamsFromApiResponse(response as Responses.KeyParamsResponse, email)
if (!keyParams || !keyParams.version) { if (!keyParams || !keyParams.version) {
return { return {
response: this.apiService.createErrorResponse(Messages.API_MESSAGE_FALLBACK_LOGIN_FAIL), response: this.apiService.createErrorResponse(API_MESSAGE_FALLBACK_LOGIN_FAIL),
} }
} }
return { keyParams, response, mfaKeyPath, mfaCode } return { keyParams, response, mfaKeyPath, mfaCode }
@@ -388,11 +391,11 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
if (!this.protocolService.supportedVersions().includes(keyParams.version)) { if (!this.protocolService.supportedVersions().includes(keyParams.version)) {
if (this.protocolService.isVersionNewerThanLibraryVersion(keyParams.version)) { if (this.protocolService.isVersionNewerThanLibraryVersion(keyParams.version)) {
return { return {
response: this.apiService.createErrorResponse(Messages.UNSUPPORTED_PROTOCOL_VERSION), response: this.apiService.createErrorResponse(UNSUPPORTED_PROTOCOL_VERSION),
} }
} else { } else {
return { return {
response: this.apiService.createErrorResponse(Messages.EXPIRED_PROTOCOL_VERSION), response: this.apiService.createErrorResponse(EXPIRED_PROTOCOL_VERSION),
} }
} }
} }
@@ -402,7 +405,7 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
const minimum = this.protocolService.costMinimumForVersion(keyParams.version) const minimum = this.protocolService.costMinimumForVersion(keyParams.version)
if (keyParams.content002.pw_cost < minimum) { if (keyParams.content002.pw_cost < minimum) {
return { return {
response: this.apiService.createErrorResponse(Messages.INVALID_PASSWORD_COST), response: this.apiService.createErrorResponse(INVALID_PASSWORD_COST),
} }
} }
@@ -415,14 +418,14 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
if (!confirmed) { if (!confirmed) {
return { return {
response: this.apiService.createErrorResponse(Messages.API_MESSAGE_FALLBACK_LOGIN_FAIL), response: this.apiService.createErrorResponse(API_MESSAGE_FALLBACK_LOGIN_FAIL),
} }
} }
} }
if (!this.protocolService.platformSupportsKeyDerivation(keyParams)) { if (!this.protocolService.platformSupportsKeyDerivation(keyParams)) {
return { return {
response: this.apiService.createErrorResponse(Messages.UNSUPPORTED_KEY_DERIVATION), response: this.apiService.createErrorResponse(UNSUPPORTED_KEY_DERIVATION),
} }
} }
@@ -433,9 +436,7 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
if (!isNullOrUndefined(minAllowedVersion)) { if (!isNullOrUndefined(minAllowedVersion)) {
if (!Common.leftVersionGreaterThanOrEqualToRight(keyParams.version, minAllowedVersion)) { if (!Common.leftVersionGreaterThanOrEqualToRight(keyParams.version, minAllowedVersion)) {
return { return {
response: this.apiService.createErrorResponse( response: this.apiService.createErrorResponse(StrictSignInFailed(keyParams.version, minAllowedVersion)),
Messages.StrictSignInFailed(keyParams.version, minAllowedVersion),
),
} }
} }
} }
@@ -532,7 +533,7 @@ export class SNSessionManager extends AbstractService<SessionEvent> implements S
public async revokeAllOtherSessions(): Promise<void> { public async revokeAllOtherSessions(): Promise<void> {
const response = await this.getSessionsList() const response = await this.getSessionsList()
if (response.error != undefined || response.data == undefined) { if (response.error != undefined || response.data == undefined) {
throw new Error(response.error?.message ?? Messages.API_MESSAGE_GENERIC_SYNC_FAIL) throw new Error(response.error?.message ?? API_MESSAGE_GENERIC_SYNC_FAIL)
} }
const sessions = response.data as RemoteSession[] const sessions = response.data as RemoteSession[]
const otherSessions = sessions.filter((session) => !session.current) const otherSessions = sessions.filter((session) => !session.current)

View File

@@ -1,8 +0,0 @@
import { ClientDisplayableError, User } from '@standardnotes/responses'
import { Base64String } from '@standardnotes/sncrypto-common'
export interface SessionsClientInterface {
createDemoShareToken(): Promise<Base64String | ClientDisplayableError>
populateSessionFromDemoShareToken(token: Base64String): Promise<void>
getUser(): User | undefined
}

View File

@@ -1,4 +1,3 @@
export * from './SessionManager' export * from './SessionManager'
export * from './Sessions' export * from './Sessions'
export * from './SessionsClientInterface'
export * from './ShareToken' export * from './ShareToken'

View File

@@ -1,6 +1,6 @@
import { SettingsList } from './SettingsList' import { SettingsList } from './SettingsList'
import { SettingName, SensitiveSettingName, SubscriptionSettingName } from '@standardnotes/settings' import { SettingName, SensitiveSettingName, SubscriptionSettingName } from '@standardnotes/settings'
import * as messages from '../Api/Messages' import { API_MESSAGE_INVALID_SESSION } from '@standardnotes/services'
import { StatusCode, User } from '@standardnotes/responses' import { StatusCode, User } from '@standardnotes/responses'
import { SettingsServerInterface } from './SettingsServerInterface' import { SettingsServerInterface } from './SettingsServerInterface'
@@ -25,7 +25,7 @@ export class SettingsGateway {
private get userUuid() { private get userUuid() {
const user = this.getUser() const user = this.getUser()
if (user == undefined || user.uuid == undefined) { if (user == undefined || user.uuid == undefined) {
throw new Error(messages.API_MESSAGE_INVALID_SESSION) throw new Error(API_MESSAGE_INVALID_SESSION)
} }
return user.uuid return user.uuid
} }

View File

@@ -1,5 +0,0 @@
import { HttpResponse, MinimalHttpResponse } from '@standardnotes/responses'
export interface UserServerInterface {
deleteAccount(userUuid: string): Promise<HttpResponse | MinimalHttpResponse>
}

View File

@@ -1,2 +0,0 @@
export * from './UserServerInterface'
export * from './UserService'

View File

@@ -19,4 +19,3 @@ export * from './Settings'
export * from './Singleton/SingletonManager' export * from './Singleton/SingletonManager'
export * from './Storage/DiskStorageService' export * from './Storage/DiskStorageService'
export * from './Sync' export * from './Sync'
export * from './User'

View File

@@ -1,10 +1,8 @@
import { ConfirmStrings } from './Confirm' import { ConfirmStrings } from './Confirm'
import { InfoStrings } from './Info'
import { InputStrings } from './Input' import { InputStrings } from './Input'
import { NetworkStrings } from './Network' import { NetworkStrings } from './Network'
export const Strings = { export const Strings = {
Info: InfoStrings,
Network: NetworkStrings, Network: NetworkStrings,
Confirm: ConfirmStrings, Confirm: ConfirmStrings,
Input: InputStrings, Input: InputStrings,

View File

@@ -68,7 +68,7 @@
}, },
"dependencies": { "dependencies": {
"@standardnotes/api": "workspace:*", "@standardnotes/api": "workspace:*",
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/domain-events": "^2.39.0", "@standardnotes/domain-events": "^2.39.0",
"@standardnotes/encryption": "workspace:*", "@standardnotes/encryption": "workspace:*",
"@standardnotes/features": "workspace:*", "@standardnotes/features": "workspace:*",

View File

@@ -23,7 +23,7 @@
"test": "jest spec" "test": "jest spec"
}, },
"dependencies": { "dependencies": {
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"@standardnotes/features": "workspace:^", "@standardnotes/features": "workspace:^",
"@standardnotes/filepicker": "workspace:^", "@standardnotes/filepicker": "workspace:^",
"@standardnotes/services": "workspace:^", "@standardnotes/services": "workspace:^",

View File

@@ -25,7 +25,7 @@
"test": "jest spec" "test": "jest spec"
}, },
"dependencies": { "dependencies": {
"@standardnotes/common": "^1.39.0", "@standardnotes/common": "^1.43.0",
"dompurify": "^2.3.8", "dompurify": "^2.3.8",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13"

View File

@@ -6354,7 +6354,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/api@workspace:packages/api" resolution: "@standardnotes/api@workspace:packages/api"
dependencies: dependencies:
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/encryption": "workspace:*" "@standardnotes/encryption": "workspace:*"
"@standardnotes/models": "workspace:*" "@standardnotes/models": "workspace:*"
"@standardnotes/responses": "workspace:*" "@standardnotes/responses": "workspace:*"
@@ -6521,7 +6521,7 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@standardnotes/common@npm:1.40.0, @standardnotes/common@npm:^1.23.1, @standardnotes/common@npm:^1.39.0": "@standardnotes/common@npm:1.40.0, @standardnotes/common@npm:^1.23.1":
version: 1.40.0 version: 1.40.0
resolution: "@standardnotes/common@npm:1.40.0" resolution: "@standardnotes/common@npm:1.40.0"
dependencies: dependencies:
@@ -6530,6 +6530,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@standardnotes/common@npm:^1.43.0":
version: 1.43.0
resolution: "@standardnotes/common@npm:1.43.0"
dependencies:
reflect-metadata: ^0.1.13
checksum: 59300594418a5cb9d4b811240c23007bb927df6f620cb37460a978d82b1b8baf7107e4a3557110c032636ab02f7e61669613d35bdcac2bcb0e8f0e66b8a16f8d
languageName: node
linkType: hard
"@standardnotes/component-relay@npm:2.2.0": "@standardnotes/component-relay@npm:2.2.0":
version: 2.2.0 version: 2.2.0
resolution: "@standardnotes/component-relay@npm:2.2.0" resolution: "@standardnotes/component-relay@npm:2.2.0"
@@ -6713,7 +6722,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/encryption@workspace:packages/encryption" resolution: "@standardnotes/encryption@workspace:packages/encryption"
dependencies: dependencies:
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/config": 2.4.3 "@standardnotes/config": 2.4.3
"@standardnotes/models": "workspace:*" "@standardnotes/models": "workspace:*"
"@standardnotes/responses": "workspace:*" "@standardnotes/responses": "workspace:*"
@@ -6755,7 +6764,7 @@ __metadata:
resolution: "@standardnotes/features@workspace:packages/features" resolution: "@standardnotes/features@workspace:packages/features"
dependencies: dependencies:
"@standardnotes/auth": ^3.19.4 "@standardnotes/auth": ^3.19.4
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/security": ^1.2.0 "@standardnotes/security": ^1.2.0
"@types/jest": ^28.1.5 "@types/jest": ^28.1.5
"@typescript-eslint/eslint-plugin": ^5.30.0 "@typescript-eslint/eslint-plugin": ^5.30.0
@@ -6771,7 +6780,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/filepicker@workspace:packages/filepicker" resolution: "@standardnotes/filepicker@workspace:packages/filepicker"
dependencies: dependencies:
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/files": "workspace:*" "@standardnotes/files": "workspace:*"
"@standardnotes/utils": "workspace:*" "@standardnotes/utils": "workspace:*"
"@types/jest": ^28.1.5 "@types/jest": ^28.1.5
@@ -6790,7 +6799,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/files@workspace:packages/files" resolution: "@standardnotes/files@workspace:packages/files"
dependencies: dependencies:
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/encryption": "workspace:*" "@standardnotes/encryption": "workspace:*"
"@standardnotes/models": "workspace:*" "@standardnotes/models": "workspace:*"
"@standardnotes/responses": "workspace:*" "@standardnotes/responses": "workspace:*"
@@ -7119,7 +7128,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/models@workspace:packages/models" resolution: "@standardnotes/models@workspace:packages/models"
dependencies: dependencies:
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/features": "workspace:*" "@standardnotes/features": "workspace:*"
"@standardnotes/responses": "workspace:*" "@standardnotes/responses": "workspace:*"
"@standardnotes/utils": "workspace:*" "@standardnotes/utils": "workspace:*"
@@ -7170,7 +7179,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/responses@workspace:packages/responses" resolution: "@standardnotes/responses@workspace:packages/responses"
dependencies: dependencies:
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/features": "workspace:*" "@standardnotes/features": "workspace:*"
"@standardnotes/security": ^1.1.0 "@standardnotes/security": ^1.1.0
"@types/jest": ^28.1.5 "@types/jest": ^28.1.5
@@ -7228,12 +7237,13 @@ __metadata:
dependencies: dependencies:
"@standardnotes/api": "workspace:^" "@standardnotes/api": "workspace:^"
"@standardnotes/auth": ^3.19.4 "@standardnotes/auth": ^3.19.4
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/encryption": "workspace:^" "@standardnotes/encryption": "workspace:^"
"@standardnotes/files": "workspace:^" "@standardnotes/files": "workspace:^"
"@standardnotes/models": "workspace:^" "@standardnotes/models": "workspace:^"
"@standardnotes/responses": "workspace:*" "@standardnotes/responses": "workspace:*"
"@standardnotes/security": ^1.2.0 "@standardnotes/security": ^1.2.0
"@standardnotes/sncrypto-common": "workspace:^"
"@standardnotes/utils": "workspace:*" "@standardnotes/utils": "workspace:*"
"@types/jest": ^28.1.5 "@types/jest": ^28.1.5
"@typescript-eslint/eslint-plugin": ^5.30.0 "@typescript-eslint/eslint-plugin": ^5.30.0
@@ -7293,7 +7303,7 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"@standardnotes/sncrypto-common@workspace:*, @standardnotes/sncrypto-common@workspace:packages/sncrypto-common": "@standardnotes/sncrypto-common@workspace:*, @standardnotes/sncrypto-common@workspace:^, @standardnotes/sncrypto-common@workspace:packages/sncrypto-common":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/sncrypto-common@workspace:packages/sncrypto-common" resolution: "@standardnotes/sncrypto-common@workspace:packages/sncrypto-common"
dependencies: dependencies:
@@ -7343,7 +7353,7 @@ __metadata:
"@babel/core": "*" "@babel/core": "*"
"@babel/preset-env": "*" "@babel/preset-env": "*"
"@standardnotes/api": "workspace:*" "@standardnotes/api": "workspace:*"
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/domain-events": ^2.39.0 "@standardnotes/domain-events": ^2.39.0
"@standardnotes/encryption": "workspace:*" "@standardnotes/encryption": "workspace:*"
"@standardnotes/features": "workspace:*" "@standardnotes/features": "workspace:*"
@@ -7478,7 +7488,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/ui-services@workspace:packages/ui-services" resolution: "@standardnotes/ui-services@workspace:packages/ui-services"
dependencies: dependencies:
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@standardnotes/features": "workspace:^" "@standardnotes/features": "workspace:^"
"@standardnotes/filepicker": "workspace:^" "@standardnotes/filepicker": "workspace:^"
"@standardnotes/services": "workspace:^" "@standardnotes/services": "workspace:^"
@@ -7499,7 +7509,7 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@standardnotes/utils@workspace:packages/utils" resolution: "@standardnotes/utils@workspace:packages/utils"
dependencies: dependencies:
"@standardnotes/common": ^1.39.0 "@standardnotes/common": ^1.43.0
"@types/dompurify": ^2.3.3 "@types/dompurify": ^2.3.3
"@types/jest": ^28.1.5 "@types/jest": ^28.1.5
"@types/jsdom": ^16.2.14 "@types/jsdom": ^16.2.14