From 305f360b4029011d5acfe94237f6ea40ede08619 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Tue, 3 May 2022 17:29:00 -0300 Subject: [PATCH] [FEAT]: Request current passcode before enter "Screen lock" screen (#4052) * move auth to handleLocalAuthentication function * add support to close ScreenLockedView * create useServer hook * add check to verify if you have a password before entering the screen * fix import --- app/lib/methods/useServer.ts | 29 ++++++++++++++++++++++ app/utils/localAuthentication.ts | 34 +++++++++++++++----------- app/views/ScreenLockedView.tsx | 40 ++++++++++++++++++++++++++----- app/views/SecurityPrivacyView.tsx | 40 +++++++++++++++++++------------ 4 files changed, 108 insertions(+), 35 deletions(-) create mode 100644 app/lib/methods/useServer.ts diff --git a/app/lib/methods/useServer.ts b/app/lib/methods/useServer.ts new file mode 100644 index 00000000..c282a5cf --- /dev/null +++ b/app/lib/methods/useServer.ts @@ -0,0 +1,29 @@ +import { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; + +import { IApplicationState, TServerModel } from '../../definitions'; +import database from '../database'; + +export default function useServer() { + const [server, setServer] = useState(null); + const shareServer = useSelector((state: IApplicationState) => state.share.server.server); + const appServer = useSelector((state: IApplicationState) => state.server.server); + + useEffect(() => { + async function init() { + const serversDB = database.servers; + const serversCollection = serversDB.get('servers'); + let serverInfo = null; + try { + serverInfo = await serversCollection.find(shareServer || appServer); + setServer(serverInfo); + } catch { + setServer(serverInfo); + } + } + + init(); + }, []); + + return [server]; +} diff --git a/app/utils/localAuthentication.ts b/app/utils/localAuthentication.ts index f094eee8..ec72efbb 100644 --- a/app/utils/localAuthentication.ts +++ b/app/utils/localAuthentication.ts @@ -50,11 +50,13 @@ export const saveLastLocalAuthenticationSession = async ( export const resetAttempts = (): Promise => AsyncStorage.multiRemove([LOCKED_OUT_TIMER_KEY, ATTEMPTS_KEY]); -const openModal = (hasBiometry: boolean) => - new Promise(resolve => { +const openModal = (hasBiometry: boolean, force?: boolean) => + new Promise((resolve, reject) => { EventEmitter.emit(LOCAL_AUTHENTICATE_EMITTER, { submit: () => resolve(), - hasBiometry + hasBiometry, + force, + cancel: () => reject() }); }); @@ -100,6 +102,20 @@ export const checkHasPasscode = async ({ force = true }: { force?: boolean }): P return Promise.resolve(); }; +export const handleLocalAuthentication = async (canCloseModal = false) => { + // let hasBiometry = false; + let hasBiometry = UserPreferences.getBool(BIOMETRY_ENABLED_KEY) ?? false; + + // if biometry is enabled on the app + if (hasBiometry) { + const isEnrolled = await LocalAuthentication.isEnrolledAsync(); + hasBiometry = isEnrolled; + } + + // Authenticate + await openModal(hasBiometry, canCloseModal); +}; + export const localAuthenticate = async (server: string): Promise => { const serversDB = database.servers; const serversCollection = serversDB.get('servers'); @@ -136,17 +152,7 @@ export const localAuthenticate = async (server: string): Promise => { // set isLocalAuthenticated to false store.dispatch(setLocalAuthenticated(false)); - // let hasBiometry = false; - let hasBiometry = UserPreferences.getBool(BIOMETRY_ENABLED_KEY) ?? false; - - // if biometry is enabled on the app - if (hasBiometry) { - const isEnrolled = await LocalAuthentication.isEnrolledAsync(); - hasBiometry = isEnrolled; - } - - // Authenticate - await openModal(hasBiometry); + await handleLocalAuthentication(); // set isLocalAuthenticated to true store.dispatch(setLocalAuthenticated(true)); diff --git a/app/views/ScreenLockedView.tsx b/app/views/ScreenLockedView.tsx index b4306688..7cf21abe 100644 --- a/app/views/ScreenLockedView.tsx +++ b/app/views/ScreenLockedView.tsx @@ -1,22 +1,37 @@ -import React, { useEffect, useState } from 'react'; -import Modal from 'react-native-modal'; -import useDeepCompareEffect from 'use-deep-compare-effect'; import isEmpty from 'lodash/isEmpty'; +import React, { useEffect, useState } from 'react'; +import { StyleSheet } from 'react-native'; +import Modal from 'react-native-modal'; import Orientation from 'react-native-orientation-locker'; +import Touchable from 'react-native-platform-touchable'; +import useDeepCompareEffect from 'use-deep-compare-effect'; -import EventEmitter from '../utils/events'; -import { LOCAL_AUTHENTICATE_EMITTER } from '../lib/constants'; -import { isTablet } from '../utils/deviceInfo'; import { PasscodeEnter } from '../containers/Passcode'; +import { LOCAL_AUTHENTICATE_EMITTER } from '../lib/constants'; +import { CustomIcon } from '../containers/CustomIcon'; +import { useTheme } from '../theme'; +import { hasNotch, isTablet } from '../utils/deviceInfo'; +import EventEmitter from '../utils/events'; interface IData { submit?: () => void; + cancel?: () => void; hasBiometry?: boolean; + force?: boolean; } +const styles = StyleSheet.create({ + close: { + position: 'absolute', + top: hasNotch ? 50 : 30, + left: 15 + } +}); + const ScreenLockedView = (): JSX.Element => { const [visible, setVisible] = useState(false); const [data, setData] = useState({}); + const { colors } = useTheme(); useDeepCompareEffect(() => { if (!isEmpty(data)) { @@ -51,6 +66,14 @@ const ScreenLockedView = (): JSX.Element => { setData({}); }; + const onCancel = () => { + const { cancel } = data; + if (cancel) { + cancel(); + } + setData({}); + }; + return ( { animationIn='fadeIn' animationOut='fadeOut'> + {data?.force ? ( + + + + ) : null} ); }; diff --git a/app/views/SecurityPrivacyView.tsx b/app/views/SecurityPrivacyView.tsx index f632ce55..5487819d 100644 --- a/app/views/SecurityPrivacyView.tsx +++ b/app/views/SecurityPrivacyView.tsx @@ -1,24 +1,26 @@ +import AsyncStorage from '@react-native-community/async-storage'; +import { StackNavigationProp } from '@react-navigation/stack'; import React, { useEffect, useState } from 'react'; import { Switch } from 'react-native'; -import { StackNavigationProp } from '@react-navigation/stack'; -import AsyncStorage from '@react-native-community/async-storage'; import { useSelector } from 'react-redux'; -import StatusBar from '../containers/StatusBar'; import * as List from '../containers/List'; -import I18n from '../i18n'; -import { - logEvent, - events, - toggleCrashErrorsReport, - toggleAnalyticsEventsReport, - getReportCrashErrorsValue, - getReportAnalyticsEventsValue -} from '../utils/log'; import SafeAreaView from '../containers/SafeAreaView'; -import { ANALYTICS_EVENTS_KEY, CRASH_REPORT_KEY, isFDroidBuild, SWITCH_TRACK_COLOR } from '../lib/constants'; -import { SettingsStackParamList } from '../stacks/types'; +import StatusBar from '../containers/StatusBar'; import { IApplicationState } from '../definitions'; +import I18n from '../i18n'; +import { ANALYTICS_EVENTS_KEY, CRASH_REPORT_KEY, isFDroidBuild, SWITCH_TRACK_COLOR } from '../lib/constants'; +import useServer from '../lib/methods/useServer'; +import { SettingsStackParamList } from '../stacks/types'; +import { handleLocalAuthentication } from '../utils/localAuthentication'; +import { + events, + getReportAnalyticsEventsValue, + getReportCrashErrorsValue, + logEvent, + toggleAnalyticsEventsReport, + toggleCrashErrorsReport +} from '../utils/log'; interface ISecurityPrivacyViewProps { navigation: StackNavigationProp; @@ -27,6 +29,7 @@ interface ISecurityPrivacyViewProps { const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Element => { const [crashReportState, setCrashReportState] = useState(getReportCrashErrorsValue()); const [analyticsEventsState, setAnalyticsEventsState] = useState(getReportAnalyticsEventsValue()); + const [server] = useServer(); const e2eEnabled = useSelector((state: IApplicationState) => state.settings.E2E_Enable); @@ -56,6 +59,13 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele navigation.navigate(screen); }; + const navigateToScreenLockConfigView = async () => { + if (server?.autoLock) { + await handleLocalAuthentication(true); + } + navigateToScreen('ScreenLockConfigView'); + }; + return ( @@ -76,7 +86,7 @@ const SecurityPrivacyView = ({ navigation }: ISecurityPrivacyViewProps): JSX.Ele navigateToScreen('ScreenLockConfigView')} + onPress={navigateToScreenLockConfigView} testID='security-privacy-view-screen-lock' />