fix: move wrapped storage to unwrapped if not encrypted (#1603)
This commit is contained in:
@@ -1,2 +1,19 @@
|
||||
node_modules
|
||||
scripts
|
||||
actions
|
||||
.yarn
|
||||
.vscode
|
||||
.sass-cache
|
||||
.husky
|
||||
.github
|
||||
.git
|
||||
**/dist
|
||||
**/coverage
|
||||
**/node_modules
|
||||
packages/components
|
||||
packages/docs
|
||||
packages/mobile/android
|
||||
packages/mobile/ios
|
||||
packages/mobile/html
|
||||
packages/snjs/mocha
|
||||
*.d.ts
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"extends": ["./node_modules/@standardnotes/config/src/.eslintrc"],
|
||||
"ignorePatterns": [".eslintrc.js", "*.webpack.*.js", "webpack-defaults.js", "jest.config.js", "__mocks__"]
|
||||
"ignorePatterns": [".eslintrc.js", "*.webpack.*.js", "webpack-defaults.js", "jest.config.js", "__mocks__", "**/**/coverage"]
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
dist
|
||||
coverage
|
||||
3
packages/desktop/.eslintignore
Normal file
3
packages/desktop/.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
dist
|
||||
jsign
|
||||
@@ -6,3 +6,4 @@ e2e
|
||||
android
|
||||
fastlane
|
||||
WebFrame
|
||||
__tests__
|
||||
@@ -570,7 +570,7 @@ export class ApplicationState extends ApplicationService {
|
||||
).includes(state)
|
||||
}
|
||||
|
||||
private async getScreenshotPrivacyEnabled(): Promise<boolean | undefined> {
|
||||
private async getScreenshotPrivacyEnabled(): Promise<boolean> {
|
||||
return this.application.getMobileScreenshotPrivacyEnabled()
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,11 @@ export class MobileDevice implements MobileDeviceInterface {
|
||||
;(this.stateObserverService as unknown) = undefined
|
||||
}
|
||||
|
||||
consoleLog(...args: any[]): void {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(args)
|
||||
}
|
||||
|
||||
async setLegacyRawKeychainValue(value: LegacyRawKeychainValue): Promise<void> {
|
||||
await Keychain.setKeys(value)
|
||||
}
|
||||
@@ -378,7 +383,7 @@ export class MobileDevice implements MobileDeviceInterface {
|
||||
await Keychain.clearKeys()
|
||||
}
|
||||
|
||||
async setAndroidScreenshotPrivacy(enable: boolean): Promise<void> {
|
||||
setAndroidScreenshotPrivacy(enable: boolean): void {
|
||||
if (Platform.OS === 'android') {
|
||||
enable ? FlagSecure.activate() : FlagSecure.deactivate()
|
||||
}
|
||||
|
||||
@@ -38,14 +38,14 @@ export const SecuritySection = (props: Props) => {
|
||||
useEffect(() => {
|
||||
let mounted = true
|
||||
const getHasScreenshotPrivacy = async () => {
|
||||
const hasScreenshotPrivacyEnabled = (await application?.getMobileScreenshotPrivacyEnabled()) ?? true
|
||||
const hasScreenshotPrivacyEnabled = application?.getMobileScreenshotPrivacyEnabled()
|
||||
if (mounted) {
|
||||
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
||||
}
|
||||
}
|
||||
void getHasScreenshotPrivacy()
|
||||
const getHasBiometrics = async () => {
|
||||
const appHasBiometrics = await application!.hasBiometrics()
|
||||
const appHasBiometrics = application!.hasBiometrics()
|
||||
if (mounted) {
|
||||
setHasBiometrics(appHasBiometrics)
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
dist
|
||||
coverage
|
||||
@@ -6,8 +6,9 @@ export interface MobileDeviceInterface extends DeviceInterface {
|
||||
|
||||
getRawKeychainValue(): Promise<RawKeychainValue | undefined>
|
||||
getDeviceBiometricsAvailability(): Promise<boolean>
|
||||
setAndroidScreenshotPrivacy(enable: boolean): Promise<void>
|
||||
setAndroidScreenshotPrivacy(enable: boolean): void
|
||||
authenticateWithBiometrics(): Promise<boolean>
|
||||
hideMobileInterfaceFromScreenshots(): void
|
||||
stopHidingMobileInterfaceFromScreenshots(): void
|
||||
consoleLog(...args: any[]): void
|
||||
}
|
||||
|
||||
@@ -3,3 +3,7 @@ dist
|
||||
test
|
||||
*.config.js
|
||||
mocha/**/*
|
||||
coverage
|
||||
e2e-server.js
|
||||
jest-global.ts
|
||||
webpack.*.js
|
||||
@@ -324,6 +324,7 @@ export class SNApplication
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
await this.handleStage(ExternalServices.ApplicationStage.StorageDecrypted_09)
|
||||
|
||||
this.apiService.loadHost()
|
||||
@@ -929,30 +930,30 @@ export class SNApplication
|
||||
return this.deinit(this.getDeinitMode(), DeinitSource.Lock)
|
||||
}
|
||||
|
||||
async setBiometricsTiming(timing: MobileUnlockTiming) {
|
||||
setBiometricsTiming(timing: MobileUnlockTiming) {
|
||||
return this.protectionService.setBiometricsTiming(timing)
|
||||
}
|
||||
|
||||
async getMobileScreenshotPrivacyEnabled(): Promise<boolean | undefined> {
|
||||
getMobileScreenshotPrivacyEnabled(): boolean {
|
||||
return this.protectionService.getMobileScreenshotPrivacyEnabled()
|
||||
}
|
||||
|
||||
async getMobilePasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.getValue(StorageKey.MobilePasscodeTiming, StorageValueModes.Nonwrapped) as Promise<
|
||||
MobileUnlockTiming | undefined
|
||||
>
|
||||
}
|
||||
|
||||
async getMobileBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.getValue(StorageKey.MobileBiometricsTiming, StorageValueModes.Nonwrapped) as Promise<
|
||||
MobileUnlockTiming | undefined
|
||||
>
|
||||
}
|
||||
|
||||
async setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
||||
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
|
||||
}
|
||||
|
||||
async loadMobileUnlockTiming() {
|
||||
return this.protectionService.loadMobileUnlockTiming()
|
||||
}
|
||||
|
||||
@@ -257,36 +257,36 @@ export class SNProtectionService extends AbstractService<ProtectionEvent> implem
|
||||
]
|
||||
}
|
||||
|
||||
private async getBiometricsTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.storageService.getValue<Promise<MobileUnlockTiming | undefined>>(
|
||||
private getBiometricsTiming(): MobileUnlockTiming | undefined {
|
||||
return this.storageService.getValue<MobileUnlockTiming | undefined>(
|
||||
StorageKey.MobileBiometricsTiming,
|
||||
StorageValueModes.Nonwrapped,
|
||||
)
|
||||
}
|
||||
|
||||
private async getPasscodeTiming(): Promise<MobileUnlockTiming | undefined> {
|
||||
return this.storageService.getValue<Promise<MobileUnlockTiming | undefined>>(
|
||||
private getPasscodeTiming(): MobileUnlockTiming | undefined {
|
||||
return this.storageService.getValue<MobileUnlockTiming | undefined>(
|
||||
StorageKey.MobilePasscodeTiming,
|
||||
StorageValueModes.Nonwrapped,
|
||||
)
|
||||
}
|
||||
|
||||
async setBiometricsTiming(timing: MobileUnlockTiming) {
|
||||
await this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped)
|
||||
this.storageService.setValue(StorageKey.MobileBiometricsTiming, timing, StorageValueModes.Nonwrapped)
|
||||
this.mobileBiometricsTiming = timing
|
||||
}
|
||||
|
||||
async setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
||||
setMobileScreenshotPrivacyEnabled(isEnabled: boolean) {
|
||||
return this.storageService.setValue(StorageKey.MobileScreenshotPrivacyEnabled, isEnabled, StorageValueModes.Default)
|
||||
}
|
||||
|
||||
async getMobileScreenshotPrivacyEnabled(): Promise<boolean | undefined> {
|
||||
return this.storageService.getValue(StorageKey.MobileScreenshotPrivacyEnabled, StorageValueModes.Default)
|
||||
getMobileScreenshotPrivacyEnabled(): boolean {
|
||||
return this.storageService.getValue(StorageKey.MobileScreenshotPrivacyEnabled, StorageValueModes.Default, false)
|
||||
}
|
||||
|
||||
async loadMobileUnlockTiming() {
|
||||
this.mobilePasscodeTiming = await this.getPasscodeTiming()
|
||||
this.mobileBiometricsTiming = await this.getBiometricsTiming()
|
||||
loadMobileUnlockTiming(): void {
|
||||
this.mobilePasscodeTiming = this.getPasscodeTiming()
|
||||
this.mobileBiometricsTiming = this.getBiometricsTiming()
|
||||
}
|
||||
|
||||
private async validateOrRenewSession(
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { DiskStorageService } from './DiskStorageService'
|
||||
|
||||
import { InternalEventBus, DeviceInterface, InternalEventBusInterface } from '@standardnotes/services'
|
||||
import { Environment } from '@standardnotes/models'
|
||||
|
||||
describe('diskStorageService', () => {
|
||||
let storageService: DiskStorageService
|
||||
let internalEventBus: InternalEventBusInterface
|
||||
let device: DeviceInterface
|
||||
|
||||
beforeEach(() => {
|
||||
internalEventBus = {} as jest.Mocked<InternalEventBus>
|
||||
device = {} as jest.Mocked<DeviceInterface>
|
||||
|
||||
storageService = new DiskStorageService(device, 'test', Environment.Desktop, internalEventBus)
|
||||
})
|
||||
|
||||
it('setInitialValues should set unwrapped values as wrapped value if wrapped value is not encrypted', async () => {
|
||||
storageService.isStorageWrapped = jest.fn().mockReturnValue(false)
|
||||
|
||||
await storageService['setInitialValues']({
|
||||
wrapped: { content: { foo: 'bar' } } as never,
|
||||
nonwrapped: {},
|
||||
unwrapped: { bar: 'zoo' },
|
||||
})
|
||||
|
||||
expect(storageService['values']).toEqual({
|
||||
wrapped: { content: { foo: 'bar' } } as never,
|
||||
nonwrapped: {},
|
||||
unwrapped: { bar: 'zoo', foo: 'bar' },
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -112,14 +112,10 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
const value = await this.deviceInterface.getRawStorageValue(this.getPersistenceKey())
|
||||
const values = value ? JSON.parse(value as string) : undefined
|
||||
|
||||
this.setInitialValues(values)
|
||||
await this.setInitialValues(values)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by platforms with the value they load from disk,
|
||||
* after they handle initializeFromDisk
|
||||
*/
|
||||
private setInitialValues(values?: Services.StorageValuesObject) {
|
||||
private async setInitialValues(values?: Services.StorageValuesObject) {
|
||||
const sureValues = values || this.defaultValuesObject()
|
||||
|
||||
if (!sureValues[Services.ValueModesKeys.Unwrapped]) {
|
||||
@@ -127,6 +123,13 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
}
|
||||
|
||||
this.values = sureValues
|
||||
|
||||
if (!this.isStorageWrapped()) {
|
||||
this.values[Services.ValueModesKeys.Unwrapped] = {
|
||||
...(this.values[Services.ValueModesKeys.Wrapped].content as object),
|
||||
...this.values[Services.ValueModesKeys.Unwrapped],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public isStorageWrapped(): boolean {
|
||||
@@ -370,7 +373,7 @@ export class DiskStorageService extends Services.AbstractService implements Serv
|
||||
* Clears simple values from storage only. Does not affect payloads.
|
||||
*/
|
||||
async clearValues() {
|
||||
this.setInitialValues()
|
||||
await this.setInitialValues()
|
||||
await this.immediatelyPersistValuesToDisk()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
extends: ['../.eslintrc.js'],
|
||||
extends: ['../.eslintrc'],
|
||||
globals: {
|
||||
chai: true,
|
||||
chaiAsPromised: true,
|
||||
|
||||
4
packages/web/.eslintignore
Normal file
4
packages/web/.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
dist
|
||||
node_modules
|
||||
web.webpack-defaults.js
|
||||
coverage
|
||||
@@ -19,7 +19,9 @@
|
||||
"__mocks__",
|
||||
"src/components",
|
||||
"src/favicon",
|
||||
"src/vendor"
|
||||
"src/vendor",
|
||||
"coverage",
|
||||
"*.config.js"
|
||||
],
|
||||
"rules": {
|
||||
"standard/no-callback-literal": 0, // Disable this as we have too many callbacks relying on literals
|
||||
|
||||
@@ -76,6 +76,11 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
|
||||
if (this.isNativeMobileWeb()) {
|
||||
this.mobileWebReceiver = new MobileWebReceiver(this)
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log = (...args) => {
|
||||
this.mobileDevice.consoleLog(...args)
|
||||
}
|
||||
}
|
||||
|
||||
this.onVisibilityChange = () => {
|
||||
@@ -228,7 +233,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
async handleMobileGainingFocusEvent(): Promise<void> {}
|
||||
|
||||
async handleMobileLosingFocusEvent(): Promise<void> {
|
||||
if (await this.getMobileScreenshotPrivacyEnabled()) {
|
||||
if (this.getMobileScreenshotPrivacyEnabled()) {
|
||||
this.mobileDevice.stopHidingMobileInterfaceFromScreenshots()
|
||||
}
|
||||
|
||||
@@ -236,7 +241,7 @@ export class WebApplication extends SNApplication implements WebApplicationInter
|
||||
}
|
||||
|
||||
async handleMobileResumingFromBackgroundEvent(): Promise<void> {
|
||||
if (await this.getMobileScreenshotPrivacyEnabled()) {
|
||||
if (this.getMobileScreenshotPrivacyEnabled()) {
|
||||
this.mobileDevice.hideMobileInterfaceFromScreenshots()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ const BiometricsLock = ({ application }: Props) => {
|
||||
|
||||
useEffect(() => {
|
||||
const getHasBiometrics = async () => {
|
||||
const appHasBiometrics = await application.hasBiometrics()
|
||||
const appHasBiometrics = application.hasBiometrics()
|
||||
setHasBiometrics(appHasBiometrics)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ const BiometricsLock = ({ application }: Props) => {
|
||||
await disableBiometrics()
|
||||
} else {
|
||||
setHasBiometrics(true)
|
||||
await application.enableBiometrics()
|
||||
application.enableBiometrics()
|
||||
await setBiometricsTimingValue(MobileUnlockTiming.OnQuit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,22 +13,19 @@ type Props = {
|
||||
}
|
||||
|
||||
const MultitaskingPrivacy = ({ application }: Props) => {
|
||||
const [hasScreenshotPrivacy, setHasScreenshotPrivacy] = useState<boolean | undefined>(false)
|
||||
const [hasScreenshotPrivacy, setHasScreenshotPrivacy] = useState<boolean>(false)
|
||||
|
||||
useEffect(() => {
|
||||
const getHasScreenshotPrivacy = async () => {
|
||||
const hasScreenshotPrivacyEnabled = (await application.getMobileScreenshotPrivacyEnabled()) ?? true
|
||||
const hasScreenshotPrivacyEnabled = application.getMobileScreenshotPrivacyEnabled()
|
||||
setHasScreenshotPrivacy(hasScreenshotPrivacyEnabled)
|
||||
}
|
||||
void getHasScreenshotPrivacy()
|
||||
}, [application])
|
||||
|
||||
const onScreenshotPrivacyPress = async () => {
|
||||
const enable = !hasScreenshotPrivacy
|
||||
setHasScreenshotPrivacy(enable)
|
||||
|
||||
await application.setMobileScreenshotPrivacyEnabled(enable)
|
||||
await (application.deviceInterface as MobileDeviceInterface).setAndroidScreenshotPrivacy(enable)
|
||||
application.setMobileScreenshotPrivacyEnabled(enable)
|
||||
;(application.deviceInterface as MobileDeviceInterface).setAndroidScreenshotPrivacy(enable)
|
||||
}
|
||||
|
||||
const screenshotPrivacyFeatureText = isIOS() ? 'Multitasking Privacy' : 'Multitasking/Screenshot Privacy'
|
||||
|
||||
Reference in New Issue
Block a user