mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Tools: Added test units for keychains on macOS and Windows
This commit is contained in:
parent
a721f170e4
commit
b125a768b8
@ -68,6 +68,7 @@ CliClient/tests/InMemoryCache.js
|
||||
CliClient/tests/models_Setting.js
|
||||
CliClient/tests/services_CommandService.js
|
||||
CliClient/tests/services_InteropService.js
|
||||
CliClient/tests/services_keychainService.js
|
||||
CliClient/tests/services_PluginService.js
|
||||
CliClient/tests/services_rest_Api.js
|
||||
CliClient/tests/services/plugins/api/JoplinSetting.js
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -62,6 +62,7 @@ CliClient/tests/InMemoryCache.js
|
||||
CliClient/tests/models_Setting.js
|
||||
CliClient/tests/services_CommandService.js
|
||||
CliClient/tests/services_InteropService.js
|
||||
CliClient/tests/services_keychainService.js
|
||||
CliClient/tests/services_PluginService.js
|
||||
CliClient/tests/services_rest_Api.js
|
||||
CliClient/tests/services/plugins/api/JoplinSetting.js
|
||||
|
1
.ignore
1
.ignore
@ -11,6 +11,7 @@ CliClient/tests/InMemoryCache.js
|
||||
CliClient/tests/models_Setting.js
|
||||
CliClient/tests/services_CommandService.js
|
||||
CliClient/tests/services_InteropService.js
|
||||
CliClient/tests/services_keychainService.js
|
||||
CliClient/tests/services_PluginService.js
|
||||
CliClient/tests/services_rest_Api.js
|
||||
CliClient/tests/services/plugins/api/JoplinSetting.js
|
||||
|
@ -1,60 +0,0 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const shim = require('lib/shim').default;
|
||||
const Setting = require('lib/models/Setting').default;
|
||||
const KeychainService = require('lib/services/keychain/KeychainService').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
function describeIfCompatible(name, fn) {
|
||||
if (['win32', 'darwin'].includes(shim.platformName())) {
|
||||
return describe(name, fn);
|
||||
}
|
||||
}
|
||||
|
||||
describeIfCompatible('services_KeychainService', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1, { keychainEnabled: true });
|
||||
await switchClient(1, { keychainEnabled: true });
|
||||
await Setting.deleteKeychainPasswords();
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(async (done) => {
|
||||
await Setting.deleteKeychainPasswords();
|
||||
done();
|
||||
});
|
||||
|
||||
it('should be enabled on macOS and Windows', asyncTest(async () => {
|
||||
expect(Setting.value('keychain.supported')).toBe(1);
|
||||
}));
|
||||
|
||||
it('should set, get and delete passwords', asyncTest(async () => {
|
||||
const service = KeychainService.instance();
|
||||
|
||||
const isSet = await service.setPassword('zz_testunit', 'password');
|
||||
expect(isSet).toBe(true);
|
||||
|
||||
const password = await service.password('zz_testunit');
|
||||
expect(password).toBe('password');
|
||||
|
||||
await service.deletePassword('zz_testunit');
|
||||
|
||||
expect(await service.password('zz_testunit')).toBe(null);
|
||||
}));
|
||||
|
||||
it('should save and load secure settings', asyncTest(async () => {
|
||||
Setting.setObjectValue('encryption.passwordCache', 'testing', '123456');
|
||||
await Setting.saveAll();
|
||||
await Setting.load();
|
||||
const passwords = Setting.value('encryption.passwordCache');
|
||||
expect(passwords.testing).toBe('123456');
|
||||
}));
|
||||
|
||||
});
|
84
CliClient/tests/services_keychainService.ts
Normal file
84
CliClient/tests/services_keychainService.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import KeychainService from 'lib/services/keychain/KeychainService';
|
||||
import shim from 'lib/shim';
|
||||
import Setting from 'lib/models/Setting';
|
||||
|
||||
const { db, asyncTest, setupDatabaseAndSynchronizer, switchClient } = require('test-utils.js');
|
||||
|
||||
function describeIfCompatible(name:string, fn:any) {
|
||||
if (['win32', 'darwin'].includes(shim.platformName())) {
|
||||
return describe(name, fn);
|
||||
}
|
||||
}
|
||||
|
||||
describeIfCompatible('services_KeychainService', function() {
|
||||
|
||||
beforeEach(async (done:Function) => {
|
||||
await setupDatabaseAndSynchronizer(1, { keychainEnabled: true });
|
||||
await switchClient(1, { keychainEnabled: true });
|
||||
await Setting.deleteKeychainPasswords();
|
||||
done();
|
||||
});
|
||||
|
||||
afterEach(async (done:Function) => {
|
||||
await Setting.deleteKeychainPasswords();
|
||||
done();
|
||||
});
|
||||
|
||||
it('should be enabled on macOS and Windows', asyncTest(async () => {
|
||||
expect(Setting.value('keychain.supported')).toBe(1);
|
||||
}));
|
||||
|
||||
it('should set, get and delete passwords', asyncTest(async () => {
|
||||
const service = KeychainService.instance();
|
||||
|
||||
const isSet = await service.setPassword('zz_testunit', 'password');
|
||||
expect(isSet).toBe(true);
|
||||
|
||||
const password = await service.password('zz_testunit');
|
||||
expect(password).toBe('password');
|
||||
|
||||
await service.deletePassword('zz_testunit');
|
||||
|
||||
expect(await service.password('zz_testunit')).toBe(null);
|
||||
}));
|
||||
|
||||
it('should save and load secure settings', asyncTest(async () => {
|
||||
Setting.setObjectValue('encryption.passwordCache', 'testing', '123456');
|
||||
await Setting.saveAll();
|
||||
await Setting.load();
|
||||
const passwords = Setting.value('encryption.passwordCache');
|
||||
expect(passwords.testing).toBe('123456');
|
||||
}));
|
||||
|
||||
it('should delete db settings if they have been saved in keychain', asyncTest(async () => {
|
||||
// First save some secure settings and make sure it ends up in the databse
|
||||
KeychainService.instance().enabled = false;
|
||||
|
||||
Setting.setValue('sync.5.password', 'password');
|
||||
await Setting.saveAll();
|
||||
|
||||
{
|
||||
// Check that it is in the database
|
||||
const row = await db().selectOne('SELECT * FROM settings WHERE key = "sync.5.password"');
|
||||
expect(row.value).toBe('password');
|
||||
}
|
||||
|
||||
KeychainService.instance().enabled = true;
|
||||
|
||||
// Change any setting to make sure a save operation is triggered
|
||||
Setting.setValue('sync.5.path', '/tmp');
|
||||
|
||||
// Save the settings - now db secure keys should have been cleared and moved to keychain
|
||||
await Setting.saveAll();
|
||||
|
||||
{
|
||||
// Check that it's been removed from the database
|
||||
const row = await db().selectOne('SELECT * FROM settings WHERE key = "sync.5.password"');
|
||||
expect(row).toBe(undefined);
|
||||
}
|
||||
|
||||
// However we should still get it via the Setting class, since it will use the keychain
|
||||
expect(Setting.value('sync.5.password')).toBe('password');
|
||||
}));
|
||||
|
||||
});
|
@ -6,33 +6,51 @@ export default class KeychainService extends BaseService {
|
||||
|
||||
private driver:KeychainServiceDriverBase;
|
||||
private static instance_:KeychainService;
|
||||
private enabled_:boolean = true;
|
||||
|
||||
static instance():KeychainService {
|
||||
if (!this.instance_) this.instance_ = new KeychainService();
|
||||
return this.instance_;
|
||||
}
|
||||
|
||||
initialize(driver:KeychainServiceDriverBase) {
|
||||
public initialize(driver:KeychainServiceDriverBase) {
|
||||
if (!driver.appId || !driver.clientId) throw new Error('appId and clientId must be set on the KeychainServiceDriver');
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
async setPassword(name:string, password:string):Promise<boolean> {
|
||||
// This is to programatically disable the keychain service, regardless whether keychain
|
||||
// is supported or not in the system (In other word, this might "enabled" but nothing
|
||||
// will be saved to the keychain if there isn't one).
|
||||
public get enabled():boolean {
|
||||
return this.enabled_;
|
||||
}
|
||||
|
||||
public set enabled(v:boolean) {
|
||||
this.enabled_ = v;
|
||||
}
|
||||
|
||||
public async setPassword(name:string, password:string):Promise<boolean> {
|
||||
if (!this.enabled) return false;
|
||||
|
||||
// Due to a bug in macOS, this may throw an exception "The user name or passphrase you entered is not correct."
|
||||
// The fix is to open Keychain Access.app. Right-click on the login keychain and try locking it and then unlocking it again.
|
||||
// https://github.com/atom/node-keytar/issues/76
|
||||
return this.driver.setPassword(name, password);
|
||||
}
|
||||
|
||||
async password(name:string):Promise<string> {
|
||||
public async password(name:string):Promise<string> {
|
||||
if (!this.enabled) return null;
|
||||
|
||||
return this.driver.password(name);
|
||||
}
|
||||
|
||||
async deletePassword(name:string):Promise<void> {
|
||||
public async deletePassword(name:string):Promise<void> {
|
||||
if (!this.enabled) return;
|
||||
|
||||
await this.driver.deletePassword(name);
|
||||
}
|
||||
|
||||
async detectIfKeychainSupported() {
|
||||
public async detectIfKeychainSupported() {
|
||||
this.logger().info('KeychainService: checking if keychain supported');
|
||||
|
||||
if (Setting.value('keychain.supported') >= 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user