You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-07-16 00:14:34 +02:00
Desktop: Windows portable: Fix keychain-backed storage incorrectly enabled (#10942)
This commit is contained in:
@ -13,18 +13,21 @@ interface SafeStorageMockOptions {
|
||||
}
|
||||
|
||||
const mockSafeStorage = ({ // Safe storage
|
||||
isEncryptionAvailable = jest.fn(() => true),
|
||||
encryptString = jest.fn(async s => (`e:${s}`)),
|
||||
decryptString = jest.fn(async s => s.substring(2)),
|
||||
isEncryptionAvailable = () => true,
|
||||
encryptString = async s => (`e:${s}`),
|
||||
decryptString = async (s) => s.substring(2),
|
||||
}: SafeStorageMockOptions) => {
|
||||
const mock = {
|
||||
isEncryptionAvailable: jest.fn(isEncryptionAvailable),
|
||||
encryptString: jest.fn(encryptString),
|
||||
decryptString: jest.fn(decryptString),
|
||||
getSelectedStorageBackend: jest.fn(() => 'mock'),
|
||||
};
|
||||
shim.electronBridge = () => ({
|
||||
safeStorage: {
|
||||
isEncryptionAvailable,
|
||||
encryptString,
|
||||
decryptString,
|
||||
getSelectedStorageBackend: () => 'mock',
|
||||
},
|
||||
safeStorage: mock,
|
||||
});
|
||||
|
||||
return mock;
|
||||
};
|
||||
|
||||
const mockKeytar = () => {
|
||||
@ -51,10 +54,19 @@ const makeDrivers = () => [
|
||||
new KeychainServiceDriverNode(Setting.value('appId'), Setting.value('clientId')),
|
||||
];
|
||||
|
||||
const testSaveLoadSecureSetting = async (expectedPassword: string) => {
|
||||
Setting.setValue('encryption.masterPassword', expectedPassword);
|
||||
await Setting.saveAll();
|
||||
|
||||
await Setting.load();
|
||||
expect(Setting.value('encryption.masterPassword')).toBe(expectedPassword);
|
||||
};
|
||||
|
||||
describe('KeychainService', () => {
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(0);
|
||||
await switchClient(0);
|
||||
KeychainService.instance().readOnly = false;
|
||||
Setting.setValue('keychain.supported', 1);
|
||||
shim.electronBridge = null;
|
||||
shim.keytar = null;
|
||||
@ -91,16 +103,12 @@ describe('KeychainService', () => {
|
||||
const keytarMock = mockKeytar();
|
||||
await KeychainService.instance().initialize(makeDrivers());
|
||||
|
||||
Setting.setValue('encryption.masterPassword', 'test-password');
|
||||
await Setting.saveAll();
|
||||
await testSaveLoadSecureSetting('test-password');
|
||||
expect(keytarMock.setPassword).toHaveBeenCalledWith(
|
||||
`${Setting.value('appId')}.setting.encryption.masterPassword`,
|
||||
`${Setting.value('clientId')}@joplin`,
|
||||
'test-password',
|
||||
);
|
||||
|
||||
await Setting.load();
|
||||
expect(Setting.value('encryption.masterPassword')).toBe('test-password');
|
||||
});
|
||||
|
||||
test('should re-check for keychain support when a new driver is added', async () => {
|
||||
@ -125,4 +133,57 @@ describe('KeychainService', () => {
|
||||
await KeychainService.instance().detectIfKeychainSupported();
|
||||
expect(Setting.value('keychain.supported')).toBe(0);
|
||||
});
|
||||
|
||||
test('should load settings from a read-only KeychainService if not present in the database', async () => {
|
||||
mockSafeStorage({});
|
||||
|
||||
const service = KeychainService.instance();
|
||||
await service.initialize(makeDrivers());
|
||||
expect(await service.setPassword('setting.encryption.masterPassword', 'keychain password')).toBe(true);
|
||||
|
||||
service.readOnly = true;
|
||||
await service.initialize(makeDrivers());
|
||||
await Setting.load();
|
||||
|
||||
expect(Setting.value('encryption.masterPassword')).toBe('keychain password');
|
||||
});
|
||||
|
||||
test('settings should be saved to database with a read-only keychain', async () => {
|
||||
const safeStorage = mockSafeStorage({});
|
||||
|
||||
const service = KeychainService.instance();
|
||||
service.readOnly = true;
|
||||
|
||||
await service.initialize(makeDrivers());
|
||||
await service.detectIfKeychainSupported();
|
||||
|
||||
expect(Setting.value('keychain.supported')).toBe(1);
|
||||
await testSaveLoadSecureSetting('testing...');
|
||||
|
||||
expect(safeStorage.encryptString).not.toHaveBeenCalledWith('testing...');
|
||||
});
|
||||
|
||||
test('loading settings with a read-only keychain should prefer the database', async () => {
|
||||
const safeStorage = mockSafeStorage({});
|
||||
|
||||
const service = KeychainService.instance();
|
||||
await service.initialize(makeDrivers());
|
||||
// Set an initial value
|
||||
expect(await service.setPassword('setting.encryption.masterPassword', 'test keychain password')).toBe(true);
|
||||
|
||||
service.readOnly = true;
|
||||
await service.initialize(makeDrivers());
|
||||
|
||||
safeStorage.encryptString.mockClear();
|
||||
|
||||
Setting.setValue('encryption.masterPassword', 'test database password');
|
||||
await Setting.saveAll();
|
||||
|
||||
await Setting.load();
|
||||
expect(Setting.value('encryption.masterPassword')).toBe('test database password');
|
||||
expect(await service.password('setting.encryption.masterPassword')).toBe('test keychain password');
|
||||
|
||||
// Should not have attempted to encrypt settings in read-only mode.
|
||||
expect(safeStorage.encryptString).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user