diff --git a/cypress/e2e/dashboard.cy.ts b/cypress/e2e/dashboard.cy.ts index b5f62097ebee1..cefbb60b6fe69 100644 --- a/cypress/e2e/dashboard.cy.ts +++ b/cypress/e2e/dashboard.cy.ts @@ -379,6 +379,8 @@ describe('Dashboard', () => { cy.get('[data-attr="date-filter"]').click() cy.contains('span', 'Last 14 days').click() + cy.wait(2000) + // insight meta should be updated to show new date range cy.get('h5').contains('Last 14 days').should('exist') diff --git a/frontend/src/layout/GlobalModals.tsx b/frontend/src/layout/GlobalModals.tsx index 803bb2c5e8237..81bdae758064a 100644 --- a/frontend/src/layout/GlobalModals.tsx +++ b/frontend/src/layout/GlobalModals.tsx @@ -5,14 +5,12 @@ import { TimeSensitiveAuthenticationModal } from 'lib/components/TimeSensitiveAu import { UpgradeModal } from 'lib/components/UpgradeModal/UpgradeModal' import { TwoFactorSetupModal } from 'scenes/authentication/TwoFactorSetupModal' import { CreateOrganizationModal } from 'scenes/organization/CreateOrganizationModal' -import { membersLogic } from 'scenes/organization/membersLogic' import { CreateEnvironmentModal } from 'scenes/project/CreateEnvironmentModal' import { CreateProjectModal } from 'scenes/project/CreateProjectModal' import { SessionPlayerModal } from 'scenes/session-recordings/player/modal/SessionPlayerModal' import { inviteLogic } from 'scenes/settings/organization/inviteLogic' import { InviteModal } from 'scenes/settings/organization/InviteModal' import { PreviewingCustomCssModal } from 'scenes/themes/PreviewingCustomCssModal' -import { userLogic } from 'scenes/userLogic' import type { globalModalsLogicType } from './GlobalModalsType' @@ -58,7 +56,6 @@ export function GlobalModals(): JSX.Element { useActions(globalModalsLogic) const { isInviteModalShown } = useValues(inviteLogic) const { hideInviteModal } = useActions(inviteLogic) - const { user } = useValues(userLogic) return ( <> @@ -71,17 +68,7 @@ export function GlobalModals(): JSX.Element { - {user && user.organization?.enforce_2fa && !user.is_2fa_enabled && ( - { - userLogic.actions.loadUser() - membersLogic.actions.loadAllMembers() - }} - forceOpen - closable={false} - required={true} - /> - )} + ) diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index 9d14c1b3c0acf..de9e072e7f69b 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -119,6 +119,7 @@ export const defaultMocks: Mocks = { }, }, ], + '/api/users/@me/two_factor_status/': () => [200, { is_enabled: true, backup_codes: [], method: 'TOTP' }], '/api/environments/@current/': MOCK_DEFAULT_TEAM, '/api/projects/@current/': MOCK_DEFAULT_TEAM, '/api/projects/:team_id/comments/count': { count: 0 }, diff --git a/frontend/src/scenes/authentication/TwoFactorSetupModal.tsx b/frontend/src/scenes/authentication/TwoFactorSetupModal.tsx index 8da04b39ed0bd..ae63d8649e87d 100644 --- a/frontend/src/scenes/authentication/TwoFactorSetupModal.tsx +++ b/frontend/src/scenes/authentication/TwoFactorSetupModal.tsx @@ -1,35 +1,25 @@ import { useActions, useValues } from 'kea' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { membersLogic } from 'scenes/organization/membersLogic' +import { userLogic } from 'scenes/userLogic' import { twoFactorLogic } from './twoFactorLogic' import { TwoFactorSetup } from './TwoFactorSetup' -interface TwoFactorSetupModalProps { - onSuccess: () => void - closable?: boolean - required?: boolean - forceOpen?: boolean -} - -export function TwoFactorSetupModal({ - onSuccess, - closable = true, - required = false, - forceOpen = false, -}: TwoFactorSetupModalProps): JSX.Element { - const { isTwoFactorSetupModalOpen } = useValues(twoFactorLogic) - const { toggleTwoFactorSetupModal } = useActions(twoFactorLogic) +export function TwoFactorSetupModal(): JSX.Element { + const { isTwoFactorSetupModalOpen, forceOpenTwoFactorSetupModal } = useValues(twoFactorLogic) + const { closeTwoFactorSetupModal } = useActions(twoFactorLogic) return ( toggleTwoFactorSetupModal(false) : undefined} - closable={closable} + isOpen={isTwoFactorSetupModalOpen || forceOpenTwoFactorSetupModal} + onClose={!forceOpenTwoFactorSetupModal ? () => closeTwoFactorSetupModal() : undefined} + closable={!forceOpenTwoFactorSetupModal} >
- {required && ( + {forceOpenTwoFactorSetupModal && ( Your organization requires you to set up 2FA. @@ -37,10 +27,9 @@ export function TwoFactorSetupModal({

Use an authenticator app like Google Authenticator or 1Password to scan the QR code below.

{ - toggleTwoFactorSetupModal(false) - if (onSuccess) { - onSuccess() - } + closeTwoFactorSetupModal() + userLogic.actions.loadUser() + membersLogic.actions.loadAllMembers() }} />
diff --git a/frontend/src/scenes/authentication/twoFactorLogic.ts b/frontend/src/scenes/authentication/twoFactorLogic.ts index 43d31a7f4d189..37c331b809868 100644 --- a/frontend/src/scenes/authentication/twoFactorLogic.ts +++ b/frontend/src/scenes/authentication/twoFactorLogic.ts @@ -4,7 +4,9 @@ import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' import api from 'lib/api' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { membersLogic } from 'scenes/organization/membersLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { userLogic } from 'scenes/userLogic' import type { twoFactorLogicType } from './twoFactorLogicType' @@ -26,7 +28,8 @@ export const twoFactorLogic = kea([ path(['scenes', 'authentication', 'loginLogic']), props({} as TwoFactorLogicProps), connect({ - values: [preflightLogic, ['preflight'], featureFlagLogic, ['featureFlags']], + values: [preflightLogic, ['preflight'], featureFlagLogic, ['featureFlags'], userLogic, ['user']], + actions: [userLogic, ['loadUser'], membersLogic, ['loadAllMembers']], }), actions({ setGeneralError: (code: string, detail: string) => ({ code, detail }), @@ -34,16 +37,24 @@ export const twoFactorLogic = kea([ loadStatus: true, generateBackupCodes: true, disable2FA: true, - toggleTwoFactorSetupModal: (open: boolean) => ({ open }), + openTwoFactorSetupModal: (forceOpen?: boolean) => ({ forceOpen }), + closeTwoFactorSetupModal: true, toggleDisable2FAModal: (open: boolean) => ({ open }), toggleBackupCodesModal: (open: boolean) => ({ open }), - startSetup: true, }), reducers({ isTwoFactorSetupModalOpen: [ false, { - toggleTwoFactorSetupModal: (_, { open }) => open, + openTwoFactorSetupModal: () => true, + closeTwoFactorSetupModal: () => false, + }, + ], + forceOpenTwoFactorSetupModal: [ + false, + { + openTwoFactorSetupModal: (_, { forceOpen }) => !!forceOpen, + closeTwoFactorSetupModal: () => false, }, ], isDisable2FAModalOpen: [ @@ -89,11 +100,9 @@ export const twoFactorLogic = kea([ startSetup: [ {}, { - toggleTwoFactorSetupModal: async ({ open }, breakpoint) => { - if (open) { - breakpoint() - await api.get('api/users/@me/two_factor_start_setup/') - } + openTwoFactorSetupModal: async (_, breakpoint) => { + breakpoint() + await api.get('api/users/@me/two_factor_start_setup/') return { status: 'completed' } }, }, @@ -144,6 +153,10 @@ export const twoFactorLogic = kea([ await api.create('api/users/@me/two_factor_disable/') lemonToast.success('2FA disabled successfully') actions.loadStatus() + + // Refresh user and members + actions.loadUser() + actions.loadAllMembers() } catch (e) { const { code, detail } = e as Record actions.setGeneralError(code, detail) @@ -153,19 +166,17 @@ export const twoFactorLogic = kea([ generateBackupCodesSuccess: () => { lemonToast.success('Backup codes generated successfully') }, - toggleTwoFactorSetupModal: ({ open }) => { - if (!open) { - // Clear the form when closing the modal - actions.resetToken() - } - }, - startSetup: async () => { - await api.get('api/users/@me/two_factor_start_setup/') + closeTwoFactorSetupModal: () => { + // Clear the form when closing the modal + actions.resetToken() }, })), - afterMount(({ actions }) => { - actions.startSetup() + afterMount(({ actions, values }) => { actions.loadStatus() + + if (values.user && values.user.organization?.enforce_2fa && !values.user.is_2fa_enabled) { + actions.openTwoFactorSetupModal(true) + } }), ]) diff --git a/frontend/src/scenes/settings/organization/Members.tsx b/frontend/src/scenes/settings/organization/Members.tsx index 3659b22c952ef..997582fa81982 100644 --- a/frontend/src/scenes/settings/organization/Members.tsx +++ b/frontend/src/scenes/settings/organization/Members.tsx @@ -19,7 +19,6 @@ import { } from 'lib/utils/permissioning' import { useEffect } from 'react' import { twoFactorLogic } from 'scenes/authentication/twoFactorLogic' -import { TwoFactorSetupModal } from 'scenes/authentication/TwoFactorSetupModal' import { membersLogic } from 'scenes/organization/membersLogic' import { organizationLogic } from 'scenes/organizationLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' @@ -143,9 +142,9 @@ export function Members(): JSX.Element | null { const { preflight } = useValues(preflightLogic) const { user } = useValues(userLogic) - const { setSearch, ensureAllMembersLoaded, loadAllMembers } = useActions(membersLogic) + const { setSearch, ensureAllMembersLoaded } = useActions(membersLogic) const { updateOrganization } = useActions(organizationLogic) - const { toggleTwoFactorSetupModal } = useActions(twoFactorLogic) + const { openTwoFactorSetupModal } = useActions(twoFactorLogic) useEffect(() => { ensureAllMembersLoaded() @@ -212,14 +211,6 @@ export function Members(): JSX.Element | null { render: function LevelRender(_, member) { return ( <> - {member.user.uuid == user.uuid && ( - { - userLogic.actions.updateUser({}) - loadAllMembers() - }} - /> - )} toggleTwoFactorSetupModal(true) + ? () => openTwoFactorSetupModal() : undefined } data-attr="2fa-enabled" diff --git a/frontend/src/scenes/settings/user/TwoFactorSettings.tsx b/frontend/src/scenes/settings/user/TwoFactorSettings.tsx index b9e71ce8575ad..dad73b097f5a9 100644 --- a/frontend/src/scenes/settings/user/TwoFactorSettings.tsx +++ b/frontend/src/scenes/settings/user/TwoFactorSettings.tsx @@ -3,7 +3,6 @@ import { LemonButton, LemonModal } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { copyToClipboard } from 'lib/utils/copyToClipboard' import { twoFactorLogic } from 'scenes/authentication/twoFactorLogic' -import { TwoFactorSetupModal } from 'scenes/authentication/TwoFactorSetupModal' import { membersLogic } from 'scenes/organization/membersLogic' import { userLogic } from 'scenes/userLogic' @@ -13,13 +12,8 @@ export function TwoFactorSettings(): JSX.Element { const { updateUser } = useActions(userLogic) const { loadMemberUpdates } = useActions(membersLogic) - const { - generateBackupCodes, - disable2FA, - toggleTwoFactorSetupModal, - toggleDisable2FAModal, - toggleBackupCodesModal, - } = useActions(twoFactorLogic) + const { generateBackupCodes, disable2FA, openTwoFactorSetupModal, toggleDisable2FAModal, toggleBackupCodesModal } = + useActions(twoFactorLogic) const handleSuccess = (): void => { updateUser({}) @@ -28,8 +22,6 @@ export function TwoFactorSettings(): JSX.Element { return (
- - {isDisable2FAModalOpen && ( 2FA is not enabled
- toggleTwoFactorSetupModal(true)}> + openTwoFactorSetupModal()}> Set up 2FA