diff --git a/CHANGELOG.md b/CHANGELOG.md index 47fb3d29d7..e3a677b0a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ + [unreleased] +* Add possible impersonation attack UI for desktop and mobile + * Fix truncated long messages in channelInput component * Unblock mobile e2e tests diff --git a/packages/desktop/src/renderer/Root.tsx b/packages/desktop/src/renderer/Root.tsx index 8717893233..1e0a5aeb71 100644 --- a/packages/desktop/src/renderer/Root.tsx +++ b/packages/desktop/src/renderer/Root.tsx @@ -31,7 +31,7 @@ import ChannelCreationModal from './components/ChannelCreationModal/ChannelCreat import { SaveStateComponent } from './components/SaveState/SaveStateComponent' import UnregisteredModalContainer from './components/widgets/userLabel/unregistered/UnregisteredModal.container' import DuplicateModalContainer from './components/widgets/userLabel/duplicate/DuplicateModal.container' -// Trigger lerna +import PossibleImpersonationAttackModalContainer from './components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.container' export const persistor = persistStore(store) export default () => { @@ -48,6 +48,7 @@ export default () => { + diff --git a/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.component.tsx b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.component.tsx new file mode 100644 index 0000000000..2c3b45a692 --- /dev/null +++ b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.component.tsx @@ -0,0 +1,80 @@ +import React from 'react' +import Modal from '../../ui/Modal/Modal' +import { Button, Grid, Typography } from '@mui/material' +import { styled } from '@mui/material/styles' +import WarnIcon from '../../../static/images/exclamationMark.svg' + +const PREFIX = 'PossibleImpersonationAttackModalComponent-' + +const classes = { + bodyText: `${PREFIX}bodyText`, + button: `${PREFIX}button`, + image: `${PREFIX}image`, +} + +const StyledGrid = styled(Grid)(({ theme }) => ({ + [`& .${classes.bodyText}`]: { + textAlign: 'center', + width: '65%', + margin: '24px 0 4px', + }, + [`& .${classes.image}`]: { + width: '70px', + height: '70px', + margin: '30px 0 24px', + }, + [`& .${classes.button}`]: { + marginTop: 16, + textTransform: 'none', + padding: '0 24px', + height: 40, + borderRadius: '8px', + color: theme.palette.colors.white, + backgroundColor: theme.palette.colors.quietBlue, + '&:hover': { + opacity: 0.7, + backgroundColor: theme.palette.colors.quietBlue, + }, + }, +})) + +export interface PossibleImpersonationAttackModalComponentProps { + communityName: string + leaveCommunity: () => void + open: boolean + handleClose: () => void +} + +const PossibleImpersonationAttackModalComponent: React.FC = ({ + communityName, + leaveCommunity, + handleClose, + open, +}) => { + return ( + + + + Possible impersonation attack + + The owner of {communityName} has registered an invalid username. Either something is very + broken, the community owner is trying to impersonate other users, or the community owner has been hacked. +
+ This should never happen and we recommend leaving this community immediately! +
+ +
+
+ ) +} + +export default PossibleImpersonationAttackModalComponent diff --git a/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.container.tsx b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.container.tsx new file mode 100644 index 0000000000..bd7b75f913 --- /dev/null +++ b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.container.tsx @@ -0,0 +1,41 @@ +import { capitalizeFirstLetter } from '@quiet/common' +import { communities, users } from '@quiet/state-manager' +import React, { useEffect } from 'react' +import { useSelector } from 'react-redux' +import { clearCommunity } from '../../..' +import { useModal } from '../../../containers/hooks' +import { ModalName } from '../../../sagas/modals/modals.types' +import PossibleImpersonationAttackModalComponent from './PossibleImpersonationAttackModal.component' + +const PossibleImpersonationAttackModalContainer = () => { + const possibleImpersonationAttackModal = useModal(ModalName.possibleImpersonationAttackModal) + + const community = useSelector(communities.selectors.currentCommunity) + const duplicateCerts = useSelector(users.selectors.duplicateCerts) + + let communityName = '...' + + if (community?.name) { + communityName = capitalizeFirstLetter(community.name) + } + + const leaveCommunity = async () => { + await clearCommunity() + } + + useEffect(() => { + if (duplicateCerts) { + possibleImpersonationAttackModal.handleOpen() + } + }, [duplicateCerts]) + + return ( + + ) +} + +export default PossibleImpersonationAttackModalContainer diff --git a/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.stories.tsx b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.stories.tsx new file mode 100644 index 0000000000..bd94e8c525 --- /dev/null +++ b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.stories.tsx @@ -0,0 +1,33 @@ +import React from 'react' +import { ComponentStory, ComponentMeta } from '@storybook/react' +import { withTheme } from '../../../storybook/decorators' +import PossibleImpersonationAttackModalComponent, { + PossibleImpersonationAttackModalComponentProps, +} from './PossibleImpersonationAttackModal.component' + +const Template: ComponentStory = args => { + return ( +
+ +
+ ) +} + +export const Component = Template.bind({}) + +const args: PossibleImpersonationAttackModalComponentProps = { + handleClose: function (): void {}, + open: true, + communityName: 'devteam', + leaveCommunity: function (): void {}, +} + +Component.args = args + +const component: ComponentMeta = { + title: 'Components/PossibleImpersonationAttackModalComponent', + decorators: [withTheme], + component: PossibleImpersonationAttackModalComponent, +} + +export default component diff --git a/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.test.tsx b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.test.tsx new file mode 100644 index 0000000000..5a5e512164 --- /dev/null +++ b/packages/desktop/src/renderer/components/widgets/possibleImpersonationAttackModal/PossibleImpersonationAttackModal.test.tsx @@ -0,0 +1,147 @@ +import React from 'react' +import theme from '../../../theme' +import { ThemeProvider } from '@mui/material/styles' +import { renderComponent } from '../../../testUtils/renderComponent' +import PossibleImpersonationAttackModalComponent from './PossibleImpersonationAttackModal.component' + +describe('PossibleImpersonationAttackModal', () => { + it('renderComponent', () => { + const result = renderComponent( + + {}} + open={true} + communityName={'devteam'} + leaveCommunity={() => {}} + /> + + ) + expect(result.baseElement).toMatchInlineSnapshot(` + +