Add password setting and check password before deleting it

This commit is contained in:
Julia Friesel
2018-09-13 20:42:24 +02:00
parent 88fc3cad09
commit b153f8d0ff
5 changed files with 144 additions and 29 deletions
+6
View File
@@ -48,6 +48,12 @@ export const settings = {
noTimeSet: 'Set a time for a daily reminder to take your temperature', noTimeSet: 'Set a time for a daily reminder to take your temperature',
timeSet: time => `Daily reminder set for ${time}`, timeSet: time => `Daily reminder set for ${time}`,
notification: 'Record your morning temperature' 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"
} }
} }
+15 -23
View File
@@ -5,7 +5,7 @@ import { AppText } from './app-text'
import { hasEncryptionObservable } from '../local-storage' import { hasEncryptionObservable } from '../local-storage'
import styles from '../styles' import styles from '../styles'
import { passwordPrompt } from './labels' import { passwordPrompt } from './labels'
import { openDbConnection, requestHash, deleteDbAndOpenNew } from '../db' import { openDbConnection, requestHash, deleteDbAndOpenNew, openDb } from '../db'
import App from './app' import App from './app'
export default class PasswordPrompt extends Component { 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.start('main.js')
nodejs.channel.addListener( nodejs.channel.addListener(
'message', 'message',
this.tryToOpenDb, this.passHashToDb,
this 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() { componentWillUnmount() {
nodejs.channel.removeListener('message', this.tryToOpenDb) nodejs.channel.removeListener('message', this.passHashToDb)
} }
render() { render() {
@@ -71,7 +63,7 @@ export default class PasswordPrompt extends Component {
/> />
<TouchableOpacity <TouchableOpacity
style={styles.settingsButton} style={styles.settingsButton}
onPress={async () => { onPress={() => {
requestHash(this.state.password) requestHash(this.state.password)
}} }}
> >
+4
View File
@@ -11,6 +11,7 @@ import TempReminderPicker from './temp-reminder-picker'
import TempSlider from './temp-slider' import TempSlider from './temp-slider'
import openImportDialogAndImport from './import-dialog' import openImportDialogAndImport from './import-dialog'
import openShareDialogAndExport from './export-dialog' import openShareDialogAndExport from './export-dialog'
import PasswordSetting from './password-setting'
export default class Settings extends Component { export default class Settings extends Component {
constructor(props) { constructor(props) {
@@ -29,6 +30,9 @@ export default class Settings extends Component {
<AppText>{labels.tempScale.segmentExplainer}</AppText> <AppText>{labels.tempScale.segmentExplainer}</AppText>
<TempSlider/> <TempSlider/>
</View> </View>
<View style={styles.settingsSegment}>
<PasswordSetting/>
</View>
<View style={styles.settingsSegment}> <View style={styles.settingsSegment}>
<AppText style={styles.settingsSegmentTitle}> <AppText style={styles.settingsSegmentTitle}>
{labels.export.button} {labels.export.button}
+85
View File
@@ -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 (
<View style={styles.settingsSegment}>
<AppText style={styles.settingsSegmentTitle}>
{labels.passwordSettings.title}
</AppText>
{this.state.enabled ?
<AppText>{labels.passwordSettings.explainerEnabled}</AppText>
:
<AppText>{labels.passwordSettings.explainerDisabled}</AppText>
}
{this.state.enterOldPassword &&
<TextInput
style={{
backgroundColor: 'white',
}}
onChangeText={val => this.setState({ oldPassword: val })}
/>
}
{this.state.wrongPassword && <AppText>Wrong PAssword!</AppText>}
<TouchableOpacity
onPress={() => {
if (!this.state.enterOldPassword) {
this.setState({ enterOldPassword: true })
} else {
requestHash(this.state.oldPassword)
}
}}
style={styles.settingsButton}>
<AppText style={styles.settingsButtonText}>
{labels.passwordSettings.deletePassword}
</AppText>
</TouchableOpacity>
</View>
)
}
}
+34 -6
View File
@@ -19,11 +19,6 @@ const realmConfig = {
schema: dbSchema schema: dbSchema
} }
export async function openDbConnection(key) {
if(key) realmConfig.encryptionKey = key
db = await Realm.open(realmConfig)
}
function getBleedingDaysSortedByDate() { function getBleedingDaysSortedByDate() {
return db.objects('CycleDay').filtered('bleeding != null').sorted('date', true) 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) { async function encryptAndRestartApp(key) {
const oldPath = db.path const oldPath = db.path
const dir = db.path.split('/') const dir = db.path.split('/')
@@ -190,10 +201,27 @@ async function encryptAndRestartApp(key) {
restart.Restart() 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() { async function deleteDbAndOpenNew() {
const exists = await fs.exists(Realm.defaultPath) const exists = await fs.exists(Realm.defaultPath)
if (exists) await fs.unlink(Realm.defaultPath) if (exists) await fs.unlink(Realm.defaultPath)
await openDbConnection() await openDb({ persistConnection: true })
} }
export { export {