You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-11-06 09:19:22 +02:00
This commit is contained in:
21
ReactNativeClient/lib/services/SettingUtils.ts
Normal file
21
ReactNativeClient/lib/services/SettingUtils.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import KeychainService from './keychain/KeychainService';
|
||||
const Setting = require('lib/models/Setting');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
|
||||
// This function takes care of initialising both the keychain service and settings.
|
||||
//
|
||||
// Loading the settings became more complicated with the keychain integration. This is because
|
||||
// the settings needs a keychain service, and the keychain service needs a clientId, which
|
||||
// is set dynamically and saved to the settings.
|
||||
// In other words, it's not possible to load the settings without the KS service and it's not
|
||||
// possible to initialise the KS service without the settings.
|
||||
// The solution is to fetch just the client ID directly from the database.
|
||||
export async function loadKeychainServiceAndSettings(KeychainServiceDriver:any) {
|
||||
const clientIdSetting = await Setting.loadOne('clientId');
|
||||
const clientId = clientIdSetting ? clientIdSetting.value : uuid.create();
|
||||
KeychainService.instance().initialize(new KeychainServiceDriver(Setting.value('appId'), clientId));
|
||||
Setting.setKeychainService(KeychainService.instance());
|
||||
await Setting.load();
|
||||
if (!clientIdSetting) Setting.setValue('clientId', clientId);
|
||||
await KeychainService.instance().detectIfKeychainSupported();
|
||||
}
|
||||
56
ReactNativeClient/lib/services/keychain/KeychainService.ts
Normal file
56
ReactNativeClient/lib/services/keychain/KeychainService.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import KeychainServiceDriverBase from './KeychainServiceDriverBase';
|
||||
const Setting = require('lib/models/Setting');
|
||||
const BaseService = require('lib/services/BaseService');
|
||||
|
||||
export default class KeychainService extends BaseService {
|
||||
|
||||
private driver:KeychainServiceDriverBase;
|
||||
private static instance_:KeychainService;
|
||||
|
||||
static instance():KeychainService {
|
||||
if (!this.instance_) this.instance_ = new KeychainService();
|
||||
return this.instance_;
|
||||
}
|
||||
|
||||
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> {
|
||||
// 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> {
|
||||
return this.driver.password(name);
|
||||
}
|
||||
|
||||
async deletePassword(name:string):Promise<void> {
|
||||
await this.driver.deletePassword(name);
|
||||
}
|
||||
|
||||
async detectIfKeychainSupported() {
|
||||
this.logger().info('KeychainService: checking if keychain supported');
|
||||
|
||||
if (Setting.value('keychain.supported') >= 0) {
|
||||
this.logger().info('KeychainService: check was already done - skipping. Supported:', Setting.value('keychain.supported'));
|
||||
return;
|
||||
}
|
||||
|
||||
const passwordIsSet = await this.setPassword('zz_testingkeychain', 'mytest');
|
||||
|
||||
if (!passwordIsSet) {
|
||||
this.logger().info('KeychainService: could not set test password - keychain support will be disabled');
|
||||
Setting.setValue('keychain.supported', 0);
|
||||
} else {
|
||||
const result = await this.password('zz_testingkeychain');
|
||||
await this.deletePassword('zz_testingkeychain');
|
||||
this.logger().info('KeychainService: tried to set and get password. Result was:', result);
|
||||
Setting.setValue('keychain.supported', result === 'mytest' ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import KeychainServiceDriverBase from './KeychainServiceDriverBase';
|
||||
|
||||
export default class KeychainServiceDriver extends KeychainServiceDriverBase {
|
||||
|
||||
async setPassword(/* name:string, password:string*/):Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async password(/* name:string*/):Promise<string> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async deletePassword(/* name:string*/):Promise<void> {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import KeychainServiceDriverBase from './KeychainServiceDriverBase';
|
||||
|
||||
export default class KeychainServiceDriver extends KeychainServiceDriverBase {
|
||||
|
||||
async setPassword(/* name:string, password:string*/):Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async password(/* name:string*/):Promise<string> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async deletePassword(/* name:string*/):Promise<void> {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import KeychainServiceDriverBase from './KeychainServiceDriverBase';
|
||||
const { shim } = require('lib/shim.js');
|
||||
|
||||
// keytar throws an error when system keychain is not present;
|
||||
// even when keytar itself is installed.
|
||||
// try/catch to ensure system keychain is present and no error is thrown.
|
||||
|
||||
// For now, keychain support is disabled on Linux because when keytar is loaded
|
||||
// it seems to cause the following error when loading Sharp:
|
||||
//
|
||||
// Something went wrong installing the "sharp" module
|
||||
// /lib/x86_64-linux-gnu/libz.so.1: version `ZLIB_1.2.9' not found (required by /home/travis/build/laurent22/joplin/CliClient/node_modules/sharp/build/Release/../../vendor/lib/libpng16.so.16)
|
||||
//
|
||||
// See: https://travis-ci.org/github/laurent22/joplin/jobs/686222036
|
||||
//
|
||||
// Also disabled in portable mode obviously.
|
||||
|
||||
let keytar:any;
|
||||
try {
|
||||
keytar = shim.isLinux() || shim.isPortable() ? null : require('keytar');
|
||||
} catch (error) {
|
||||
console.error('Cannot load keytar - keychain support will be disabled', error);
|
||||
keytar = null;
|
||||
}
|
||||
|
||||
export default class KeychainServiceDriver extends KeychainServiceDriverBase {
|
||||
|
||||
async setPassword(name:string, password:string):Promise<boolean> {
|
||||
if (!keytar) return false;
|
||||
await keytar.setPassword(`${this.appId}.${name}`, `${this.clientId}@joplin`, password);
|
||||
return true;
|
||||
}
|
||||
|
||||
async password(name:string):Promise<string> {
|
||||
if (!keytar) return null;
|
||||
return keytar.getPassword(`${this.appId}.${name}`, `${this.clientId}@joplin`);
|
||||
}
|
||||
|
||||
async deletePassword(name:string):Promise<void> {
|
||||
if (!keytar) return;
|
||||
await keytar.deletePassword(`${this.appId}.${name}`, `${this.clientId}@joplin`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
abstract class KeychainServiceDriverBase {
|
||||
|
||||
private appId_:string;
|
||||
private clientId_:string;
|
||||
|
||||
constructor(appId:string, clientId:string) {
|
||||
this.appId_ = appId;
|
||||
this.clientId_ = clientId;
|
||||
}
|
||||
|
||||
get appId():string {
|
||||
return this.appId_;
|
||||
}
|
||||
|
||||
get clientId():string {
|
||||
return this.clientId_;
|
||||
}
|
||||
|
||||
abstract async setPassword(name:string, password:string):Promise<boolean>;
|
||||
abstract async password(name:string):Promise<string>;
|
||||
abstract async deletePassword(name:string):Promise<void>;
|
||||
|
||||
}
|
||||
|
||||
export default KeychainServiceDriverBase;
|
||||
Reference in New Issue
Block a user