1
0
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:
Henry Heino
2024-09-02 04:26:43 -07:00
committed by GitHub
parent 6163364b26
commit fd06c18cf0
5 changed files with 119 additions and 24 deletions

View File

@ -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();
});
});