From b153f8d0fffdfbd06681ce837be7f888366a4c57 Mon Sep 17 00:00:00 2001 From: Julia Friesel Date: Thu, 13 Sep 2018 20:42:24 +0200 Subject: [PATCH] Add password setting and check password before deleting it --- components/labels.js | 6 ++ components/password-prompt.js | 38 +++++------ components/settings/index.js | 4 ++ components/settings/password-setting.js | 85 +++++++++++++++++++++++++ db/index.js | 40 ++++++++++-- 5 files changed, 144 insertions(+), 29 deletions(-) create mode 100644 components/settings/password-setting.js diff --git a/components/labels.js b/components/labels.js index 5f1e749..7ddbae1 100644 --- a/components/labels.js +++ b/components/labels.js @@ -48,6 +48,12 @@ export const settings = { noTimeSet: 'Set a time for a daily reminder to take your temperature', timeSet: time => `Daily reminder set for ${time}`, notification: 'Record your morning temperature' + }, + passwordSettings: { + title: 'App password', + explainerDisabled: "Encrypt the app's database with a password. You have to enter the password every time the app is started.", + explainerEnabled: "Your app's data is encrypted with your password.", + deletePassword: "Delete password" } } diff --git a/components/password-prompt.js b/components/password-prompt.js index 796e072..e21dba6 100644 --- a/components/password-prompt.js +++ b/components/password-prompt.js @@ -5,7 +5,7 @@ import { AppText } from './app-text' import { hasEncryptionObservable } from '../local-storage' import styles from '../styles' import { passwordPrompt } from './labels' -import { openDbConnection, requestHash, deleteDbAndOpenNew } from '../db' +import { openDbConnection, requestHash, deleteDbAndOpenNew, openDb } from '../db' import App from './app' export default class PasswordPrompt extends Component { @@ -21,35 +21,27 @@ export default class PasswordPrompt extends Component { } }) - this.tryToOpenDb = async (msg) => { - msg = JSON.parse(msg) - if (msg.type === 'sha512') { - const hash = msg.message - const key = new Uint8Array(64) - for (let i = 0; i < key.length; i++) { - const twoDigitHex = hash.slice(i * 2, i * 2 + 2) - key[i] = parseInt(twoDigitHex, 16) - } - try { - await openDbConnection(key) - this.setState({showApp: true}) - } catch (err) { - console.log(err) - this.setState({ wrongPassword: true }) - } - } - } - nodejs.start('main.js') nodejs.channel.addListener( 'message', - this.tryToOpenDb, + this.passHashToDb, this ) } + passHashToDb = async msg => { + msg = JSON.parse(msg) + if (msg.type != 'sha512') return + try { + await openDb({hash: msg.message, persistConnection: true }) + this.setState({ showApp: true }) + } catch (err) { + this.setState({ wrongPassword: true }) + } + } + componentWillUnmount() { - nodejs.channel.removeListener('message', this.tryToOpenDb) + nodejs.channel.removeListener('message', this.passHashToDb) } render() { @@ -71,7 +63,7 @@ export default class PasswordPrompt extends Component { /> { + onPress={() => { requestHash(this.state.password) }} > diff --git a/components/settings/index.js b/components/settings/index.js index 5e729be..c52f3ae 100644 --- a/components/settings/index.js +++ b/components/settings/index.js @@ -11,6 +11,7 @@ import TempReminderPicker from './temp-reminder-picker' import TempSlider from './temp-slider' import openImportDialogAndImport from './import-dialog' import openShareDialogAndExport from './export-dialog' +import PasswordSetting from './password-setting' export default class Settings extends Component { constructor(props) { @@ -29,6 +30,9 @@ export default class Settings extends Component { {labels.tempScale.segmentExplainer} + + + {labels.export.button} diff --git a/components/settings/password-setting.js b/components/settings/password-setting.js new file mode 100644 index 0000000..9fe820b --- /dev/null +++ b/components/settings/password-setting.js @@ -0,0 +1,85 @@ +import React, { Component } from 'react' +import { + View, + TouchableOpacity, + TextInput +} from 'react-native' +import nodejs from 'nodejs-mobile-react-native' +import { AppText } from '../app-text' +import { + hasEncryptionObservable +} from '../../local-storage' +import styles from '../../styles/index' +import { settings as labels } from '../labels' +import { requestHash, openDb } from '../../db' + +export default class PasswordSetting extends Component { + constructor(props) { + super(props) + this.state = { + enabled: hasEncryptionObservable.value + } + + nodejs.start('main.js') + nodejs.channel.addListener( + 'message', + this.passHashToDb, + this + ) + } + + componentWillUnmount() { + nodejs.channel.removeListener('message', this.passHashToDb) + } + + passHashToDb = async (msg) => { + msg = JSON.parse(msg) + if (msg.type != 'sha512') return + try { + await openDb({ hash: msg.message, persistConnection: false }) + this.setState({ + wrongPassword: false, + enterOldPassword: false + }) + } catch (err) { + this.setState({wrongPassword: true}) + } + } + + render() { + return ( + + + {labels.passwordSettings.title} + + {this.state.enabled ? + {labels.passwordSettings.explainerEnabled} + : + {labels.passwordSettings.explainerDisabled} + } + {this.state.enterOldPassword && + this.setState({ oldPassword: val })} + /> + } + {this.state.wrongPassword && Wrong PAssword!} + { + if (!this.state.enterOldPassword) { + this.setState({ enterOldPassword: true }) + } else { + requestHash(this.state.oldPassword) + } + }} + style={styles.settingsButton}> + + {labels.passwordSettings.deletePassword} + + + + ) + } +} \ No newline at end of file diff --git a/db/index.js b/db/index.js index fc096a9..ceac742 100644 --- a/db/index.js +++ b/db/index.js @@ -19,11 +19,6 @@ const realmConfig = { schema: dbSchema } -export async function openDbConnection(key) { - if(key) realmConfig.encryptionKey = key - db = await Realm.open(realmConfig) -} - function getBleedingDaysSortedByDate() { return db.objects('CycleDay').filtered('bleeding != null').sorted('date', true) } @@ -173,6 +168,22 @@ function requestHash(pw) { })) } +export async function openDb ({ hash, persistConnection }) { + if (hash) { + const key = new Uint8Array(64) + for (let i = 0; i < key.length; i++) { + const twoDigitHex = hash.slice(i * 2, i * 2 + 2) + key[i] = parseInt(twoDigitHex, 16) + } + + realmConfig.encryptionKey = key + } + + const connection = await Realm.open(realmConfig) + + if (persistConnection) db = connection +} + async function encryptAndRestartApp(key) { const oldPath = db.path const dir = db.path.split('/') @@ -190,10 +201,27 @@ async function encryptAndRestartApp(key) { restart.Restart() } +export async function removeDbEncryptionAndRestartApp(key) { + const oldPath = db.path + const dir = db.path.split('/') + dir.pop() + dir.push('copied.realm') + const copyPath = dir.join('/') + const exists = await fs.exists(copyPath) + if (exists) await fs.unlink(copyPath) + db.writeCopyTo(copyPath) + db.close() + await fs.unlink(oldPath) + realmConfig.encryptionKey = key + db = new Realm(realmConfig) + await saveEncryptionFlag(true) + restart.Restart() +} + async function deleteDbAndOpenNew() { const exists = await fs.exists(Realm.defaultPath) if (exists) await fs.unlink(Realm.defaultPath) - await openDbConnection() + await openDb({ persistConnection: true }) } export {