import type {RouteComponentProps} from 'react-router'; import styled from '@emotion/styled'; import {addErrorMessage} from 'sentry/actionCreators/indicator'; import {openEmailVerification} from 'sentry/actionCreators/modal'; import {Button} from 'sentry/components/button'; import CircleIndicator from 'sentry/components/circleIndicator'; import EmptyMessage from 'sentry/components/emptyMessage'; import FieldGroup from 'sentry/components/forms/fieldGroup'; import ListLink from 'sentry/components/links/listLink'; import NavTabs from 'sentry/components/navTabs'; import Panel from 'sentry/components/panels/panel'; import PanelBody from 'sentry/components/panels/panelBody'; import PanelHeader from 'sentry/components/panels/panelHeader'; import PanelItem from 'sentry/components/panels/panelItem'; import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import {Tooltip} from 'sentry/components/tooltip'; import {IconDelete} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Authenticator, OrganizationSummary} from 'sentry/types'; import oxfordizeArray from 'sentry/utils/oxfordizeArray'; import recreateRoute from 'sentry/utils/recreateRoute'; import useApi from 'sentry/utils/useApi'; import RemoveConfirm from 'sentry/views/settings/account/accountSecurity/components/removeConfirm'; import TwoFactorRequired from 'sentry/views/settings/account/accountSecurity/components/twoFactorRequired'; import PasswordForm from 'sentry/views/settings/account/passwordForm'; import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader'; import TextBlock from 'sentry/views/settings/components/text/textBlock'; type Props = { authenticators: Authenticator[] | null; countEnrolled: number; deleteDisabled: boolean; handleRefresh: () => void; hasVerifiedEmail: boolean; onDisable: (auth: Authenticator) => void; orgsRequire2fa: OrganizationSummary[]; } & RouteComponentProps<{}, {}>; /** * Lists 2fa devices + password change form */ function AccountSecurity({ authenticators, countEnrolled, deleteDisabled, onDisable, hasVerifiedEmail, orgsRequire2fa, handleRefresh, params, routes, }: Props) { const api = useApi(); async function handleSessionClose() { try { await api.requestPromise('/auth/', { method: 'DELETE', data: {all: true}, }); window.location.assign('/auth/login/'); } catch (err) { addErrorMessage(t('There was a problem closing all sessions')); throw err; } } const formatOrgSlugs = () => { const slugs = orgsRequire2fa.map(({slug}) => slug); return oxfordizeArray(slugs); }; const handleAdd2FAClicked = () => { openEmailVerification({ onClose: () => { handleRefresh(); }, actionMessage: 'enrolling a 2FA device', }); }; const isEmpty = !authenticators?.length; return ( {t('Settings')} {t('Session History')} } /> {!isEmpty && countEnrolled === 0 && } {t('Sessions')} {t('Two-Factor Authentication')} {isEmpty && ( {t('No available authenticators to add')} )} {!isEmpty && authenticators?.map(auth => { const { id, authId, description, isBackupInterface, isEnrolled, disallowNewEnrollment, configureButton, name, } = auth; if (disallowNewEnrollment && !isEnrolled) { return null; } return ( {name} {!isBackupInterface && !isEnrolled && hasVerifiedEmail && ( )} {!isBackupInterface && !isEnrolled && !hasVerifiedEmail && ( )} {isEnrolled && authId && ( )} {!isBackupInterface && isEnrolled && ( onDisable(auth)} disabled={deleteDisabled} >