From 5f65d2eca76cf5925aff309e54a73a1760345061 Mon Sep 17 00:00:00 2001 From: Gorjan Petrovski Date: Thu, 23 Sep 2021 17:34:59 +0200 Subject: [PATCH] feat: implement Protections in prefs (#645) --- .../preferences/panes/Security.tsx | 3 +- .../panes/security-segments/Protections.tsx | 93 +++++++++++++++++++ .../panes/security-segments/index.ts | 1 + 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/preferences/panes/security-segments/Protections.tsx diff --git a/app/assets/javascripts/preferences/panes/Security.tsx b/app/assets/javascripts/preferences/panes/Security.tsx index 13d7ba2b0..900940d7a 100644 --- a/app/assets/javascripts/preferences/panes/Security.tsx +++ b/app/assets/javascripts/preferences/panes/Security.tsx @@ -2,7 +2,7 @@ import { WebApplication } from '@/ui_models/application'; import { AppState } from '@/ui_models/app_state'; import { FunctionComponent } from 'preact'; import { PreferencesPane } from '../components'; -import { Encryption, PasscodeLock } from './security-segments'; +import { Encryption, PasscodeLock, Protections } from './security-segments'; import { TwoFactorAuthWrapper } from './two-factor-auth'; import { MfaProps } from './two-factor-auth/MfaProps'; @@ -14,6 +14,7 @@ interface SecurityProps extends MfaProps { export const Security: FunctionComponent = (props) => ( + = ({ application }) => { + const enableProtections = () => { + application.clearProtectionSession(); + }; + + const [hasProtections, setHasProtections] = useState(() => application.hasProtectionSources()); + + const getProtectionsDisabledUntil = useCallback((): string | null => { + const protectionExpiry = application.getProtectionSessionExpiryDate(); + const now = new Date(); + if (protectionExpiry > now) { + let f: Intl.DateTimeFormat; + if (isSameDay(protectionExpiry, now)) { + f = new Intl.DateTimeFormat(undefined, { + hour: 'numeric', + minute: 'numeric' + }); + } else { + f = new Intl.DateTimeFormat(undefined, { + weekday: 'long', + day: 'numeric', + month: 'short', + hour: 'numeric', + minute: 'numeric' + }); + } + + return f.format(protectionExpiry); + } + return null; + }, [application]); + + const [protectionsDisabledUntil, setProtectionsDisabledUntil] = useState(getProtectionsDisabledUntil()); + + useEffect(() => { + const removeProtectionSessionExpiryDateChangedObserver = application.addEventObserver( + async () => { + setProtectionsDisabledUntil(getProtectionsDisabledUntil()); + }, + ApplicationEvent.ProtectionSessionExpiryDateChanged + ); + + const removeKeyStatusChangedObserver = application.addEventObserver( + async () => { + setHasProtections(application.hasProtectionSources()); + }, + ApplicationEvent.KeyStatusChanged + ); + + return () => { + removeProtectionSessionExpiryDateChangedObserver(); + removeKeyStatusChangedObserver(); + }; + }, [application, getProtectionsDisabledUntil]); + + if (!hasProtections) { + return null; + } + + return ( + + + Protections + {protectionsDisabledUntil + ? Protections are disabled until {protectionsDisabledUntil}. + : Protections are enabled. + } + + Actions like viewing protected notes, exporting decrypted backups, + or revoking an active session, require additional authentication + like entering your account password or application passcode. + + {protectionsDisabledUntil && +