mirror of
https://github.com/standardnotes/app
synced 2026-09-15 15:46:05 -04:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8de6023848 | ||
|
|
01ba715eba | ||
|
|
3733707bf1 | ||
|
|
cbbe913cd6 | ||
|
|
c13dd883a4 | ||
|
|
1377846f3f | ||
|
|
f9ee197f04 | ||
|
|
c944eb9365 | ||
|
|
4609053bd0 | ||
|
|
1c10214104 | ||
|
|
1602d8157f | ||
|
|
ed1d583476 | ||
|
|
05aff2776b | ||
|
|
4b6662d62d | ||
|
|
ac0ad49af0 | ||
|
|
09b994f8f9 | ||
|
|
6c26b96cdc |
@@ -48,16 +48,6 @@ jobs:
|
||||
uses: martinbeentjes/npm-get-version-action@main
|
||||
with:
|
||||
path: packages/mobile
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
token: ${{ secrets.CI_PAT_TOKEN }}
|
||||
tag_name: "@standardnotes/mobile@${{ steps.package-version.outputs.current-version}}"
|
||||
prerelease: true
|
||||
draft: true
|
||||
name: "Mobile Dev ${{ steps.package-version.outputs.current-version }}"
|
||||
files: |
|
||||
packages/mobile/android/app/build/outputs/apk/dev/release/app-dev-release.apk
|
||||
ios:
|
||||
defaults:
|
||||
run:
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.11.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add workspaces api ([#1765](https://github.com/standardnotes/app/issues/1765)) ([01ba715](https://github.com/standardnotes/app/commit/01ba715eba987a7da1ee062fec0b3593a7a453ed))
|
||||
|
||||
# [1.10.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
## [1.9.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
## [1.9.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api
|
||||
|
||||
@@ -10,10 +10,10 @@ module.exports = {
|
||||
},
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 20,
|
||||
functions: 66,
|
||||
lines: 63,
|
||||
statements: 63
|
||||
branches: 22,
|
||||
functions: 69,
|
||||
lines: 67,
|
||||
statements: 67
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api",
|
||||
"version": "1.9.1",
|
||||
"version": "1.11.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { RootKeyParamsInterface } from '@standardnotes/models'
|
||||
|
||||
import { ErrorMessage } from '../../Error/ErrorMessage'
|
||||
import { ApiCallError } from '../../Error/ApiCallError'
|
||||
import { UserRegistrationResponse } from '../../Response/User/UserRegistrationResponse'
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export enum WorkspaceApiOperations {
|
||||
Creating,
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
|
||||
|
||||
import { WorkspaceApiOperations } from './WorkspaceApiOperations'
|
||||
import { WorkspaceApiService } from './WorkspaceApiService'
|
||||
|
||||
describe('WorkspaceApiService', () => {
|
||||
let workspaceServer: WorkspaceServerInterface
|
||||
|
||||
const createService = () => new WorkspaceApiService(workspaceServer)
|
||||
|
||||
beforeEach(() => {
|
||||
workspaceServer = {} as jest.Mocked<WorkspaceServerInterface>
|
||||
workspaceServer.createWorkspace = jest.fn().mockReturnValue({
|
||||
data: { uuid: '1-2-3' },
|
||||
} as jest.Mocked<WorkspaceCreationResponse>)
|
||||
})
|
||||
|
||||
it('should create a workspace', async () => {
|
||||
const response = await createService().createWorkspace({
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
})
|
||||
expect(workspaceServer.createWorkspace).toHaveBeenCalledWith({
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a workspace if it is already creating', async () => {
|
||||
const service = createService()
|
||||
Object.defineProperty(service, 'operationsInProgress', {
|
||||
get: () => new Map([[WorkspaceApiOperations.Creating, true]]),
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await service.createWorkspace({
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should not create a workspace if the server fails', async () => {
|
||||
workspaceServer.createWorkspace = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
let error = null
|
||||
try {
|
||||
await createService().createWorkspace({
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
})
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error).not.toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import { ErrorMessage } from '../../Error/ErrorMessage'
|
||||
import { ApiCallError } from '../../Error/ApiCallError'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
|
||||
|
||||
import { WorkspaceApiServiceInterface } from './WorkspaceApiServiceInterface'
|
||||
import { WorkspaceApiOperations } from './WorkspaceApiOperations'
|
||||
|
||||
export class WorkspaceApiService implements WorkspaceApiServiceInterface {
|
||||
private operationsInProgress: Map<WorkspaceApiOperations, boolean>
|
||||
|
||||
constructor(private workspaceServer: WorkspaceServerInterface) {
|
||||
this.operationsInProgress = new Map()
|
||||
}
|
||||
|
||||
async createWorkspace(dto: {
|
||||
encryptedWorkspaceKey: string
|
||||
encryptedPrivateKey: string
|
||||
publicKey: string
|
||||
workspaceName?: string
|
||||
}): Promise<WorkspaceCreationResponse> {
|
||||
if (this.operationsInProgress.get(WorkspaceApiOperations.Creating)) {
|
||||
throw new ApiCallError(ErrorMessage.GenericInProgress)
|
||||
}
|
||||
|
||||
this.operationsInProgress.set(WorkspaceApiOperations.Creating, true)
|
||||
|
||||
try {
|
||||
const response = await this.workspaceServer.createWorkspace({
|
||||
encryptedPrivateKey: dto.encryptedPrivateKey,
|
||||
encryptedWorkspaceKey: dto.encryptedWorkspaceKey,
|
||||
publicKey: dto.publicKey,
|
||||
workspaceName: dto.workspaceName,
|
||||
})
|
||||
|
||||
this.operationsInProgress.set(WorkspaceApiOperations.Creating, false)
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { WorkspaceCreationResponse } from '../../Response'
|
||||
|
||||
export interface WorkspaceApiServiceInterface {
|
||||
createWorkspace(dto: {
|
||||
encryptedWorkspaceKey: string
|
||||
encryptedPrivateKey: string
|
||||
publicKey: string
|
||||
workspaceName?: string
|
||||
}): Promise<WorkspaceCreationResponse>
|
||||
}
|
||||
@@ -5,3 +5,5 @@ export * from './User/UserApiService'
|
||||
export * from './User/UserApiServiceInterface'
|
||||
export * from './WebSocket/WebSocketApiService'
|
||||
export * from './WebSocket/WebSocketApiServiceInterface'
|
||||
export * from './Workspace/WorkspaceApiService'
|
||||
export * from './Workspace/WorkspaceApiServiceInterface'
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export type WorkspaceCreationRequestParams = {
|
||||
encryptedWorkspaceKey: string
|
||||
encryptedPrivateKey: string
|
||||
publicKey: string
|
||||
workspaceName?: string
|
||||
[additionalParam: string]: unknown
|
||||
}
|
||||
@@ -6,3 +6,4 @@ export * from './Subscription/SubscriptionInviteListRequestParams'
|
||||
export * from './Subscription/SubscriptionInviteRequestParams'
|
||||
export * from './User/UserRegistrationRequestParams'
|
||||
export * from './WebSocket/WebSocketConnectionTokenRequestParams'
|
||||
export * from './Workspace/WorkspaceCreationRequestParams'
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Either } from '@standardnotes/common'
|
||||
|
||||
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
|
||||
import { HttpResponse } from '../../Http/HttpResponse'
|
||||
import { WorkspaceCreationResponseBody } from './WorkspaceCreationResponseBody'
|
||||
|
||||
export interface WorkspaceCreationResponse extends HttpResponse {
|
||||
data: Either<WorkspaceCreationResponseBody, HttpErrorResponseBody>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type WorkspaceCreationResponseBody = {
|
||||
uuid: string
|
||||
}
|
||||
@@ -12,3 +12,5 @@ export * from './User/UserRegistrationResponse'
|
||||
export * from './User/UserRegistrationResponseBody'
|
||||
export * from './WebSocket/WebSocketConnectionTokenResponse'
|
||||
export * from './WebSocket/WebSocketConnectionTokenResponseBody'
|
||||
export * from './Workspace/WorkspaceCreationResponse'
|
||||
export * from './Workspace/WorkspaceCreationResponseBody'
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
const WorkspacePaths = {
|
||||
createWorkspace: '/v1/workspaces',
|
||||
}
|
||||
|
||||
export const Paths = {
|
||||
v1: {
|
||||
...WorkspacePaths,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { HttpServiceInterface } from '../../Http'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
|
||||
import { WorkspaceServer } from './WorkspaceServer'
|
||||
|
||||
describe('WorkspaceServer', () => {
|
||||
let httpService: HttpServiceInterface
|
||||
|
||||
const createServer = () => new WorkspaceServer(httpService)
|
||||
|
||||
beforeEach(() => {
|
||||
httpService = {} as jest.Mocked<HttpServiceInterface>
|
||||
httpService.post = jest.fn().mockReturnValue({
|
||||
data: { uuid: '1-2-3' },
|
||||
} as jest.Mocked<WorkspaceCreationResponse>)
|
||||
})
|
||||
|
||||
it('should create a workspace', async () => {
|
||||
const response = await createServer().createWorkspace({
|
||||
encryptedPrivateKey: 'foo',
|
||||
encryptedWorkspaceKey: 'bar',
|
||||
publicKey: 'buzz',
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
data: {
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,16 @@
|
||||
import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
|
||||
import { WorkspaceCreationRequestParams } from '../../Request/Workspace/WorkspaceCreationRequestParams'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
|
||||
import { Paths } from './Paths'
|
||||
import { WorkspaceServerInterface } from './WorkspaceServerInterface'
|
||||
|
||||
export class WorkspaceServer implements WorkspaceServerInterface {
|
||||
constructor(private httpService: HttpServiceInterface) {}
|
||||
|
||||
async createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse> {
|
||||
const response = await this.httpService.post(Paths.v1.createWorkspace, params)
|
||||
|
||||
return response as WorkspaceCreationResponse
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { WorkspaceCreationRequestParams } from '../../Request/Workspace/WorkspaceCreationRequestParams'
|
||||
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
|
||||
|
||||
export interface WorkspaceServerInterface {
|
||||
createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse>
|
||||
}
|
||||
@@ -4,3 +4,5 @@ export * from './User/UserServer'
|
||||
export * from './User/UserServerInterface'
|
||||
export * from './WebSocket/WebSocketServer'
|
||||
export * from './WebSocket/WebSocketServerInterface'
|
||||
export * from './Workspace/WorkspaceServer'
|
||||
export * from './Workspace/WorkspaceServerInterface'
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.7.16](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
## [2.7.15](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/components-meta
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+6
-6
@@ -70,9 +70,9 @@
|
||||
"binary": "b621bfd81829a205f2f02e85d446e277ad20e367b92676ec20b3ee59b1bfab3f"
|
||||
},
|
||||
"org.standardnotes.markdown-visual-editor": {
|
||||
"version": "1.1.2",
|
||||
"base64": "2645d31c51fa6a198c14a32737ee4045dcbbecb8bdaa1432ca4686cab78834d2",
|
||||
"binary": "c4479f8768f5f6cda41850212a8e03927990adfa33ebd36e29ee6de2335e8e46"
|
||||
"version": "1.1.3",
|
||||
"base64": "8bb35d66b0ef2bee2243dbdf514c0bf50ef706c034395cb9d50119f017fef9aa",
|
||||
"binary": "1738193ab2a57cf280c29c9ca1e4caf49179009c7382048577d2b34eaf96d5e9"
|
||||
},
|
||||
"org.standardnotes.simple-task-editor": {
|
||||
"version": "1.4.4",
|
||||
@@ -80,9 +80,9 @@
|
||||
"binary": "9df983a144b9686f4f58b03bf78bf1f84f53aea60251621a75586722bf576475"
|
||||
},
|
||||
"org.standardnotes.token-vault": {
|
||||
"version": "2.2.4",
|
||||
"base64": "ac07bd989bfb1100aeaacf6687b492b4aac754645da5a132ecd879dc64786522",
|
||||
"binary": "8f74bacd0dd3a0a6e04bcce9c54694bb2f429b470a3023e96e0a750a271184ae"
|
||||
"version": "2.2.5",
|
||||
"base64": "ca103f730737d3a3046001666b626139692c08ab4dd5748b0290fdcb1b1eb04a",
|
||||
"binary": "52f419cd60da00a2cad13fd357b50993c7694160fcca1c1bdf1859e3bdca3a2b"
|
||||
},
|
||||
"org.standardnotes.standard-sheets": {
|
||||
"version": "1.5.1",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/components-meta",
|
||||
"version": "2.7.15",
|
||||
"version": "2.7.16",
|
||||
"private": true,
|
||||
"author": "Standard Notes.",
|
||||
"main": "dist",
|
||||
|
||||
+4
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/markdown-visual
|
||||
|
||||
## [1.1.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-07-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/markdown-visual",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"author": "Johnny Almonte <[email protected]>",
|
||||
"description": "A lightweight WYSIWYG markdown editor for Standard Notes, derived from Milkdown.",
|
||||
"keywords": [
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.2.5](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/authenticator
|
||||
|
||||
## [2.2.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-08-22)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/authenticator",
|
||||
"version": "2.2.4",
|
||||
"version": "2.2.5",
|
||||
"main": "dist/dist.js",
|
||||
"author": "Standard Notes",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.23.196](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.195](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.194](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.193](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.192](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
## [3.23.191](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/desktop
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@standardnotes/desktop",
|
||||
"main": "./app/dist/index.js",
|
||||
"version": "3.23.191",
|
||||
"version": "3.23.196",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.16.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
## [1.15.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
## [1.15.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/encryption
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/encryption",
|
||||
"version": "1.15.10",
|
||||
"version": "1.16.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -44,3 +44,8 @@ export enum V004Algorithm {
|
||||
EncryptionKeyLength = 256,
|
||||
EncryptionNonceLength = 192,
|
||||
}
|
||||
|
||||
export enum V005Algorithm {
|
||||
AsymmetricEncryptionNonceLength = 192,
|
||||
SymmetricEncryptionNonceLength = 192,
|
||||
}
|
||||
|
||||
@@ -254,21 +254,21 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
||||
encrypted: EncryptedParameters,
|
||||
key: ItemsKeyInterface | SNRootKey,
|
||||
): DecryptedParameters<C> | ErrorDecryptingParameters {
|
||||
const itemKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
||||
const authenticatedData = this.stringToAuthenticatedData(itemKeyComponents.authenticatedData, {
|
||||
const contentKeyComponents = this.deconstructEncryptedPayloadString(encrypted.enc_item_key)
|
||||
const authenticatedData = this.stringToAuthenticatedData(contentKeyComponents.authenticatedData, {
|
||||
u: encrypted.uuid,
|
||||
v: encrypted.version,
|
||||
})
|
||||
|
||||
const useAuthenticatedString = this.authenticatedDataToString(authenticatedData)
|
||||
const itemKey = this.decryptString004(
|
||||
itemKeyComponents.ciphertext,
|
||||
const contentKey = this.decryptString004(
|
||||
contentKeyComponents.ciphertext,
|
||||
key.itemsKey,
|
||||
itemKeyComponents.nonce,
|
||||
contentKeyComponents.nonce,
|
||||
useAuthenticatedString,
|
||||
)
|
||||
|
||||
if (!itemKey) {
|
||||
if (!contentKey) {
|
||||
console.error('Error decrypting itemKey parameters', encrypted)
|
||||
return {
|
||||
uuid: encrypted.uuid,
|
||||
@@ -279,10 +279,11 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
||||
const contentComponents = this.deconstructEncryptedPayloadString(encrypted.content)
|
||||
const content = this.decryptString004(
|
||||
contentComponents.ciphertext,
|
||||
itemKey,
|
||||
contentKey,
|
||||
contentComponents.nonce,
|
||||
useAuthenticatedString,
|
||||
)
|
||||
|
||||
if (!content) {
|
||||
return {
|
||||
uuid: encrypted.uuid,
|
||||
@@ -305,6 +306,7 @@ export class SNProtocolOperator004 implements SynchronousOperator {
|
||||
V004Algorithm.ArgonMemLimit,
|
||||
V004Algorithm.ArgonOutputKeyBytes,
|
||||
)
|
||||
|
||||
const partitions = Utils.splitString(derivedKey, 2)
|
||||
const masterKey = partitions[0]
|
||||
const serverPassword = partitions[1]
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { ProtocolOperator005 } from './Operator005'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
|
||||
describe('operator 005', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
let operator: ProtocolOperator005
|
||||
|
||||
beforeEach(() => {
|
||||
crypto = {} as jest.Mocked<PureCryptoInterface>
|
||||
crypto.generateRandomKey = jest.fn().mockImplementation(() => {
|
||||
return 'random-string'
|
||||
})
|
||||
crypto.xchacha20Encrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return `<e>${text}<e>`
|
||||
})
|
||||
crypto.xchacha20Decrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return text.split('<e>')[1]
|
||||
})
|
||||
crypto.sodiumCryptoBoxGenerateKeypair = jest.fn().mockImplementation(() => {
|
||||
return { privateKey: 'private-key', publicKey: 'public-key', keyType: 'x25519' }
|
||||
})
|
||||
crypto.sodiumCryptoBoxEasyEncrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return `<e>${text}<e>`
|
||||
})
|
||||
crypto.sodiumCryptoBoxEasyDecrypt = jest.fn().mockImplementation((text: string) => {
|
||||
return text.split('<e>')[1]
|
||||
})
|
||||
|
||||
operator = new ProtocolOperator005(crypto)
|
||||
})
|
||||
|
||||
it('should generateKeyPair', () => {
|
||||
const result = operator.generateKeyPair()
|
||||
|
||||
expect(result).toEqual({ privateKey: 'private-key', publicKey: 'public-key', keyType: 'x25519' })
|
||||
})
|
||||
|
||||
it('should asymmetricEncryptKey', () => {
|
||||
const senderKeypair = operator.generateKeyPair()
|
||||
const recipientKeypair = operator.generateKeyPair()
|
||||
|
||||
const plaintext = 'foo'
|
||||
|
||||
const result = operator.asymmetricEncryptKey(plaintext, senderKeypair.privateKey, recipientKeypair.publicKey)
|
||||
|
||||
expect(result).toEqual(`${'005_KeyAsym'}:random-string:<e>foo<e>`)
|
||||
})
|
||||
|
||||
it('should asymmetricDecryptKey', () => {
|
||||
const senderKeypair = operator.generateKeyPair()
|
||||
const recipientKeypair = operator.generateKeyPair()
|
||||
const plaintext = 'foo'
|
||||
const ciphertext = operator.asymmetricEncryptKey(plaintext, senderKeypair.privateKey, recipientKeypair.publicKey)
|
||||
const decrypted = operator.asymmetricDecryptKey(ciphertext, senderKeypair.publicKey, recipientKeypair.privateKey)
|
||||
|
||||
expect(decrypted).toEqual('foo')
|
||||
})
|
||||
|
||||
it('should symmetricEncryptPrivateKey', () => {
|
||||
const keypair = operator.generateKeyPair()
|
||||
const symmetricKey = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
const encryptedKey = operator.symmetricEncryptPrivateKey(keypair.privateKey, symmetricKey)
|
||||
|
||||
expect(encryptedKey).toEqual(`${'005_KeySym'}:random-string:<e>${keypair.privateKey}<e>`)
|
||||
})
|
||||
|
||||
it('should symmetricDecryptPrivateKey', () => {
|
||||
const keypair = operator.generateKeyPair()
|
||||
const symmetricKey = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
||||
const encryptedKey = operator.symmetricEncryptPrivateKey(keypair.privateKey, symmetricKey)
|
||||
const decryptedKey = operator.symmetricDecryptPrivateKey(encryptedKey, symmetricKey)
|
||||
|
||||
expect(decryptedKey).toEqual(keypair.privateKey)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,80 @@
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { Base64String, HexString, PkcKeyPair, Utf8String } from '@standardnotes/sncrypto-common'
|
||||
import { V005Algorithm } from '../../Algorithm'
|
||||
import { SNProtocolOperator004 } from '../004/Operator004'
|
||||
|
||||
const VersionString = '005'
|
||||
const SymmetricCiphertextPrefix = `${VersionString}_KeySym`
|
||||
const AsymmetricCiphertextPrefix = `${VersionString}_KeyAsym`
|
||||
|
||||
export type AsymmetricallyEncryptedKey = Base64String
|
||||
export type SymmetricallyEncryptedPrivateKey = Base64String
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
* @unreleased
|
||||
*/
|
||||
export class ProtocolOperator005 extends SNProtocolOperator004 {
|
||||
public override getEncryptionDisplayName(): string {
|
||||
return 'XChaCha20-Poly1305'
|
||||
}
|
||||
|
||||
override get version(): ProtocolVersion {
|
||||
return VersionString as ProtocolVersion
|
||||
}
|
||||
|
||||
generateKeyPair(): PkcKeyPair {
|
||||
return this.crypto.sodiumCryptoBoxGenerateKeypair()
|
||||
}
|
||||
|
||||
asymmetricEncryptKey(
|
||||
keyToEncrypt: HexString,
|
||||
senderSecretKey: HexString,
|
||||
recipientPublicKey: HexString,
|
||||
): AsymmetricallyEncryptedKey {
|
||||
const nonce = this.crypto.generateRandomKey(V005Algorithm.AsymmetricEncryptionNonceLength)
|
||||
|
||||
const ciphertext = this.crypto.sodiumCryptoBoxEasyEncrypt(keyToEncrypt, nonce, senderSecretKey, recipientPublicKey)
|
||||
|
||||
return [AsymmetricCiphertextPrefix, nonce, ciphertext].join(':')
|
||||
}
|
||||
|
||||
asymmetricDecryptKey(
|
||||
keyToDecrypt: AsymmetricallyEncryptedKey,
|
||||
senderPublicKey: HexString,
|
||||
recipientSecretKey: HexString,
|
||||
): Utf8String {
|
||||
const components = keyToDecrypt.split(':')
|
||||
|
||||
const nonce = components[1]
|
||||
|
||||
return this.crypto.sodiumCryptoBoxEasyDecrypt(keyToDecrypt, nonce, senderPublicKey, recipientSecretKey)
|
||||
}
|
||||
|
||||
symmetricEncryptPrivateKey(privateKey: HexString, symmetricKey: HexString): SymmetricallyEncryptedPrivateKey {
|
||||
if (symmetricKey.length !== 64) {
|
||||
throw new Error('Symmetric key length must be 256 bits')
|
||||
}
|
||||
|
||||
const nonce = this.crypto.generateRandomKey(V005Algorithm.SymmetricEncryptionNonceLength)
|
||||
|
||||
const encryptedKey = this.crypto.xchacha20Encrypt(privateKey, nonce, symmetricKey)
|
||||
|
||||
return [SymmetricCiphertextPrefix, nonce, encryptedKey].join(':')
|
||||
}
|
||||
|
||||
symmetricDecryptPrivateKey(
|
||||
encryptedPrivateKey: SymmetricallyEncryptedPrivateKey,
|
||||
symmetricKey: HexString,
|
||||
): HexString | null {
|
||||
if (symmetricKey.length !== 64) {
|
||||
throw new Error('Symmetric key length must be 256 bits')
|
||||
}
|
||||
|
||||
const components = encryptedPrivateKey.split(':')
|
||||
|
||||
const nonce = components[1]
|
||||
|
||||
return this.crypto.xchacha20Decrypt(encryptedPrivateKey, nonce, symmetricKey)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -11,7 +11,7 @@ import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
|
||||
import { KeyedDecryptionSplit } from '../../Split/KeyedDecryptionSplit'
|
||||
import { KeyedEncryptionSplit } from '../../Split/KeyedEncryptionSplit'
|
||||
|
||||
export interface EncryptionProvider {
|
||||
export interface EncryptionProviderInterface {
|
||||
encryptSplitSingle(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface>
|
||||
|
||||
encryptSplit(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface[]>
|
||||
@@ -14,11 +14,12 @@ export * from './Operator/001/Operator001'
|
||||
export * from './Operator/002/Operator002'
|
||||
export * from './Operator/003/Operator003'
|
||||
export * from './Operator/004/Operator004'
|
||||
export * from './Operator/005/Operator005'
|
||||
export * from './Operator/Functions'
|
||||
export * from './Operator/Operator'
|
||||
export * from './Operator/OperatorManager'
|
||||
export * from './Operator/OperatorWrapper'
|
||||
export * from './Service/Encryption/EncryptionProvider'
|
||||
export * from './Service/Encryption/EncryptionProviderInterface'
|
||||
export * from './Service/Functions'
|
||||
export * from './Service/RootKey/KeyMode'
|
||||
export * from './Service/RootKey/RootKeyServiceEvent'
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.23.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
## [1.23.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/filepicker
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/filepicker",
|
||||
"version": "1.23.10",
|
||||
"version": "1.23.12",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.10.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
## [1.10.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files",
|
||||
"version": "1.10.10",
|
||||
"version": "1.10.12",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,36 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.40.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
# [3.40.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
## [3.39.12](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.39.11](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add android platform check for status bar color sync ([1602d81](https://github.com/standardnotes/app/commit/1602d8157f9e61c6ad842bcce28f3aaf6a8f7154))
|
||||
|
||||
## [3.39.10](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/mobile
|
||||
|
||||
## [3.39.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** passcode timing options ([#1744](https://github.com/standardnotes/app/issues/1744)) ([6c26b96](https://github.com/standardnotes/app/commit/6c26b96cdce0d69a4a459104709880850a401a92))
|
||||
|
||||
## [3.39.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/mobile",
|
||||
"version": "3.39.8",
|
||||
"version": "3.40.1",
|
||||
"author": "Standard Notes.",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -483,7 +483,7 @@ export class ApplicationState extends ApplicationService {
|
||||
return
|
||||
}
|
||||
|
||||
const hasBiometrics = this.application.hasBiometrics()
|
||||
const hasBiometrics = this.application.protections.hasBiometricsEnabled()
|
||||
const hasPasscode = this.application.hasPasscode()
|
||||
const passcodeLockImmediately = hasPasscode && this.passcodeTiming === MobileUnlockTiming.Immediately
|
||||
const biometricsLockImmediately =
|
||||
@@ -571,19 +571,19 @@ export class ApplicationState extends ApplicationService {
|
||||
}
|
||||
|
||||
private async getScreenshotPrivacyEnabled(): Promise<boolean> {
|
||||
return this.application.getMobileScreenshotPrivacyEnabled()
|
||||
return this.application.protections.getMobileScreenshotPrivacyEnabled()
|
||||
}
|
||||
|
||||
private async getPasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.application.getMobilePasscodeTiming()
|
||||
return this.application.protections.getMobilePasscodeTiming()
|
||||
}
|
||||
|
||||
private async getBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.application.getMobileBiometricsTiming()
|
||||
return this.application.protections.getMobileBiometricsTiming()
|
||||
}
|
||||
|
||||
public async setScreenshotPrivacyEnabled(enabled: boolean) {
|
||||
await this.application.setMobileScreenshotPrivacyEnabled(enabled)
|
||||
await this.application.protections.setMobileScreenshotPrivacyEnabled(enabled)
|
||||
this.screenshotPrivacyEnabled = enabled
|
||||
await (this.application.deviceInterface as MobileDevice).setAndroidScreenshotPrivacy(enabled)
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ export class MobileDevice implements MobileDeviceInterface {
|
||||
}
|
||||
|
||||
reloadStatusBarStyle(animated = true) {
|
||||
if (this.statusBarBgColor) {
|
||||
if (this.statusBarBgColor && Platform.OS === 'android') {
|
||||
StatusBar.setBackgroundColor(this.statusBarBgColor, animated)
|
||||
}
|
||||
StatusBar.setBarStyle(this.isDarkMode ? 'light-content' : 'dark-content', animated)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
Base64String,
|
||||
HexString,
|
||||
PkcKeyPair,
|
||||
PureCryptoInterface,
|
||||
SodiumConstant,
|
||||
StreamDecryptorResult,
|
||||
@@ -129,6 +130,28 @@ export class SNReactNativeCrypto implements PureCryptoInterface {
|
||||
}
|
||||
}
|
||||
|
||||
public sodiumCryptoBoxEasyEncrypt(
|
||||
_message: Utf8String,
|
||||
_nonce: HexString,
|
||||
_senderSecretKey: HexString,
|
||||
_recipientPublicKey: HexString,
|
||||
): Base64String {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
public sodiumCryptoBoxEasyDecrypt(
|
||||
_ciphertext: Base64String,
|
||||
_nonce: HexString,
|
||||
_senderPublicKey: HexString,
|
||||
_recipientSecretKey: HexString,
|
||||
): Utf8String {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
public sodiumCryptoBoxGenerateKeypair(): PkcKeyPair {
|
||||
throw new Error('Not implemented')
|
||||
}
|
||||
|
||||
public generateUUID() {
|
||||
const randomBuf = Sodium.randombytes_buf(16)
|
||||
const tempBuf = new Uint8Array(randomBuf.length / 2)
|
||||
|
||||
@@ -197,7 +197,7 @@ export const Authenticate = ({
|
||||
state: AuthenticationValueStateType.Pending,
|
||||
})
|
||||
|
||||
if (await application?.getMobileScreenshotPrivacyEnabled()) {
|
||||
if (application?.protections.getMobileScreenshotPrivacyEnabled()) {
|
||||
hide()
|
||||
}
|
||||
|
||||
|
||||
@@ -31,21 +31,23 @@ export const SecuritySection = (props: Props) => {
|
||||
const [hasBiometrics, setHasBiometrics] = useState(false)
|
||||
const [supportsBiometrics, setSupportsBiometrics] = useState(false)
|
||||
const [biometricsTimingOptions, setBiometricsTimingOptions] = useState(() =>
|
||||
application!.getBiometricsTimingOptions(),
|
||||
application!.protections.getMobileBiometricsTimingOptions(),
|
||||
)
|
||||
const [passcodeTimingOptions, setPasscodeTimingOptions] = useState(() =>
|
||||
application!.protections.getMobilePasscodeTimingOptions(),
|
||||
)
|
||||
const [passcodeTimingOptions, setPasscodeTimingOptions] = useState(() => application!.getPasscodeTimingOptions())
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
const getHasScreenshotPrivacy = async () => {
|
||||
const hasScreenshotPrivacyEnabled = application?.getMobileScreenshotPrivacyEnabled()
|
||||
const hasScreenshotPrivacyEnabled = application?.protections.getMobileScreenshotPrivacyEnabled()
|
||||
if (mounted) {
|
||||
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
||||
}
|
||||
}
|
||||
void getHasScreenshotPrivacy()
|
||||
const getHasBiometrics = async () => {
|
||||
const appHasBiometrics = application!.hasBiometrics()
|
||||
const appHasBiometrics = application!.protections.hasBiometricsEnabled()
|
||||
if (mounted) {
|
||||
setHasBiometrics(appHasBiometrics)
|
||||
}
|
||||
@@ -68,7 +70,7 @@ export const SecuritySection = (props: Props) => {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
if (props.hasPasscode) {
|
||||
setPasscodeTimingOptions(() => application!.getPasscodeTimingOptions())
|
||||
setPasscodeTimingOptions(() => application!.protections.getMobilePasscodeTimingOptions())
|
||||
}
|
||||
}, [application, props.hasPasscode]),
|
||||
)
|
||||
@@ -126,12 +128,12 @@ export const SecuritySection = (props: Props) => {
|
||||
|
||||
const setBiometricsTiming = async (timing: MobileUnlockTiming) => {
|
||||
await application?.getAppState().setBiometricsTiming(timing)
|
||||
setBiometricsTimingOptions(() => application!.getBiometricsTimingOptions())
|
||||
setBiometricsTimingOptions(() => application!.protections.getMobileBiometricsTimingOptions())
|
||||
}
|
||||
|
||||
const setPasscodeTiming = async (timing: MobileUnlockTiming) => {
|
||||
await application?.getAppState().setPasscodeTiming(timing)
|
||||
setPasscodeTimingOptions(() => application!.getPasscodeTimingOptions())
|
||||
setPasscodeTimingOptions(() => application!.protections.getMobilePasscodeTimingOptions())
|
||||
}
|
||||
|
||||
const onScreenshotPrivacyPress = async () => {
|
||||
@@ -153,14 +155,14 @@ export const SecuritySection = (props: Props) => {
|
||||
void disableAuthentication('biometrics')
|
||||
} else {
|
||||
setHasBiometrics(true)
|
||||
await application?.enableBiometrics()
|
||||
await application?.protections.enableBiometrics()
|
||||
await setBiometricsTiming(MobileUnlockTiming.OnQuit)
|
||||
props.updateProtectionsAvailable()
|
||||
}
|
||||
}
|
||||
|
||||
const disableBiometrics = useCallback(async () => {
|
||||
if (await application?.disableBiometrics()) {
|
||||
if (await application?.protections.disableBiometrics()) {
|
||||
setHasBiometrics(false)
|
||||
props.updateProtectionsAvailable()
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.24.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
# [1.23.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Features
|
||||
|
||||
* add free dark mode ([#1748](https://github.com/standardnotes/app/issues/1748)) ([09b994f](https://github.com/standardnotes/app/commit/09b994f8f938ae0536f42742f7a221df536c4c4a))
|
||||
|
||||
## [1.22.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/models
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/models",
|
||||
"version": "1.22.1",
|
||||
"version": "1.24.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -24,11 +24,11 @@ export class DecryptedItem<C extends ItemContent = ItemContent>
|
||||
|
||||
constructor(payload: DecryptedPayloadInterface<C>) {
|
||||
super(payload)
|
||||
this.conflictOf = payload.content.conflict_of
|
||||
|
||||
const userModVal = this.getAppDomainValueWithDefault(AppDataField.UserModifiedDate, this.serverUpdatedAt || 0)
|
||||
|
||||
this.userModifiedDate = new Date(userModVal as number | Date)
|
||||
|
||||
this.conflictOf = payload.content.conflict_of
|
||||
this.updatedAtString = dateToLocalizedString(this.userModifiedDate)
|
||||
this.protected = useBoolean(this.payload.content.protected, false)
|
||||
this.trashed = useBoolean(this.payload.content.trashed, false)
|
||||
|
||||
@@ -37,6 +37,7 @@ export enum PrefKey {
|
||||
NewNoteTitleFormat = 'newNoteTitleFormat',
|
||||
CustomNoteTitleFormat = 'customNoteTitleFormat',
|
||||
UpdateSavingStatusIndicator = 'updateSavingStatusIndicator',
|
||||
DarkMode = 'darkMode',
|
||||
}
|
||||
|
||||
export enum NewNoteTitleFormat {
|
||||
@@ -82,8 +83,8 @@ export type PrefValue = {
|
||||
[PrefKey.NotesHideTags]: boolean
|
||||
[PrefKey.NotesHideEditorIcon]: boolean
|
||||
[PrefKey.UseSystemColorScheme]: boolean
|
||||
[PrefKey.AutoLightThemeIdentifier]: FeatureIdentifier | 'Default'
|
||||
[PrefKey.AutoDarkThemeIdentifier]: FeatureIdentifier | 'Default'
|
||||
[PrefKey.AutoLightThemeIdentifier]: FeatureIdentifier | 'Default' | 'Dark'
|
||||
[PrefKey.AutoDarkThemeIdentifier]: FeatureIdentifier | 'Default' | 'Dark'
|
||||
[PrefKey.NoteAddToParentFolders]: boolean
|
||||
[PrefKey.MobileSortNotesBy]: CollectionSortProperty
|
||||
[PrefKey.MobileSortNotesReverse]: boolean
|
||||
@@ -99,4 +100,5 @@ export type PrefValue = {
|
||||
[PrefKey.EditorLineHeight]: EditorLineHeight
|
||||
[PrefKey.EditorFontSize]: EditorFontSize
|
||||
[PrefKey.UpdateSavingStatusIndicator]: boolean
|
||||
[PrefKey.DarkMode]: boolean
|
||||
}
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.3.122](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.121](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.120](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.119](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.118](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.117](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
## [1.3.116](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/releases
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/releases",
|
||||
"version": "1.3.116",
|
||||
"version": "1.3.122",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/releases.json",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.26.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add workspaces api ([#1765](https://github.com/standardnotes/app/issues/1765)) ([01ba715](https://github.com/standardnotes/app/commit/01ba715eba987a7da1ee062fec0b3593a7a453ed))
|
||||
|
||||
# [1.25.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
## [1.24.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
## [1.24.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/services
|
||||
|
||||
@@ -12,7 +12,7 @@ module.exports = {
|
||||
global: {
|
||||
branches: 9,
|
||||
functions: 10,
|
||||
lines: 17,
|
||||
lines: 16,
|
||||
statements: 16
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/services",
|
||||
"version": "1.24.2",
|
||||
"version": "1.26.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { ApplicationIdentifier, ContentType } from '@standardnotes/common'
|
||||
import { BackupFile, DecryptedItemInterface, ItemStream, Platform, PrefKey, PrefValue } from '@standardnotes/models'
|
||||
import { FilesClientInterface } from '@standardnotes/files'
|
||||
import { AlertService } from '../Alert/AlertService'
|
||||
|
||||
import { AlertService } from '../Alert/AlertService'
|
||||
import { ComponentManagerInterface } from '../Component/ComponentManagerInterface'
|
||||
import { ApplicationEvent } from '../Event/ApplicationEvent'
|
||||
import { ApplicationEventCallback } from '../Event/ApplicationEventCallback'
|
||||
import { FeaturesClientInterface } from '../Feature/FeaturesClientInterface'
|
||||
import { SubscriptionClientInterface } from '../Subscription/SubscriptionClientInterface'
|
||||
import { DeviceInterface } from '../Device/DeviceInterface'
|
||||
import { WorkspaceClientInterface } from '../Workspace/WorkspaceClientInterface'
|
||||
import { ItemsClientInterface } from '../Item/ItemsClientInterface'
|
||||
import { MutatorClientInterface } from '../Mutator/MutatorClientInterface'
|
||||
import { StorageValueModes } from '../Storage/StorageTypes'
|
||||
@@ -15,7 +17,6 @@ import { StorageValueModes } from '../Storage/StorageTypes'
|
||||
import { DeinitMode } from './DeinitMode'
|
||||
import { DeinitSource } from './DeinitSource'
|
||||
import { UserClientInterface } from './UserClientInterface'
|
||||
import { DeviceInterface } from '../Device/DeviceInterface'
|
||||
|
||||
export interface ApplicationInterface {
|
||||
deinit(mode: DeinitMode, source: DeinitSource): void
|
||||
@@ -49,6 +50,7 @@ export interface ApplicationInterface {
|
||||
get user(): UserClientInterface
|
||||
get files(): FilesClientInterface
|
||||
get subscriptions(): SubscriptionClientInterface
|
||||
get workspaces(): WorkspaceClientInterface
|
||||
readonly identifier: ApplicationIdentifier
|
||||
readonly platform: Platform
|
||||
deviceInterface: DeviceInterface
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ContentType, Uuid } from '@standardnotes/common'
|
||||
import { EncryptionProvider } from '@standardnotes/encryption'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { PayloadEmitSource, FileItem, CreateEncryptedBackupFileContextPayload } from '@standardnotes/models'
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { FilesApiInterface, FileBackupMetadataFile, FileBackupsDevice, FileBackupsMapping } from '@standardnotes/files'
|
||||
@@ -15,7 +15,7 @@ export class FilesBackupService extends AbstractService {
|
||||
constructor(
|
||||
private items: ItemManagerInterface,
|
||||
private api: FilesApiInterface,
|
||||
private encryptor: EncryptionProvider,
|
||||
private encryptor: EncryptionProviderInterface,
|
||||
private device: FileBackupsDevice,
|
||||
private status: StatusServiceInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
DecryptedParameters,
|
||||
EncryptedParameters,
|
||||
encryptedParametersFromPayload,
|
||||
EncryptionProvider,
|
||||
EncryptionProviderInterface,
|
||||
ErrorDecryptingParameters,
|
||||
findDefaultItemsKey,
|
||||
FindPayloadInDecryptionSplit,
|
||||
@@ -100,7 +100,7 @@ import { EncryptionServiceEvent } from './EncryptionServiceEvent'
|
||||
* It also exposes public methods that allows consumers to retrieve an items key
|
||||
* for a particular payload, and also retrieve all available items keys.
|
||||
*/
|
||||
export class EncryptionService extends AbstractService<EncryptionServiceEvent> implements EncryptionProvider {
|
||||
export class EncryptionService extends AbstractService<EncryptionServiceEvent> implements EncryptionProviderInterface {
|
||||
private operatorManager: OperatorManager
|
||||
private readonly itemsEncryption: ItemsEncryptionService
|
||||
private readonly rootKeyEncryption: RootKeyEncryptionService
|
||||
@@ -714,7 +714,7 @@ export class EncryptionService extends AbstractService<EncryptionServiceEvent> i
|
||||
await this.rootKeyEncryption.createNewDefaultItemsKey()
|
||||
}
|
||||
|
||||
this.syncUnsycnedItemsKeys()
|
||||
this.syncUnsyncedItemsKeys()
|
||||
}
|
||||
|
||||
private async handleFullSyncCompletion() {
|
||||
@@ -734,7 +734,7 @@ export class EncryptionService extends AbstractService<EncryptionServiceEvent> i
|
||||
* items key never syncing to the account even though it is being used to encrypt synced items.
|
||||
* Until we can determine its cause, this corrective function will find any such keys and sync them.
|
||||
*/
|
||||
private syncUnsycnedItemsKeys(): void {
|
||||
private syncUnsyncedItemsKeys(): void {
|
||||
if (!this.hasAccount()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
ItemsKeyContent,
|
||||
RootKeyInterface,
|
||||
} from '@standardnotes/models'
|
||||
import { EncryptionProvider, KeyRecoveryStrings, SNRootKeyParams } from '@standardnotes/encryption'
|
||||
import { EncryptionProviderInterface, KeyRecoveryStrings, SNRootKeyParams } from '@standardnotes/encryption'
|
||||
import { ChallengeServiceInterface } from '../Challenge/ChallengeServiceInterface'
|
||||
import { ChallengePrompt } from '../Challenge/Prompt/ChallengePrompt'
|
||||
import { ChallengeReason } from '../Challenge/Types/ChallengeReason'
|
||||
@@ -13,7 +13,7 @@ import { ChallengeValidation } from '../Challenge/Types/ChallengeValidation'
|
||||
|
||||
export async function DecryptItemsKeyWithUserFallback(
|
||||
itemsKey: EncryptedPayloadInterface,
|
||||
encryptor: EncryptionProvider,
|
||||
encryptor: EncryptionProviderInterface,
|
||||
challengor: ChallengeServiceInterface,
|
||||
): Promise<DecryptedPayloadInterface<ItemsKeyContent> | 'failed' | 'aborted'> {
|
||||
const decryptionResult = await encryptor.decryptSplitSingle<ItemsKeyContent>({
|
||||
@@ -37,7 +37,7 @@ export async function DecryptItemsKeyWithUserFallback(
|
||||
|
||||
export async function DecryptItemsKeyByPromptingUser(
|
||||
itemsKey: EncryptedPayloadInterface,
|
||||
encryptor: EncryptionProvider,
|
||||
encryptor: EncryptionProviderInterface,
|
||||
challengor: ChallengeServiceInterface,
|
||||
keyParams?: SNRootKeyParams,
|
||||
): Promise<
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PureCryptoInterface, StreamEncryptor } from '@standardnotes/sncrypto-common'
|
||||
import { FileItem } from '@standardnotes/models'
|
||||
import { EncryptionProvider } from '@standardnotes/encryption'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
|
||||
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
|
||||
import { ChallengeServiceInterface } from '../Challenge'
|
||||
@@ -19,7 +19,7 @@ describe('fileService', () => {
|
||||
let crypto: PureCryptoInterface
|
||||
let challengor: ChallengeServiceInterface
|
||||
let fileService: FileService
|
||||
let encryptor: EncryptionProvider
|
||||
let encryptor: EncryptionProviderInterface
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -41,7 +41,7 @@ describe('fileService', () => {
|
||||
syncService = {} as jest.Mocked<SyncServiceInterface>
|
||||
syncService.sync = jest.fn()
|
||||
|
||||
encryptor = {} as jest.Mocked<EncryptionProvider>
|
||||
encryptor = {} as jest.Mocked<EncryptionProviderInterface>
|
||||
|
||||
alertService = {} as jest.Mocked<AlertService>
|
||||
alertService.confirm = jest.fn().mockReturnValue(true)
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from '@standardnotes/models'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { UuidGenerator } from '@standardnotes/utils'
|
||||
import { EncryptionProvider, SNItemsKey } from '@standardnotes/encryption'
|
||||
import { EncryptionProviderInterface, SNItemsKey } from '@standardnotes/encryption'
|
||||
import {
|
||||
DownloadAndDecryptFileOperation,
|
||||
EncryptAndUploadFileOperation,
|
||||
@@ -49,7 +49,7 @@ export class FileService extends AbstractService implements FilesClientInterface
|
||||
private api: FilesApiInterface,
|
||||
private itemManager: ItemManagerInterface,
|
||||
private syncService: SyncServiceInterface,
|
||||
private encryptor: EncryptionProvider,
|
||||
private encryptor: EncryptionProviderInterface,
|
||||
private challengor: ChallengeServiceInterface,
|
||||
private alertService: AlertService,
|
||||
private crypto: PureCryptoInterface,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface WorkspaceClientInterface {
|
||||
createWorkspace(dto: {
|
||||
encryptedWorkspaceKey: string
|
||||
encryptedPrivateKey: string
|
||||
publicKey: string
|
||||
workspaceName?: string
|
||||
}): Promise<{ uuid: string } | null>
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { WorkspaceApiServiceInterface } from '@standardnotes/api'
|
||||
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
|
||||
import { AbstractService } from '../Service/AbstractService'
|
||||
import { WorkspaceClientInterface } from './WorkspaceClientInterface'
|
||||
|
||||
export class WorkspaceManager extends AbstractService implements WorkspaceClientInterface {
|
||||
constructor(
|
||||
private workspaceApiService: WorkspaceApiServiceInterface,
|
||||
protected override internalEventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(internalEventBus)
|
||||
}
|
||||
|
||||
async createWorkspace(dto: {
|
||||
encryptedWorkspaceKey: string
|
||||
encryptedPrivateKey: string
|
||||
publicKey: string
|
||||
workspaceName?: string
|
||||
}): Promise<{ uuid: string } | null> {
|
||||
try {
|
||||
const result = await this.workspaceApiService.createWorkspace(dto)
|
||||
|
||||
if (result.data.error !== undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
return result.data
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,3 +73,5 @@ export * from './Sync/SyncOptions'
|
||||
export * from './Sync/SyncQueueStrategy'
|
||||
export * from './Sync/SyncServiceInterface'
|
||||
export * from './Sync/SyncSource'
|
||||
export * from './Workspace/WorkspaceClientInterface'
|
||||
export * from './Workspace/WorkspaceManager'
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
# [1.12.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/sncrypto-common",
|
||||
"version": "1.12.0",
|
||||
"version": "1.13.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PkcKeyPair } from '../Types'
|
||||
import { Base64String } from '../Types/Base64String'
|
||||
import { Base64URLSafeString } from '../Types/Base64URLSafeString'
|
||||
import { HexString } from '../Types/HexString'
|
||||
@@ -27,7 +28,7 @@ export interface PureCryptoInterface {
|
||||
* @param bits - Length of key in bits
|
||||
* @returns A string key in hex format
|
||||
*/
|
||||
generateRandomKey(bits: number): string
|
||||
generateRandomKey(bits: number): HexString
|
||||
|
||||
/**
|
||||
* @legacy
|
||||
@@ -98,7 +99,7 @@ export interface PureCryptoInterface {
|
||||
* @param assocData
|
||||
* @returns Base64 ciphertext string
|
||||
*/
|
||||
xchacha20Encrypt(plaintext: Utf8String, nonce: HexString, key: HexString, assocData: Utf8String): Base64String
|
||||
xchacha20Encrypt(plaintext: Utf8String, nonce: HexString, key: HexString, assocData?: Utf8String): Base64String
|
||||
|
||||
/**
|
||||
* Decrypt a message (and associated data) with XChaCha20-Poly1305
|
||||
@@ -112,7 +113,7 @@ export interface PureCryptoInterface {
|
||||
ciphertext: Base64String,
|
||||
nonce: HexString,
|
||||
key: HexString,
|
||||
assocData: Utf8String | Uint8Array,
|
||||
assocData?: Utf8String | Uint8Array,
|
||||
): Utf8String | null
|
||||
|
||||
xchacha20StreamInitEncryptor(key: HexString): StreamEncryptor
|
||||
@@ -132,6 +133,22 @@ export interface PureCryptoInterface {
|
||||
assocData: Utf8String,
|
||||
): { message: Uint8Array; tag: SodiumConstant } | false
|
||||
|
||||
sodiumCryptoBoxEasyEncrypt(
|
||||
message: Utf8String,
|
||||
nonce: HexString,
|
||||
senderSecretKey: HexString,
|
||||
recipientPublicKey: HexString,
|
||||
): Base64String
|
||||
|
||||
sodiumCryptoBoxEasyDecrypt(
|
||||
ciphertext: Base64String,
|
||||
nonce: HexString,
|
||||
senderPublicKey: HexString,
|
||||
recipientSecretKey: HexString,
|
||||
): Utf8String
|
||||
|
||||
sodiumCryptoBoxGenerateKeypair(): PkcKeyPair
|
||||
|
||||
/**
|
||||
* Converts a plain string into base64
|
||||
* @param text - A plain string
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.14.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
# [1.13.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-09-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/sncrypto-web",
|
||||
"version": "1.13.0",
|
||||
"version": "1.14.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -93,7 +93,7 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
return this.webCryptoDeriveBits(key, salt, iterations, length)
|
||||
}
|
||||
|
||||
public generateRandomKey(bits: number): string {
|
||||
public generateRandomKey(bits: number): HexString {
|
||||
const bytes = bits / 8
|
||||
const arrayBuffer = Utils.getGlobalScope().crypto.getRandomValues(new Uint8Array(bytes))
|
||||
return Utils.arrayBufferToHexString(arrayBuffer)
|
||||
@@ -249,14 +249,14 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
plaintext: Utf8String,
|
||||
nonce: HexString,
|
||||
key: HexString,
|
||||
assocData: Utf8String,
|
||||
assocData?: Utf8String,
|
||||
): Base64String {
|
||||
if (nonce.length !== 48) {
|
||||
throw Error('Nonce must be 24 bytes')
|
||||
}
|
||||
const arrayBuffer = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
plaintext,
|
||||
assocData,
|
||||
assocData || null,
|
||||
null,
|
||||
Utils.hexStringToArrayBuffer(nonce),
|
||||
Utils.hexStringToArrayBuffer(key),
|
||||
@@ -268,7 +268,7 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
ciphertext: Base64String,
|
||||
nonce: HexString,
|
||||
key: HexString,
|
||||
assocData: Utf8String | Uint8Array,
|
||||
assocData?: Utf8String | Uint8Array,
|
||||
): Utf8String | null {
|
||||
if (nonce.length !== 48) {
|
||||
throw Error('Nonce must be 24 bytes')
|
||||
@@ -277,7 +277,7 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
return sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
null,
|
||||
Utils.base64ToArrayBuffer(ciphertext),
|
||||
assocData,
|
||||
assocData || null,
|
||||
Utils.hexStringToArrayBuffer(nonce),
|
||||
Utils.hexStringToArrayBuffer(key),
|
||||
'text',
|
||||
@@ -368,7 +368,7 @@ export class SNWebCrypto implements PureCryptoInterface {
|
||||
nonce: HexString,
|
||||
senderPublicKey: HexString,
|
||||
recipientSecretKey: HexString,
|
||||
): Base64String {
|
||||
): Utf8String {
|
||||
const result = sodium.crypto_box_open_easy(
|
||||
Utils.base64ToArrayBuffer(ciphertext),
|
||||
Utils.hexStringToArrayBuffer(nonce),
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [2.136.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** add workspaces api ([#1765](https://github.com/standardnotes/app/issues/1765)) ([01ba715](https://github.com/standardnotes/app/commit/01ba715eba987a7da1ee062fec0b3593a7a453ed))
|
||||
|
||||
# [2.135.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* experimental 005 operator ([#1753](https://github.com/standardnotes/app/issues/1753)) ([cbbe913](https://github.com/standardnotes/app/commit/cbbe913cd6eb694dd27997927bd5c45e8a64cc09))
|
||||
|
||||
## [2.134.9](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** passcode timing options ([#1744](https://github.com/standardnotes/app/issues/1744)) ([6c26b96](https://github.com/standardnotes/app/commit/6c26b96cdce0d69a4a459104709880850a401a92))
|
||||
|
||||
## [2.134.8](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/snjs
|
||||
|
||||
@@ -14,6 +14,10 @@ import {
|
||||
WebSocketApiServiceInterface,
|
||||
WebSocketServer,
|
||||
WebSocketServerInterface,
|
||||
WorkspaceApiService,
|
||||
WorkspaceApiServiceInterface,
|
||||
WorkspaceServer,
|
||||
WorkspaceServerInterface,
|
||||
} from '@standardnotes/api'
|
||||
import * as Common from '@standardnotes/common'
|
||||
import * as ExternalServices from '@standardnotes/services'
|
||||
@@ -45,7 +49,8 @@ import {
|
||||
FileService,
|
||||
SubscriptionClientInterface,
|
||||
SubscriptionManager,
|
||||
StorageValueModes,
|
||||
WorkspaceClientInterface,
|
||||
WorkspaceManager,
|
||||
} from '@standardnotes/services'
|
||||
import { FilesClientInterface } from '@standardnotes/files'
|
||||
import { ComputePrivateWorkspaceIdentifier } from '@standardnotes/encryption'
|
||||
@@ -65,7 +70,6 @@ import { SNLog } from '../Log'
|
||||
import { Challenge, ChallengeResponse } from '../Services'
|
||||
import { ApplicationConstructorOptions, FullyResolvedApplicationOptions } from './Options/ApplicationOptions'
|
||||
import { ApplicationOptionsDefaults } from './Options/Defaults'
|
||||
import { MobileUnlockTiming } from '@Lib/Services/Protection/MobileUnlockTiming'
|
||||
|
||||
/** How often to automatically sync, in milliseconds */
|
||||
const DEFAULT_AUTO_SYNC_INTERVAL = 30_000
|
||||
@@ -112,6 +116,9 @@ export class SNApplication
|
||||
private declare subscriptionApiService: SubscriptionApiServiceInterface
|
||||
private declare subscriptionServer: SubscriptionServerInterface
|
||||
private declare subscriptionManager: SubscriptionClientInterface
|
||||
private declare workspaceApiService: WorkspaceApiServiceInterface
|
||||
private declare workspaceServer: WorkspaceServerInterface
|
||||
private declare workspaceManager: WorkspaceClientInterface
|
||||
private declare webSocketApiService: WebSocketApiServiceInterface
|
||||
private declare webSocketServer: WebSocketServerInterface
|
||||
private sessionManager!: InternalServices.SNSessionManager
|
||||
@@ -213,6 +220,10 @@ export class SNApplication
|
||||
return this.subscriptionManager
|
||||
}
|
||||
|
||||
get workspaces(): ExternalServices.WorkspaceClientInterface {
|
||||
return this.workspaceManager
|
||||
}
|
||||
|
||||
public get files(): FilesClientInterface {
|
||||
return this.fileService
|
||||
}
|
||||
@@ -899,24 +910,6 @@ export class SNApplication
|
||||
return this.launched
|
||||
}
|
||||
|
||||
public hasBiometrics(): boolean {
|
||||
return this.protectionService.hasBiometricsEnabled()
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the operation was successful or not
|
||||
*/
|
||||
public enableBiometrics(): boolean {
|
||||
return this.protectionService.enableBiometrics()
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns whether the operation was successful or not
|
||||
*/
|
||||
public disableBiometrics(): Promise<boolean> {
|
||||
return this.protectionService.disableBiometrics()
|
||||
}
|
||||
|
||||
public hasPasscode(): boolean {
|
||||
return this.protocolService.hasPasscode()
|
||||
}
|
||||
@@ -936,38 +929,6 @@ export class SNApplication
|
||||
return this.deinit(this.getDeinitMode(), DeinitSource.Lock)
|
||||
}
|
||||
|
||||
setBiometricsTiming(timing: MobileUnlockTiming) {
|
||||
return this.protectionService.setBiometricsTiming(timing)
|
||||
}
|
||||
|
||||
getMobileScreenshotPrivacyEnabled(): boolean {
|
||||
return this.protectionService.getMobileScreenshotPrivacyEnabled()
|
||||
}
|
||||
|
||||
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
||||
return this.protectionService.setMobileScreenshotPrivacyEnabled(isEnabled)
|
||||
}
|
||||
|
||||
getMobilePasscodeTiming(): MobileUnlockTiming | undefined {
|
||||
return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as
|
||||
| MobileUnlockTiming
|
||||
| undefined
|
||||
}
|
||||
|
||||
getMobileBiometricsTiming(): MobileUnlockTiming | undefined {
|
||||
return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as
|
||||
| MobileUnlockTiming
|
||||
| undefined
|
||||
}
|
||||
|
||||
getBiometricsTimingOptions() {
|
||||
return this.protectionService.getBiometricsTimingOptions()
|
||||
}
|
||||
|
||||
getPasscodeTimingOptions() {
|
||||
return this.protectionService.getPasscodeTimingOptions()
|
||||
}
|
||||
|
||||
isNativeMobileWeb() {
|
||||
return this.environment === Environment.NativeMobileWeb
|
||||
}
|
||||
@@ -1099,6 +1060,9 @@ export class SNApplication
|
||||
this.createWebSocketServer()
|
||||
this.createWebSocketApiService()
|
||||
this.createSubscriptionManager()
|
||||
this.createWorkspaceServer()
|
||||
this.createWorkspaceApiService()
|
||||
this.createWorkspaceManager()
|
||||
this.createWebSocketsService()
|
||||
this.createSessionManager()
|
||||
this.createHistoryManager()
|
||||
@@ -1139,9 +1103,12 @@ export class SNApplication
|
||||
;(this.userServer as unknown) = undefined
|
||||
;(this.subscriptionApiService as unknown) = undefined
|
||||
;(this.subscriptionServer as unknown) = undefined
|
||||
;(this.subscriptionManager as unknown) = undefined
|
||||
;(this.workspaceApiService as unknown) = undefined
|
||||
;(this.workspaceServer as unknown) = undefined
|
||||
;(this.workspaceManager as unknown) = undefined
|
||||
;(this.webSocketApiService as unknown) = undefined
|
||||
;(this.webSocketServer as unknown) = undefined
|
||||
;(this.subscriptionManager as unknown) = undefined
|
||||
;(this.sessionManager as unknown) = undefined
|
||||
;(this.syncService as unknown) = undefined
|
||||
;(this.challengeService as unknown) = undefined
|
||||
@@ -1356,6 +1323,18 @@ export class SNApplication
|
||||
this.subscriptionManager = new SubscriptionManager(this.subscriptionApiService, this.internalEventBus)
|
||||
}
|
||||
|
||||
private createWorkspaceServer() {
|
||||
this.workspaceServer = new WorkspaceServer(this.httpService)
|
||||
}
|
||||
|
||||
private createWorkspaceApiService() {
|
||||
this.workspaceApiService = new WorkspaceApiService(this.workspaceServer)
|
||||
}
|
||||
|
||||
private createWorkspaceManager() {
|
||||
this.workspaceManager = new WorkspaceManager(this.workspaceApiService, this.internalEventBus)
|
||||
}
|
||||
|
||||
private createItemManager() {
|
||||
this.itemManager = new InternalServices.ItemManager(this.payloadManager, this.options, this.internalEventBus)
|
||||
this.services.push(this.itemManager)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { ItemsKeyInterface } from '@standardnotes/models'
|
||||
import { dateSorted } from '@standardnotes/utils'
|
||||
import { SNRootKeyParams, EncryptionProvider } from '@standardnotes/encryption'
|
||||
import { SNRootKeyParams, EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { DecryptionQueueItem, KeyRecoveryOperationResult } from './Types'
|
||||
import { serverKeyParamsAreSafe } from './Utils'
|
||||
import { ChallengeServiceInterface, DecryptItemsKeyByPromptingUser } from '@standardnotes/services'
|
||||
@@ -11,7 +11,7 @@ export class KeyRecoveryOperation {
|
||||
constructor(
|
||||
private queueItem: DecryptionQueueItem,
|
||||
private itemManager: ItemManager,
|
||||
private protocolService: EncryptionProvider,
|
||||
private protocolService: EncryptionProviderInterface,
|
||||
private challengeService: ChallengeServiceInterface,
|
||||
private clientParams: SNRootKeyParams | undefined,
|
||||
private serverParams: SNRootKeyParams | undefined,
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
ChallengeReason,
|
||||
MutatorClientInterface,
|
||||
} from '@standardnotes/services'
|
||||
import { EncryptionProvider } from '@standardnotes/encryption'
|
||||
import { EncryptionProviderInterface } from '@standardnotes/encryption'
|
||||
import { ClientDisplayableError } from '@standardnotes/responses'
|
||||
import { ContentType, ProtocolVersion, compareVersions } from '@standardnotes/common'
|
||||
import { ItemManager } from '../Items'
|
||||
@@ -49,7 +49,7 @@ export class MutatorService extends AbstractService implements MutatorClientInte
|
||||
private itemManager: ItemManager,
|
||||
private syncService: SNSyncService,
|
||||
private protectionService: SNProtectionService,
|
||||
private encryption: EncryptionProvider,
|
||||
private encryption: EncryptionProviderInterface,
|
||||
private payloadManager: PayloadManager,
|
||||
private challengeService: ChallengeService,
|
||||
private componentManager: SNComponentManager,
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import { ChallengeReason } from '@standardnotes/services'
|
||||
import { DecryptedItem } from '@standardnotes/models'
|
||||
import { TimingDisplayOption, MobileUnlockTiming } from './MobileUnlockTiming'
|
||||
|
||||
export interface ProtectionsClientInterface {
|
||||
authorizeProtectedActionForItems<T extends DecryptedItem>(files: T[], challengeReason: ChallengeReason): Promise<T[]>
|
||||
|
||||
authorizeItemAccess(item: DecryptedItem): Promise<boolean>
|
||||
getMobileBiometricsTiming(): MobileUnlockTiming | undefined
|
||||
getMobilePasscodeTiming(): MobileUnlockTiming | undefined
|
||||
setMobileBiometricsTiming(timing: MobileUnlockTiming): void
|
||||
setMobilePasscodeTiming(timing: MobileUnlockTiming): void
|
||||
setMobileScreenshotPrivacyEnabled(isEnabled: boolean): void
|
||||
getMobileScreenshotPrivacyEnabled(): boolean
|
||||
getMobilePasscodeTimingOptions(): TimingDisplayOption[]
|
||||
getMobileBiometricsTimingOptions(): TimingDisplayOption[]
|
||||
hasBiometricsEnabled(): boolean
|
||||
enableBiometrics(): boolean
|
||||
disableBiometrics(): Promise<boolean>
|
||||
}
|
||||
|
||||
@@ -2,3 +2,9 @@ export enum MobileUnlockTiming {
|
||||
Immediately = 'immediately',
|
||||
OnQuit = 'on-quit',
|
||||
}
|
||||
|
||||
export type TimingDisplayOption = {
|
||||
title: string
|
||||
key: MobileUnlockTiming
|
||||
selected: boolean
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from '@standardnotes/services'
|
||||
import { ProtectionsClientInterface } from './ClientInterface'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { MobileUnlockTiming } from './MobileUnlockTiming'
|
||||
import { MobileUnlockTiming, TimingDisplayOption } from './MobileUnlockTiming'
|
||||
|
||||
export enum ProtectionEvent {
|
||||
UnprotectedSessionBegan = 'UnprotectedSessionBegan',
|
||||
@@ -64,8 +64,8 @@ export const ProtectionSessionDurations = [
|
||||
*/
|
||||
export class SNProtectionService extends AbstractService<ProtectionEvent> implements ProtectionsClientInterface {
|
||||
private sessionExpiryTimeout = -1
|
||||
private mobilePasscodeTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.Immediately
|
||||
private mobileBiometricsTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.Immediately
|
||||
private mobilePasscodeTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit
|
||||
private mobileBiometricsTiming: MobileUnlockTiming | undefined = MobileUnlockTiming.OnQuit
|
||||
|
||||
constructor(
|
||||
private protocolService: EncryptionService,
|
||||
@@ -87,8 +87,8 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
override handleApplicationStage(stage: ApplicationStage): Promise<void> {
|
||||
if (stage === ApplicationStage.LoadedDatabase_12) {
|
||||
this.updateSessionExpiryTimer(this.getSessionExpiryDate())
|
||||
this.mobilePasscodeTiming = this.getPasscodeTiming()
|
||||
this.mobileBiometricsTiming = this.getBiometricsTiming()
|
||||
this.mobilePasscodeTiming = this.getMobilePasscodeTiming()
|
||||
this.mobileBiometricsTiming = this.getMobileBiometricsTiming()
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
@@ -229,7 +229,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
})
|
||||
}
|
||||
|
||||
getPasscodeTimingOptions() {
|
||||
getMobilePasscodeTimingOptions(): TimingDisplayOption[] {
|
||||
return [
|
||||
{
|
||||
title: 'Immediately',
|
||||
@@ -244,7 +244,7 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
]
|
||||
}
|
||||
|
||||
getBiometricsTimingOptions() {
|
||||
getMobileBiometricsTimingOptions(): TimingDisplayOption[] {
|
||||
return [
|
||||
{
|
||||
title: 'Immediately',
|
||||
@@ -259,25 +259,32 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
]
|
||||
}
|
||||
|
||||
private getBiometricsTiming(): MobileUnlockTiming | undefined {
|
||||
getMobileBiometricsTiming(): MobileUnlockTiming | undefined {
|
||||
return this.storageService.getValue<MobileUnlockTiming | undefined>(
|
||||
StorageKey.MobileBiometricsTiming,
|
||||
StorageValueModes.Nonwrapped,
|
||||
MobileUnlockTiming.OnQuit,
|
||||
)
|
||||
}
|
||||
|
||||
private getPasscodeTiming(): MobileUnlockTiming | undefined {
|
||||
getMobilePasscodeTiming(): MobileUnlockTiming | undefined {
|
||||
return this.storageService.getValue<MobileUnlockTiming | undefined>(
|
||||
StorageKey.MobilePasscodeTiming,
|
||||
StorageValueModes.Nonwrapped,
|
||||
MobileUnlockTiming.OnQuit,
|
||||
)
|
||||
}
|
||||
|
||||
async setBiometricsTiming(timing: MobileUnlockTiming) {
|
||||
setMobileBiometricsTiming(timing: MobileUnlockTiming): void {
|
||||
this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped)
|
||||
this.mobileBiometricsTiming = timing
|
||||
}
|
||||
|
||||
setMobilePasscodeTiming(timing: MobileUnlockTiming): void {
|
||||
this.storageService.setValue(StorageKey.MobilePasscodeTiming, timing, StorageValueModes.Nonwrapped)
|
||||
this.mobilePasscodeTiming = timing
|
||||
}
|
||||
|
||||
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
||||
return this.storageService.setValue(StorageKey.MobileScreenshotPrivacyEnabled, isEnabled, StorageValueModes.Default)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
* key can decrypt wrapped storage.
|
||||
*/
|
||||
export class DiskStorageService extends Services.AbstractService implements Services.StorageServiceInterface {
|
||||
private encryptionProvider!: Encryption.EncryptionProvider
|
||||
private encryptionProvider!: Encryption.EncryptionProviderInterface
|
||||
private storagePersistable = false
|
||||
private persistencePolicy!: Services.StoragePersistencePolicies
|
||||
private encryptionPolicy!: Services.StorageEncryptionPolicy
|
||||
@@ -53,7 +53,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
void this.setEncryptionPolicy(Services.StorageEncryptionPolicy.Default, false)
|
||||
}
|
||||
|
||||
public provideEncryptionProvider(provider: Encryption.EncryptionProvider): void {
|
||||
public provideEncryptionProvider(provider: Encryption.EncryptionProviderInterface): void {
|
||||
this.encryptionProvider = provider
|
||||
}
|
||||
|
||||
|
||||
@@ -39,12 +39,14 @@ describe('basic auth', function () {
|
||||
let error = null
|
||||
try {
|
||||
await this.application.register(this.email, password)
|
||||
} catch(caughtError) {
|
||||
} catch (caughtError) {
|
||||
error = caughtError
|
||||
}
|
||||
|
||||
expect(error.message).to.equal('Your password must be at least 8 characters in length. '
|
||||
+ 'For your security, please choose a longer password or, ideally, a passphrase, and try again.')
|
||||
expect(error.message).to.equal(
|
||||
'Your password must be at least 8 characters in length. ' +
|
||||
'For your security, please choose a longer password or, ideally, a passphrase, and try again.',
|
||||
)
|
||||
|
||||
expect(await this.application.protocolService.getRootKey()).to.not.be.ok
|
||||
})
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('device authentication', function () {
|
||||
const passcode = 'foobar'
|
||||
const wrongPasscode = 'barfoo'
|
||||
await application.addPasscode(passcode)
|
||||
await application.protectionService.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(await application.hasPasscode()).to.equal(true)
|
||||
expect((await application.protectionService.createLaunchChallenge()).prompts.length).to.equal(2)
|
||||
expect(application.protocolService.rootKeyEncryption.keyMode).to.equal(KeyMode.WrapperOnly)
|
||||
|
||||
@@ -69,7 +69,8 @@ export default class FakeWebCrypto {
|
||||
}
|
||||
|
||||
generateRandomKey(bits) {
|
||||
const length = bits / 8
|
||||
const bitsPerHexChar = 4
|
||||
const length = bits / bitsPerHexChar
|
||||
return this.randomString(length)
|
||||
}
|
||||
|
||||
@@ -107,7 +108,13 @@ export default class FakeWebCrypto {
|
||||
}
|
||||
|
||||
argon2(password, salt, iterations, bytes, length) {
|
||||
return btoa(password)
|
||||
const bitsPerHexChar = 4
|
||||
const bitsInByte = 8
|
||||
const encoded = btoa(password)
|
||||
const desiredLength = length * (bitsInByte / bitsPerHexChar)
|
||||
const missingLength = desiredLength - encoded.length
|
||||
const result = `${encoded}${encoded.repeat(Math.ceil(missingLength / encoded.length))}`.slice(0, desiredLength)
|
||||
return result
|
||||
}
|
||||
|
||||
xchacha20Encrypt(plaintext, nonce, key, assocData) {
|
||||
@@ -128,6 +135,33 @@ export default class FakeWebCrypto {
|
||||
return data.plaintext
|
||||
}
|
||||
|
||||
sodiumCryptoBoxEasyEncrypt(message, nonce, senderSecretKey, recipientPublicKey) {
|
||||
const data = {
|
||||
message,
|
||||
nonce,
|
||||
senderSecretKey,
|
||||
recipientPublicKey,
|
||||
}
|
||||
return btoa(JSON.stringify(data))
|
||||
}
|
||||
|
||||
sodiumCryptoBoxEasyDecrypt(ciphertext, nonce, senderPublicKey, recipientSecretKey) {
|
||||
const data = JSON.parse(atob(ciphertext))
|
||||
if (
|
||||
data.senderPublicKey !== senderPublicKey ||
|
||||
data.recipientSecretKey !== recipientSecretKey ||
|
||||
data.nonce !== nonce ||
|
||||
data.assocData !== assocData
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
return data.message
|
||||
}
|
||||
|
||||
sodiumCryptoBoxGenerateKeypair() {
|
||||
return { publicKey: this.randomString(64), privateKey: this.randomString(64), keyType: 'x25519' }
|
||||
}
|
||||
|
||||
generateOtpSecret() {
|
||||
return 'WQVV2GFBRQWU3UQZWQFZC37PSNRXKTA6'
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ describe('protections', function () {
|
||||
|
||||
it('no account, no passcode, biometrics', async function () {
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
await application.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(application.hasProtectionSources()).to.be.true
|
||||
})
|
||||
|
||||
@@ -324,7 +324,7 @@ describe('protections', function () {
|
||||
it('no account, passcode, biometrics', async function () {
|
||||
application = await Factory.createInitAppWithFakeCrypto()
|
||||
await application.addPasscode('passcode')
|
||||
await application.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(application.hasProtectionSources()).to.be.true
|
||||
})
|
||||
|
||||
@@ -345,7 +345,7 @@ describe('protections', function () {
|
||||
email: UuidGenerator.GenerateUuid(),
|
||||
password: UuidGenerator.GenerateUuid(),
|
||||
})
|
||||
await application.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(application.hasProtectionSources()).to.be.true
|
||||
})
|
||||
|
||||
@@ -372,7 +372,7 @@ describe('protections', function () {
|
||||
})
|
||||
Factory.handlePasswordChallenges(application, password)
|
||||
await application.addPasscode('passcode')
|
||||
await application.enableBiometrics()
|
||||
await application.protections.enableBiometrics()
|
||||
expect(application.hasProtectionSources()).to.be.true
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/snjs",
|
||||
"version": "2.134.8",
|
||||
"version": "2.136.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.5.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Features
|
||||
|
||||
* add free dark mode ([#1748](https://github.com/standardnotes/app/issues/1748)) ([09b994f](https://github.com/standardnotes/app/commit/09b994f8f938ae0536f42742f7a221df536c4c4a))
|
||||
|
||||
## [1.4.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-06-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/styles",
|
||||
"version": "1.4.2",
|
||||
"version": "1.5.0",
|
||||
"private": true,
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
--sn-stylekit-background-color: #ffffff;
|
||||
// For borders inside background-color
|
||||
--sn-stylekit-border-color: #dfe1e4;
|
||||
--sn-stylekit-foreground-color: #19191C;
|
||||
--sn-stylekit-foreground-color: #19191c;
|
||||
// Colors for layers placed on top of non-prefixed background, border, and foreground
|
||||
--sn-stylekit-contrast-background-color: rgba(244, 245, 247, 1.0);
|
||||
--sn-stylekit-contrast-background-color: rgba(244, 245, 247, 1);
|
||||
--sn-stylekit-contrast-foreground-color: #2e2e2e;
|
||||
--sn-stylekit-contrast-border-color: #e3e3e3; // For borders inside contrast-background-color
|
||||
|
||||
// Alternative set of background and contrast options
|
||||
--sn-stylekit-secondary-background-color: #EEEFF1;
|
||||
--sn-stylekit-secondary-background-color: #eeeff1;
|
||||
--sn-stylekit-secondary-foreground-color: #2e2e2e;
|
||||
--sn-stylekit-secondary-border-color: #e3e3e3;
|
||||
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.6.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
## [1.6.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* dark mode toggling ([1377846](https://github.com/standardnotes/app/commit/1377846f3f3a23a405c96b8a733d439947abfefe))
|
||||
|
||||
# [1.6.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Features
|
||||
|
||||
* add free dark mode ([#1748](https://github.com/standardnotes/app/issues/1748)) ([09b994f](https://github.com/standardnotes/app/commit/09b994f8f938ae0536f42742f7a221df536c4c4a))
|
||||
|
||||
## [1.5.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/ui-services
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/ui-services",
|
||||
"version": "1.5.2",
|
||||
"version": "1.6.2",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
const CachedThemesKey = 'cachedThemes'
|
||||
const TimeBeforeApplyingColorScheme = 5
|
||||
const DefaultThemeIdentifier = 'Default'
|
||||
const DarkThemeIdentifier = 'Dark'
|
||||
|
||||
export class ThemeManager extends AbstractService {
|
||||
private activeThemes: Uuid[] = []
|
||||
@@ -90,11 +91,13 @@ export class ThemeManager extends AbstractService {
|
||||
private handlePreferencesChangeEvent(): void {
|
||||
const useDeviceThemeSettings = this.application.getPreference(PrefKey.UseSystemColorScheme, false)
|
||||
|
||||
if (useDeviceThemeSettings !== this.lastUseDeviceThemeSettings) {
|
||||
const hasPreferenceChanged = useDeviceThemeSettings !== this.lastUseDeviceThemeSettings
|
||||
|
||||
if (hasPreferenceChanged) {
|
||||
this.lastUseDeviceThemeSettings = useDeviceThemeSettings
|
||||
}
|
||||
|
||||
if (useDeviceThemeSettings) {
|
||||
if (hasPreferenceChanged && useDeviceThemeSettings) {
|
||||
const prefersDarkColorScheme = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
|
||||
this.setThemeAsPerColorScheme(prefersDarkColorScheme.matches)
|
||||
@@ -159,7 +162,11 @@ export class ThemeManager extends AbstractService {
|
||||
}
|
||||
|
||||
private colorSchemeEventHandler(event: MediaQueryListEvent) {
|
||||
this.setThemeAsPerColorScheme(event.matches)
|
||||
const shouldChangeTheme = this.application.getPreference(PrefKey.UseSystemColorScheme, false)
|
||||
|
||||
if (shouldChangeTheme) {
|
||||
this.setThemeAsPerColorScheme(event.matches)
|
||||
}
|
||||
}
|
||||
|
||||
private showColorSchemeToast(setThemeCallback: () => void) {
|
||||
@@ -192,19 +199,35 @@ export class ThemeManager extends AbstractService {
|
||||
|
||||
private setThemeAsPerColorScheme(prefersDarkColorScheme: boolean) {
|
||||
const preference = prefersDarkColorScheme ? PrefKey.AutoDarkThemeIdentifier : PrefKey.AutoLightThemeIdentifier
|
||||
const preferenceDefault =
|
||||
preference === PrefKey.AutoDarkThemeIdentifier ? DarkThemeIdentifier : DefaultThemeIdentifier
|
||||
|
||||
const themes = this.application.items
|
||||
.getDisplayableComponents()
|
||||
.filter((component) => component.isTheme()) as SNTheme[]
|
||||
|
||||
const activeTheme = themes.find((theme) => theme.active && !theme.isLayerable())
|
||||
const activeThemeIdentifier = activeTheme ? activeTheme.identifier : DefaultThemeIdentifier
|
||||
const activeThemeIdentifier = activeTheme
|
||||
? activeTheme.identifier
|
||||
: this.application.getPreference(PrefKey.DarkMode, false)
|
||||
? DarkThemeIdentifier
|
||||
: DefaultThemeIdentifier
|
||||
|
||||
const themeIdentifier = this.application.getPreference(preference, DefaultThemeIdentifier) as string
|
||||
const themeIdentifier = this.application.getPreference(preference, preferenceDefault) as string
|
||||
|
||||
const toggleActiveTheme = () => {
|
||||
if (activeTheme) {
|
||||
void this.application.mutator.toggleTheme(activeTheme)
|
||||
}
|
||||
}
|
||||
|
||||
const setTheme = () => {
|
||||
if (themeIdentifier === DefaultThemeIdentifier && activeTheme) {
|
||||
this.application.mutator.toggleTheme(activeTheme).catch(console.error)
|
||||
if (themeIdentifier === DefaultThemeIdentifier) {
|
||||
toggleActiveTheme()
|
||||
void this.application.setPreference(PrefKey.DarkMode, false)
|
||||
} else if (themeIdentifier === DarkThemeIdentifier) {
|
||||
toggleActiveTheme()
|
||||
void this.application.setPreference(PrefKey.DarkMode, true)
|
||||
} else {
|
||||
const theme = themes.find((theme) => theme.package_info.identifier === themeIdentifier)
|
||||
if (theme && !theme.active) {
|
||||
@@ -300,6 +323,8 @@ export class ThemeManager extends AbstractService {
|
||||
}
|
||||
}
|
||||
document.getElementsByTagName('head')[0].appendChild(link)
|
||||
|
||||
void this.application.setPreference(PrefKey.DarkMode, false)
|
||||
}
|
||||
|
||||
private getBackgroundColor() {
|
||||
|
||||
@@ -3,6 +3,39 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [3.70.4](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/web
|
||||
|
||||
## [3.70.3](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* dark mode toggling ([1377846](https://github.com/standardnotes/app/commit/1377846f3f3a23a405c96b8a733d439947abfefe))
|
||||
* **mobile:** reenable bouncing in lists ([c13dd88](https://github.com/standardnotes/app/commit/c13dd883a41d21d46350676f350ac95b5ae5d5a6))
|
||||
|
||||
## [3.70.2](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* increase default editor font size on mobile ([c944eb9](https://github.com/standardnotes/app/commit/c944eb9365e5835494724724d04c7dc3e20b8f61))
|
||||
|
||||
## [3.70.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add fallback for initial window size ([#1749](https://github.com/standardnotes/app/issues/1749)) ([05aff27](https://github.com/standardnotes/app/commit/05aff2776ba84f2988ebc7b3fca5ab9a18a58430))
|
||||
|
||||
# [3.70.0](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **mobile:** passcode timing options ([#1744](https://github.com/standardnotes/app/issues/1744)) ([6c26b96](https://github.com/standardnotes/app/commit/6c26b96cdce0d69a4a459104709880850a401a92))
|
||||
|
||||
### Features
|
||||
|
||||
* add free dark mode ([#1748](https://github.com/standardnotes/app/issues/1748)) ([09b994f](https://github.com/standardnotes/app/commit/09b994f8f938ae0536f42742f7a221df536c4c4a))
|
||||
|
||||
## [3.69.1](https://github.com/standardnotes/app/compare/@standardnotes/[email protected]...@standardnotes/[email protected]) (2022-10-05)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/web",
|
||||
"version": "3.69.1",
|
||||
"version": "3.70.4",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "dist/app.js",
|
||||
"author": "Standard Notes.",
|
||||
|
||||
@@ -42,10 +42,19 @@ const getKey = () => {
|
||||
}
|
||||
|
||||
const setViewportHeight = () => {
|
||||
document.documentElement.style.setProperty(
|
||||
'--viewport-height',
|
||||
`${visualViewport ? visualViewport.height : window.innerHeight}px`,
|
||||
)
|
||||
const currentValue = parseInt(document.documentElement.style.getPropertyValue('--viewport-height'))
|
||||
const newValue = visualViewport && visualViewport.height > 0 ? visualViewport.height : window.innerHeight
|
||||
|
||||
if (currentValue && !newValue) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!currentValue && !newValue) {
|
||||
document.documentElement.style.setProperty('--viewport-height', '100vh')
|
||||
return
|
||||
}
|
||||
|
||||
document.documentElement.style.setProperty('--viewport-height', `${newValue}px`)
|
||||
}
|
||||
|
||||
const setDefaultMonospaceFont = (platform?: Platform) => {
|
||||
|
||||
@@ -32,7 +32,7 @@ import { PrefDefaults } from '@/Constants/PrefDefaults'
|
||||
type WebServices = {
|
||||
viewControllerManager: ViewControllerManager
|
||||
desktopService?: DesktopManager
|
||||
autolockService: AutolockService
|
||||
autolockService?: AutolockService
|
||||
archiveService: ArchiveManager
|
||||
themeService: ThemeManager
|
||||
io: IOService
|
||||
@@ -237,7 +237,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
async handleMobileGainingFocusEvent(): Promise<void> {}
|
||||
|
||||
async handleMobileLosingFocusEvent(): Promise<void> {
|
||||
if (this.getMobileScreenshotPrivacyEnabled()) {
|
||||
if (this.protections.getMobileScreenshotPrivacyEnabled()) {
|
||||
this.mobileDevice().stopHidingMobileInterfaceFromScreenshots()
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
}
|
||||
|
||||
async handleMobileResumingFromBackgroundEvent(): Promise<void> {
|
||||
if (this.getMobileScreenshotPrivacyEnabled()) {
|
||||
if (this.protections.getMobileScreenshotPrivacyEnabled()) {
|
||||
this.mobileDevice().hideMobileInterfaceFromScreenshots()
|
||||
}
|
||||
}
|
||||
@@ -256,10 +256,10 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
return
|
||||
}
|
||||
|
||||
const hasBiometrics = this.hasBiometrics()
|
||||
const hasBiometrics = this.protections.hasBiometricsEnabled()
|
||||
const hasPasscode = this.hasPasscode()
|
||||
const passcodeTiming = await this.getMobilePasscodeTiming()
|
||||
const biometricsTiming = await this.getMobileBiometricsTiming()
|
||||
const passcodeTiming = this.protections.getMobilePasscodeTiming()
|
||||
const biometricsTiming = this.protections.getMobileBiometricsTiming()
|
||||
|
||||
const passcodeLockImmediately = hasPasscode && passcodeTiming === MobileUnlockTiming.Immediately
|
||||
const biometricsLockImmediately = hasBiometrics && biometricsTiming === MobileUnlockTiming.Immediately
|
||||
|
||||
@@ -34,7 +34,6 @@ const createApplication = (
|
||||
const archiveService = new ArchiveManager(application)
|
||||
const io = new IOService(platform === Platform.MacWeb || platform === Platform.MacDesktop)
|
||||
const internalEventBus = new InternalEventBus()
|
||||
const autolockService = new AutolockService(application, internalEventBus)
|
||||
const themeService = new ThemeManager(application, internalEventBus)
|
||||
|
||||
application.setWebServices({
|
||||
@@ -42,7 +41,7 @@ const createApplication = (
|
||||
archiveService,
|
||||
desktopService: isDesktopDevice(device) ? new DesktopManager(application, device) : undefined,
|
||||
io,
|
||||
autolockService,
|
||||
autolockService: application.isNativeMobileWeb() ? undefined : new AutolockService(application, internalEventBus),
|
||||
themeService,
|
||||
})
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user