mirror of
https://github.com/balzack/databag.git
synced 2025-02-15 21:19:16 +00:00
adding e2e key management in mobile app
This commit is contained in:
parent
32e2479793
commit
4a8c2f1776
@ -669,7 +669,7 @@ SPEC CHECKSUMS:
|
|||||||
FirebaseInstallations: 99d24bac0243cf8b0e96cf5426340d211f0bcc80
|
FirebaseInstallations: 99d24bac0243cf8b0e96cf5426340d211f0bcc80
|
||||||
FirebaseMessaging: 4487bbff9b9b927ba1dd3ea40d1ceb58e4ee3cb5
|
FirebaseMessaging: 4487bbff9b9b927ba1dd3ea40d1ceb58e4ee3cb5
|
||||||
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
||||||
glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a
|
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
|
||||||
GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f
|
GoogleDataTransport: 1c8145da7117bd68bbbed00cf304edb6a24de00f
|
||||||
GoogleUtilities: 1d20a6ad97ef46f67bbdec158ce00563a671ebb7
|
GoogleUtilities: 1d20a6ad97ef46f67bbdec158ce00563a671ebb7
|
||||||
nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431
|
nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431
|
||||||
|
7
app/mobile/src/api/setAccountSeal.js
Normal file
7
app/mobile/src/api/setAccountSeal.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||||
|
|
||||||
|
export async function setAccountSeal(server, token, seal) {
|
||||||
|
let res = await fetchWithTimeout(`https://${server}/account/seal?agent=${token}`, { method: 'PUT', body: JSON.stringify(seal) })
|
||||||
|
checkResponse(res);
|
||||||
|
}
|
||||||
|
|
@ -48,8 +48,9 @@ export function useAccountContext() {
|
|||||||
setSession: async (access) => {
|
setSession: async (access) => {
|
||||||
const { guid, server, appToken } = access;
|
const { guid, server, appToken } = access;
|
||||||
const status = await store.actions.getAccountStatus(guid);
|
const status = await store.actions.getAccountStatus(guid);
|
||||||
|
const sealKey = await store.actions.getAccountSealKey(guid);
|
||||||
const revision = await store.actions.getAccountRevision(guid);
|
const revision = await store.actions.getAccountRevision(guid);
|
||||||
updateState({ status });
|
updateState({ status, sealKey });
|
||||||
setRevision.current = revision;
|
setRevision.current = revision;
|
||||||
curRevision.current = revision;
|
curRevision.current = revision;
|
||||||
session.current = access;
|
session.current = access;
|
||||||
@ -70,6 +71,16 @@ export function useAccountContext() {
|
|||||||
const { server, appToken } = session.current;
|
const { server, appToken } = session.current;
|
||||||
await setAccountSearchable(server, appToken, flag);
|
await setAccountSearchable(server, appToken, flag);
|
||||||
},
|
},
|
||||||
|
setAccountSeal: async (seal, key) => {
|
||||||
|
const { guid, server, appToken } = session.current;
|
||||||
|
await setAccountSeal(server, appToken, seal);
|
||||||
|
await store.actions.setAccountSealKey(guid, key);
|
||||||
|
updateState({ sealKey: key });
|
||||||
|
},
|
||||||
|
unlockAccountSeal: async (key) => {
|
||||||
|
await store.actions.setAccountSealKey(guid, key);
|
||||||
|
updateState({ sealKey: key });
|
||||||
|
},
|
||||||
setLogin: async (username, password) => {
|
setLogin: async (username, password) => {
|
||||||
const { server, appToken } = session.current;
|
const { server, appToken } = session.current;
|
||||||
await setAccountLogin(server, appToken, username, password);
|
await setAccountLogin(server, appToken, username, password);
|
||||||
|
@ -76,6 +76,14 @@ export function useStoreContext() {
|
|||||||
const dataId = `${guid}_status`;
|
const dataId = `${guid}_status`;
|
||||||
await db.current.executeSql("INSERT OR REPLACE INTO app (key, value) values (?, ?);", [dataId, encodeObject(status)]);
|
await db.current.executeSql("INSERT OR REPLACE INTO app (key, value) values (?, ?);", [dataId, encodeObject(status)]);
|
||||||
},
|
},
|
||||||
|
getAccountSealKey: async (guid) => {
|
||||||
|
const dataId = `${guid}_sealkey`;
|
||||||
|
return await getAppValue(db.current, dataId, {});
|
||||||
|
},
|
||||||
|
setAccountSealKey: async (guid, key) => {
|
||||||
|
const dataId = `${guid}_sealkey`;
|
||||||
|
await db.current.executeSql("INSERT OR REPLACE INTO app (key, value) values (?, ?);", [dataId, encodeObject(key)]);
|
||||||
|
},
|
||||||
getAccountRevision: async (guid) => {
|
getAccountRevision: async (guid) => {
|
||||||
const dataId = `${guid}_accountRevision`;
|
const dataId = `${guid}_accountRevision`;
|
||||||
return await getAppValue(db.current, dataId, null);
|
return await getAppValue(db.current, dataId, null);
|
||||||
|
@ -143,10 +143,12 @@ export function ProfileBody({ navigation }) {
|
|||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Switch style={styles.visibleSwitch} value={state.pushEnabled} onValueChange={setNotifications} trackColor={styles.switch}/>
|
<Switch style={styles.visibleSwitch} value={state.pushEnabled} onValueChange={setNotifications} trackColor={styles.switch}/>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity style={styles.link} onPress={actions.sealTest}>
|
<TouchableOpacity style={styles.link} onPress={actions.showSealEdit}>
|
||||||
<Text style={styles.linkText}>Test Seal</Text>
|
<Ionicons name="setting" size={14} color={Colors.primary} />
|
||||||
|
<Text style={styles.linkText}>Sealed Topics</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity style={styles.link} onPress={actions.showLoginEdit}>
|
<TouchableOpacity style={styles.link} onPress={actions.showLoginEdit}>
|
||||||
|
<Ionicons name="lock" size={14} color={Colors.primary} />
|
||||||
<Text style={styles.linkText}>Change Login</Text>
|
<Text style={styles.linkText}>Change Login</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity style={styles.link} onPress={actions.showBlockedCards}>
|
<TouchableOpacity style={styles.link} onPress={actions.showBlockedCards}>
|
||||||
@ -256,6 +258,128 @@ export function ProfileBody({ navigation }) {
|
|||||||
</View>
|
</View>
|
||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
animationType="fade"
|
||||||
|
transparent={true}
|
||||||
|
visible={state.sealEdit}
|
||||||
|
supportedOrientations={['portrait', 'landscape']}
|
||||||
|
onRequestClose={actions.hideSealEdit}
|
||||||
|
>
|
||||||
|
<KeyboardAvoidingView behavior="height" style={styles.editWrapper}>
|
||||||
|
<View style={styles.editContainer}>
|
||||||
|
<Text style={styles.editHeader}>Sealed Topics:</Text>
|
||||||
|
<View style={styles.sealable}>
|
||||||
|
<TouchableOpacity onPress={() => actions.setSealable(!state.sealable)} activeOpacity={1}>
|
||||||
|
<Text style={styles.sealableText}>Enable Sealed Topics</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Switch style={styles.sealableSwitch} value={state.sealable} onValueChange={actions.setSealable} trackColor={styles.switch}/>
|
||||||
|
</View>
|
||||||
|
{ state.sealMode === 'unlocking' && (
|
||||||
|
<>
|
||||||
|
{ !state.showSealUnlock && (
|
||||||
|
<View style={styles.inputField}>
|
||||||
|
<TextInput style={styles.input} value={state.sealUnlock} onChangeText={actions.setSealUnlock}
|
||||||
|
autoCapitalize={'none'} secureTextEntry={true} placeholder="Seal Password"
|
||||||
|
placeholderTextColor={Colors.grey} />
|
||||||
|
<TouchableOpacity onPress={actions.showSealUnlock}>
|
||||||
|
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{ state.showSealUnlock && (
|
||||||
|
<View style={styles.inputField}>
|
||||||
|
<TextInput style={styles.input} value={state.sealUnlock} onChangeText={actions.setSealUnlock}
|
||||||
|
autoCapitalize={'none'} secureTextEntry={false} placeholder="Seal Password"
|
||||||
|
placeholderTextColor={Colors.grey} />
|
||||||
|
<TouchableOpacity onPress={actions.hideSealUnlock}>
|
||||||
|
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{ (state.sealMode === 'updating' || state.sealMode === 'enabling') && (
|
||||||
|
<>
|
||||||
|
{ !state.showSealPassword && (
|
||||||
|
<View style={styles.inputField}>
|
||||||
|
<TextInput style={styles.input} value={state.sealPassword} onChangeText={actions.setSealPassword}
|
||||||
|
autoCapitalize={'none'} secureTextEntry={true} placeholder="Password Seal"
|
||||||
|
placeholderTextColor={Colors.grey} />
|
||||||
|
<TouchableOpacity onPress={actions.showSealPassword}>
|
||||||
|
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{ state.showSealPassword && (
|
||||||
|
<View style={styles.inputField}>
|
||||||
|
<TextInput style={styles.input} value={state.sealPassword} onChangeText={actions.setSealPassword}
|
||||||
|
autoCapitalize={'none'} secureTextEntry={false} placeholder="Password Seal"
|
||||||
|
placeholderTextColor={Colors.grey} />
|
||||||
|
<TouchableOpacity onPress={actions.hideSealPassword}>
|
||||||
|
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{ !state.showSealConfirm && (
|
||||||
|
<View style={styles.inputField}>
|
||||||
|
<TextInput style={styles.input} value={state.sealConfirm} onChangeText={actions.setSealConfirm}
|
||||||
|
autoCapitalize={'none'} secureTextEntry={true} placeholder="Confirm Seal"
|
||||||
|
placeholderTextColor={Colors.grey} />
|
||||||
|
<TouchableOpacity onPress={actions.showSealConfirm}>
|
||||||
|
<Ionicons style={styles.icon} name="eyeo" size={18} color="#888888" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{ state.showSealConfirm && (
|
||||||
|
<View style={styles.inputField}>
|
||||||
|
<TextInput style={styles.input} value={state.sealConfirm} onChangeText={actions.setSealConfirm}
|
||||||
|
autoCapitalize={'none'} secureTextEntry={false} placeholder="Confirm Seal"
|
||||||
|
placeholderTextColor={Colors.grey} />
|
||||||
|
<TouchableOpacity onPress={actions.hideSealConfirm}>
|
||||||
|
<Ionicons style={styles.icon} name="eye" size={18} color="#888888" />
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{ state.sealMode === 'disabling' && (
|
||||||
|
<View style={styles.inputField}>
|
||||||
|
<Ionicons style={styles.warn} name="exclamationcircleo" size={18} color="#888888" />
|
||||||
|
<TextInput style={styles.input} value={state.sealDelete} onChangeText={actions.setSealDelete}
|
||||||
|
autoCapitalize={'none'} placeholder="Type 'delete' to remove sealing key"
|
||||||
|
placeholderTextColor={Colors.grey} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{ state.sealMode === 'unlocked' && (
|
||||||
|
<View style={styles.inputField}>
|
||||||
|
<TextInput style={styles.input} value={'xxxxxxxx'} editable="false" secureTextEntry={true} />
|
||||||
|
<TouchableOpacity style={styles.sealUpdate} onPress={actions.updateSeal} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<View style={styles.editControls}>
|
||||||
|
<TouchableOpacity style={styles.cancel} onPress={actions.hideSealEdit}>
|
||||||
|
<Text>Cancel</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
{ state.sealMode !== 'unlocking' && (
|
||||||
|
<TouchableOpacity style={styles.cancel} onPress={actions.hideSealEdit}>
|
||||||
|
<Text>Save</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
{ state.sealMode === 'unlocking' && (
|
||||||
|
<TouchableOpacity style={styles.cancel} onPress={actions.hideSealEdit}>
|
||||||
|
<Text>Unlock</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</KeyboardAvoidingView>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
animationType="fade"
|
animationType="fade"
|
||||||
transparent={true}
|
transparent={true}
|
||||||
|
@ -14,6 +14,10 @@ export const styles = StyleSheet.create({
|
|||||||
icon: {
|
icon: {
|
||||||
paddingTop: 2,
|
paddingTop: 2,
|
||||||
},
|
},
|
||||||
|
warn: {
|
||||||
|
paddingTop: 2,
|
||||||
|
paddingRight: 8,
|
||||||
|
},
|
||||||
wrapper: {
|
wrapper: {
|
||||||
backgroundColor: Colors.formBackground,
|
backgroundColor: Colors.formBackground,
|
||||||
},
|
},
|
||||||
@ -133,6 +137,27 @@ export const styles = StyleSheet.create({
|
|||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: Colors.text,
|
color: Colors.text,
|
||||||
},
|
},
|
||||||
|
sealUpdate: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
height: '100%',
|
||||||
|
left: 0,
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
sealable: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingBottom: 8,
|
||||||
|
},
|
||||||
|
sealableText: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.text,
|
||||||
|
},
|
||||||
|
sealableSwitch: {
|
||||||
|
transform: [{ scaleX: .7 }, { scaleY: .7 }],
|
||||||
|
},
|
||||||
visible: {
|
visible: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@ -282,8 +307,11 @@ export const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
link: {
|
link: {
|
||||||
marginTop: 16,
|
marginTop: 16,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
},
|
},
|
||||||
linkText: {
|
linkText: {
|
||||||
|
paddingLeft: 8,
|
||||||
color: Colors.primary,
|
color: Colors.primary,
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
|
@ -36,6 +36,22 @@ export function useProfileBody() {
|
|||||||
blockedMessages: false,
|
blockedMessages: false,
|
||||||
tabbed: null,
|
tabbed: null,
|
||||||
disconnected: false,
|
disconnected: false,
|
||||||
|
|
||||||
|
seal: null,
|
||||||
|
sealKey: null,
|
||||||
|
sealEnabled: false,
|
||||||
|
sealUnlocked: false,
|
||||||
|
|
||||||
|
sealEdit: false,
|
||||||
|
sealMode: null,
|
||||||
|
sealable: false,
|
||||||
|
sealUnlock: null,
|
||||||
|
showSealUnlock: false,
|
||||||
|
sealPassword: null,
|
||||||
|
showSealPassword: false,
|
||||||
|
sealConfirm: null,
|
||||||
|
showSealConfirm: false,
|
||||||
|
sealDelete: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const app = useContext(AppContext);
|
const app = useContext(AppContext);
|
||||||
@ -66,8 +82,11 @@ export function useProfileBody() {
|
|||||||
}, [profile]);
|
}, [profile]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { searchable, pushEnabled } = account.state.status;
|
const { searchable, pushEnabled, seal } = account.state.status;
|
||||||
updateState({ searchable, pushEnabled });
|
const sealKey = account.state.sealKey;
|
||||||
|
const sealEnabled = seal?.publicKey != null;
|
||||||
|
const sealUnlocked = seal?.publicKey === sealKey?.public && sealKey?.private && sealKey?.public;
|
||||||
|
updateState({ searchable, pushEnabled, seal, sealKey, sealEnabled, sealUnlocked });
|
||||||
}, [account]);
|
}, [account]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -122,6 +141,79 @@ export function useProfileBody() {
|
|||||||
console.log("SEAL:", seal);
|
console.log("SEAL:", seal);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
showSealEdit: () => {
|
||||||
|
let sealMode = null;
|
||||||
|
const sealable = state.sealEnabled;
|
||||||
|
if (state.sealEnabled && !state.sealUnlocked) {
|
||||||
|
sealMode = 'unlocking';
|
||||||
|
}
|
||||||
|
else if (state.sealEnabled && state.sealUnlocked) {
|
||||||
|
sealMode = 'unlocked';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sealMode = 'disabled';
|
||||||
|
}
|
||||||
|
updateState({ sealEdit: true, sealable, sealMode });
|
||||||
|
},
|
||||||
|
hideSealEdit: () => {
|
||||||
|
updateState({ sealEdit: false });
|
||||||
|
},
|
||||||
|
setSealable: (sealable) => {
|
||||||
|
let sealMode = null;
|
||||||
|
if (sealable !== state.sealEnabled) {
|
||||||
|
if (sealable) {
|
||||||
|
sealMode = 'enabling';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sealMode = 'disabling';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (state.sealEnabled && !state.sealUnlocked) {
|
||||||
|
sealMode = 'unlocking';
|
||||||
|
}
|
||||||
|
else if (state.sealEnabled && state.sealUnlocked) {
|
||||||
|
sealMode = 'unlocked';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sealMode = 'disabled';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateState({ sealable, sealMode });
|
||||||
|
},
|
||||||
|
showSealUnlock: () => {
|
||||||
|
updateState({ showSealUnlock: true });
|
||||||
|
},
|
||||||
|
hideSealUnlock: () => {
|
||||||
|
updateState({ showSealUnlock: false });
|
||||||
|
},
|
||||||
|
setSealUnlock: (sealUnlock) => {
|
||||||
|
updateState({ sealUnlock });
|
||||||
|
},
|
||||||
|
showSealPassword: () => {
|
||||||
|
updateState({ showSealPassword: true });
|
||||||
|
},
|
||||||
|
hideSealPassword: () => {
|
||||||
|
updateState({ showSealPassword: false });
|
||||||
|
},
|
||||||
|
setSealPassword: (sealPassword) => {
|
||||||
|
updateState({ sealPassword });
|
||||||
|
},
|
||||||
|
showSealConfirm: () => {
|
||||||
|
updateState({ showSealConfirm: true });
|
||||||
|
},
|
||||||
|
hideSealConfirm: () => {
|
||||||
|
updateState({ showSealConfirm: false });
|
||||||
|
},
|
||||||
|
setSealConfirm: (sealConfirm) => {
|
||||||
|
updateState({ sealConfirm });
|
||||||
|
},
|
||||||
|
setSealDelete: (sealDelete) => {
|
||||||
|
updateState({ sealDelete });
|
||||||
|
},
|
||||||
|
updateSeal: () => {
|
||||||
|
updateState({ sealMode: 'updating' });
|
||||||
|
},
|
||||||
logout: () => {
|
logout: () => {
|
||||||
app.actions.logout();
|
app.actions.logout();
|
||||||
navigate('/');
|
navigate('/');
|
||||||
|
Loading…
Reference in New Issue
Block a user