chore: block disabling 2FA if U2F authenticators present - skip e2e (#2862)
* chore: block disabling 2FA if U2F authenticators present * chore: remove the u2f wrapper component * chore: adjust fetching 2fa status * chore: fix property name * chore: remove wrapper
This commit is contained in:
@@ -1,8 +1,7 @@
|
|||||||
import { NativeFeatureIdentifier, FeatureStatus } from '@standardnotes/snjs'
|
import { NativeFeatureIdentifier, FeatureStatus } from '@standardnotes/snjs'
|
||||||
import { FunctionComponent, useState } from 'react'
|
import { FunctionComponent, useEffect, useState } from 'react'
|
||||||
|
|
||||||
import { WebApplication } from '@/Application/WebApplication'
|
import { WebApplication } from '@/Application/WebApplication'
|
||||||
import TwoFactorAuthWrapper from './TwoFactorAuth/TwoFactorAuthWrapper'
|
|
||||||
import Encryption from './Encryption'
|
import Encryption from './Encryption'
|
||||||
import PasscodeLock from './PasscodeLock'
|
import PasscodeLock from './PasscodeLock'
|
||||||
import Privacy from './Privacy'
|
import Privacy from './Privacy'
|
||||||
@@ -11,8 +10,9 @@ import ErroredItems from './ErroredItems'
|
|||||||
import PreferencesPane from '@/Components/Preferences/PreferencesComponents/PreferencesPane'
|
import PreferencesPane from '@/Components/Preferences/PreferencesComponents/PreferencesPane'
|
||||||
import BiometricsLock from '@/Components/Preferences/Panes/Security/BiometricsLock'
|
import BiometricsLock from '@/Components/Preferences/Panes/Security/BiometricsLock'
|
||||||
import MultitaskingPrivacy from '@/Components/Preferences/Panes/Security/MultitaskingPrivacy'
|
import MultitaskingPrivacy from '@/Components/Preferences/Panes/Security/MultitaskingPrivacy'
|
||||||
import U2FWrapper from './U2F/U2FWrapper'
|
|
||||||
import { TwoFactorAuth, is2FAEnabled as checkIf2FAIsEnabled } from './TwoFactorAuth/TwoFactorAuth'
|
import { TwoFactorAuth, is2FAEnabled as checkIf2FAIsEnabled } from './TwoFactorAuth/TwoFactorAuth'
|
||||||
|
import U2FView from './U2F/U2FView/U2FView'
|
||||||
|
import TwoFactorAuthView from './TwoFactorAuth/TwoFactorAuthView/TwoFactorAuthView'
|
||||||
|
|
||||||
interface SecurityProps {
|
interface SecurityProps {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
@@ -21,6 +21,7 @@ interface SecurityProps {
|
|||||||
const Security: FunctionComponent<SecurityProps> = (props) => {
|
const Security: FunctionComponent<SecurityProps> = (props) => {
|
||||||
const isNativeMobileWeb = props.application.isNativeMobileWeb()
|
const isNativeMobileWeb = props.application.isNativeMobileWeb()
|
||||||
const [is2FAEnabled, setIs2FAEnabled] = useState(false)
|
const [is2FAEnabled, setIs2FAEnabled] = useState(false)
|
||||||
|
const [canDisable2FA, setCanDisable2FA] = useState(true)
|
||||||
|
|
||||||
const [auth] = useState(
|
const [auth] = useState(
|
||||||
() =>
|
() =>
|
||||||
@@ -28,7 +29,14 @@ const Security: FunctionComponent<SecurityProps> = (props) => {
|
|||||||
setIs2FAEnabled(checkIf2FAIsEnabled(status)),
|
setIs2FAEnabled(checkIf2FAIsEnabled(status)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
auth.fetchStatus()
|
|
||||||
|
useEffect(() => {
|
||||||
|
auth.fetchStatus()
|
||||||
|
}, [auth])
|
||||||
|
|
||||||
|
const onU2FDevicesLoaded = (devices: Array<{ id: string; name: string }>) => {
|
||||||
|
setCanDisable2FA(devices.length === 0)
|
||||||
|
}
|
||||||
|
|
||||||
const isU2FFeatureAvailable =
|
const isU2FFeatureAvailable =
|
||||||
props.application.features.getFeatureStatus(
|
props.application.features.getFeatureStatus(
|
||||||
@@ -40,8 +48,14 @@ const Security: FunctionComponent<SecurityProps> = (props) => {
|
|||||||
<Encryption />
|
<Encryption />
|
||||||
{props.application.items.invalidNonVaultedItems.length > 0 && <ErroredItems />}
|
{props.application.items.invalidNonVaultedItems.length > 0 && <ErroredItems />}
|
||||||
<Protections application={props.application} />
|
<Protections application={props.application} />
|
||||||
<TwoFactorAuthWrapper auth={auth} application={props.application} />
|
<TwoFactorAuthView auth={auth} application={props.application} canDisable2FA={canDisable2FA} />
|
||||||
{isU2FFeatureAvailable && <U2FWrapper application={props.application} is2FAEnabled={is2FAEnabled} />}
|
{isU2FFeatureAvailable && (
|
||||||
|
<U2FView
|
||||||
|
application={props.application}
|
||||||
|
is2FAEnabled={is2FAEnabled}
|
||||||
|
loadAuthenticatorsCallback={onU2FDevicesLoaded}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{isNativeMobileWeb && <MultitaskingPrivacy application={props.application} />}
|
{isNativeMobileWeb && <MultitaskingPrivacy application={props.application} />}
|
||||||
<PasscodeLock application={props.application} />
|
<PasscodeLock application={props.application} />
|
||||||
{isNativeMobileWeb && <BiometricsLock application={props.application} />}
|
{isNativeMobileWeb && <BiometricsLock application={props.application} />}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { WebApplication } from '@/Application/WebApplication'
|
|
||||||
import { TwoFactorAuth } from './TwoFactorAuth'
|
|
||||||
|
|
||||||
export interface MfaProps {
|
|
||||||
application: WebApplication
|
|
||||||
auth: TwoFactorAuth
|
|
||||||
}
|
|
||||||
@@ -16,9 +16,10 @@ import ModalOverlay from '@/Components/Modal/ModalOverlay'
|
|||||||
type Props = {
|
type Props = {
|
||||||
auth: TwoFactorAuth
|
auth: TwoFactorAuth
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
|
canDisable2FA: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const TwoFactorAuthView: FunctionComponent<Props> = ({ auth, application }) => {
|
const TwoFactorAuthView: FunctionComponent<Props> = ({ auth, application, canDisable2FA }) => {
|
||||||
const shouldShowActivationModal = auth.status !== 'fetching' && is2FAActivation(auth.status)
|
const shouldShowActivationModal = auth.status !== 'fetching' && is2FAActivation(auth.status)
|
||||||
|
|
||||||
const activationModalTitle = shouldShowActivationModal
|
const activationModalTitle = shouldShowActivationModal
|
||||||
@@ -96,7 +97,7 @@ const TwoFactorAuthView: FunctionComponent<Props> = ({ auth, application }) => {
|
|||||||
<TwoFactorTitle auth={auth} />
|
<TwoFactorTitle auth={auth} />
|
||||||
<TwoFactorDescription auth={auth} />
|
<TwoFactorDescription auth={auth} />
|
||||||
</div>
|
</div>
|
||||||
<TwoFactorSwitch auth={auth} />
|
<TwoFactorSwitch auth={auth} canDisable2FA={canDisable2FA} />
|
||||||
</div>
|
</div>
|
||||||
</PreferencesSegment>
|
</PreferencesSegment>
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import Spinner from '@/Components/Spinner/Spinner'
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
auth: TwoFactorAuth
|
auth: TwoFactorAuth
|
||||||
|
canDisable2FA: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const TwoFactorSwitch: FunctionComponent<Props> = ({ auth }) => {
|
const TwoFactorSwitch: FunctionComponent<Props> = ({ auth, canDisable2FA }) => {
|
||||||
if (!auth.isLoggedIn()) {
|
if (!auth.isLoggedIn()) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -17,7 +18,9 @@ const TwoFactorSwitch: FunctionComponent<Props> = ({ auth }) => {
|
|||||||
return <Spinner className="h-4 w-4" />
|
return <Spinner className="h-4 w-4" />
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Switch checked={!is2FADisabled(auth.status)} onChange={auth.toggle2FA} />
|
const shouldSwitchBeDisabled = auth.status === 'two-factor-enabled' && !canDisable2FA
|
||||||
|
|
||||||
|
return <Switch checked={!is2FADisabled(auth.status)} onChange={auth.toggle2FA} disabled={shouldSwitchBeDisabled} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default observer(TwoFactorSwitch)
|
export default observer(TwoFactorSwitch)
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { FunctionComponent } from 'react'
|
|
||||||
|
|
||||||
import { MfaProps } from './MfaProps'
|
|
||||||
import TwoFactorAuthView from './TwoFactorAuthView/TwoFactorAuthView'
|
|
||||||
|
|
||||||
const TwoFactorAuthWrapper: FunctionComponent<MfaProps> = (props) => {
|
|
||||||
return <TwoFactorAuthView auth={props.auth} application={props.application} />
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TwoFactorAuthWrapper
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { WebApplication } from '@/Application/WebApplication'
|
|
||||||
|
|
||||||
export interface U2FProps {
|
|
||||||
application: WebApplication
|
|
||||||
is2FAEnabled: boolean
|
|
||||||
}
|
|
||||||
@@ -16,9 +16,10 @@ import RecoveryCodeBanner from '@/Components/RecoveryCodeBanner/RecoveryCodeBann
|
|||||||
type Props = {
|
type Props = {
|
||||||
application: WebApplication
|
application: WebApplication
|
||||||
is2FAEnabled: boolean
|
is2FAEnabled: boolean
|
||||||
|
loadAuthenticatorsCallback: (devices: Array<{ id: string; name: string }>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const U2FView: FunctionComponent<Props> = ({ application, is2FAEnabled }) => {
|
const U2FView: FunctionComponent<Props> = ({ application, is2FAEnabled, loadAuthenticatorsCallback }) => {
|
||||||
const [showDeviceAddingModal, setShowDeviceAddingModal] = useState(false)
|
const [showDeviceAddingModal, setShowDeviceAddingModal] = useState(false)
|
||||||
const [devices, setDevices] = useState<Array<{ id: string; name: string }>>([])
|
const [devices, setDevices] = useState<Array<{ id: string; name: string }>>([])
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
@@ -35,8 +36,10 @@ const U2FView: FunctionComponent<Props> = ({ application, is2FAEnabled }) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setDevices(authenticatorListOrError.getValue())
|
const authenticatorList = authenticatorListOrError.getValue()
|
||||||
}, [setError, setDevices, application])
|
setDevices(authenticatorList)
|
||||||
|
loadAuthenticatorsCallback(authenticatorList)
|
||||||
|
}, [setError, setDevices, application, loadAuthenticatorsCallback])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadAuthenticatorDevices().catch(console.error)
|
loadAuthenticatorDevices().catch(console.error)
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { FunctionComponent } from 'react'
|
|
||||||
|
|
||||||
import { U2FProps } from './U2FProps'
|
|
||||||
import U2FView from './U2FView/U2FView'
|
|
||||||
|
|
||||||
const U2FWrapper: FunctionComponent<U2FProps> = (props) => {
|
|
||||||
return <U2FView application={props.application} is2FAEnabled={props.is2FAEnabled} />
|
|
||||||
}
|
|
||||||
|
|
||||||
export default U2FWrapper
|
|
||||||
Reference in New Issue
Block a user