Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade to rust crypto #2168

Open
wants to merge 24 commits into
base: rework-user-settings
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7,568 changes: 3,495 additions & 4,073 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,6 @@
"vite": "5.0.13",
"vite-plugin-pwa": "0.20.5",
"vite-plugin-static-copy": "1.0.4",
"vite-plugin-top-level-await": "1.4.1"
"vite-plugin-top-level-await": "1.4.4"
}
}
73 changes: 73 additions & 0 deletions src/app/components/ActionUIA.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { ReactNode } from 'react';
import { AuthDict, AuthType, IAuthData, UIAFlow } from 'matrix-js-sdk';
import { getUIAFlowForStages } from '../utils/matrix-uia';
import { useSupportedUIAFlows, useUIACompleted, useUIAFlow } from '../hooks/useUIAFlows';
import { UIAFlowOverlay } from './UIAFlowOverlay';
import { PasswordStage, SSOStage } from './uia-stages';
import { useMatrixClient } from '../hooks/useMatrixClient';

export const SUPPORTED_IN_APP_UIA_STAGES = [AuthType.Password, AuthType.Sso];

export const pickUIAFlow = (uiaFlows: UIAFlow[]): UIAFlow | undefined => {
const passwordFlow = getUIAFlowForStages(uiaFlows, [AuthType.Password]);
if (passwordFlow) return passwordFlow;
return getUIAFlowForStages(uiaFlows, [AuthType.Sso]);
};

type ActionUIAProps = {
authData: IAuthData;
ongoingFlow: UIAFlow;
action: (authDict: AuthDict) => void;
onCancel: () => void;
};
export function ActionUIA({ authData, ongoingFlow, action, onCancel }: ActionUIAProps) {
const mx = useMatrixClient();
const completed = useUIACompleted(authData);
const { getStageToComplete } = useUIAFlow(authData, ongoingFlow);

const stageToComplete = getStageToComplete();

if (!stageToComplete) return null;
return (
<UIAFlowOverlay
currentStep={completed.length + 1}
stepCount={ongoingFlow.stages.length}
onCancel={onCancel}
>
{stageToComplete.type === AuthType.Password && (
<PasswordStage
userId={mx.getUserId()!}
stageData={stageToComplete}
onCancel={onCancel}
submitAuthDict={action}
/>
)}
{stageToComplete.type === AuthType.Sso && stageToComplete.session && (
<SSOStage
ssoRedirectURL={mx.getFallbackAuthUrl(AuthType.Sso, stageToComplete.session)}
stageData={stageToComplete}
onCancel={onCancel}
submitAuthDict={action}
/>
)}
</UIAFlowOverlay>
);
}

type ActionUIAFlowsLoaderProps = {
authData: IAuthData;
unsupported: () => ReactNode;
children: (ongoingFlow: UIAFlow) => ReactNode;
};
export function ActionUIAFlowsLoader({
authData,
unsupported,
children,
}: ActionUIAFlowsLoaderProps) {
const supportedFlows = useSupportedUIAFlows(authData.flows ?? [], SUPPORTED_IN_APP_UIA_STAGES);
const ongoingFlow = supportedFlows.length > 0 ? supportedFlows[0] : undefined;

if (!ongoingFlow) return unsupported();

return children(ongoingFlow);
}
2 changes: 1 addition & 1 deletion src/app/components/CapabilitiesAndMediaConfigLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function CapabilitiesAndMediaConfigLoader({
[]
>(
useCallback(async () => {
const result = await Promise.allSettled([mx.getCapabilities(true), mx.getMediaConfig()]);
const result = await Promise.allSettled([mx.getCapabilities(), mx.getMediaConfig()]);
const capabilities = promiseFulfilledResult(result[0]);
const mediaConfig = promiseFulfilledResult(result[1]);
return [capabilities, mediaConfig];
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/CapabilitiesLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type CapabilitiesLoaderProps = {
export function CapabilitiesLoader({ children }: CapabilitiesLoaderProps) {
const mx = useMatrixClient();

const [state, load] = useAsyncCallback(useCallback(() => mx.getCapabilities(true), [mx]));
const [state, load] = useAsyncCallback(useCallback(() => mx.getCapabilities(), [mx]));

useEffect(() => {
load();
Expand Down
3 changes: 1 addition & 2 deletions src/app/components/UIAFlowOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
IconButton,
} from 'folds';
import FocusTrap from 'focus-trap-react';
import { stopPropagation } from '../utils/keyboard';

export type UIAFlowOverlayProps = {
currentStep: number;
Expand All @@ -29,7 +28,7 @@ export function UIAFlowOverlay({
}: UIAFlowOverlayProps) {
return (
<Overlay open backdrop={<OverlayBackdrop />}>
<FocusTrap focusTrapOptions={{ initialFocus: false, escapeDeactivates: stopPropagation }}>
<FocusTrap focusTrapOptions={{ initialFocus: false, escapeDeactivates: false }}>
<Box style={{ height: '100%' }} direction="Column" grow="Yes" gap="400">
<Box grow="Yes" direction="Column" alignItems="Center" justifyContent="Center">
{children}
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/image-pack-view/ImagePackContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ export const ImagePackContent = as<'div', ImagePackContentProps>(
variant="Success"
radii="300"
disabled={!canApplyChanges || applying}
before={applying && <Spinner variant="Success" fill="Soft" size="100" />}
before={applying && <Spinner variant="Success" fill="Solid" size="100" />}
onClick={applyChanges}
>
<Text size="B300">Apply Changes</Text>
Expand Down
100 changes: 100 additions & 0 deletions src/app/components/uia-stages/PasswordStage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
Box,
Button,
color,
config,
Dialog,
Header,
Icon,
IconButton,
Icons,
Input,
Text,
} from 'folds';
import React, { FormEventHandler } from 'react';
import { AuthType } from 'matrix-js-sdk';
import { StageComponentProps } from './types';
import { ErrorCode } from '../../cs-errorcode';

export function PasswordStage({
stageData,
submitAuthDict,
onCancel,
userId,
}: StageComponentProps & {
userId: string;
}) {
const { errorCode, error, session } = stageData;

const handleFormSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
evt.preventDefault();
const { passwordInput } = evt.target as HTMLFormElement & {
passwordInput: HTMLInputElement;
};
const password = passwordInput.value;
if (!password) return;
submitAuthDict({
type: AuthType.Password,
identifier: {
type: 'm.id.user',
user: userId,
},
password,
session,
});
};

return (
<Dialog>
<Header
style={{
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
}}
variant="Surface"
size="500"
>
<Box grow="Yes">
<Text size="H4">Account Password</Text>
</Box>
<IconButton size="300" onClick={onCancel} radii="300">
<Icon src={Icons.Cross} />
</IconButton>
</Header>
<Box
as="form"
onSubmit={handleFormSubmit}
style={{ padding: `0 ${config.space.S400} ${config.space.S400}` }}
direction="Column"
gap="400"
>
<Box direction="Column" gap="400">
<Text size="T200">
To perform this action you need to authenticate yourself by entering you account
password.
</Text>
<Box direction="Column" gap="100">
<Text size="L400">Password</Text>
<Input type="password" name="passwordInput" outlined autoFocus required />
{errorCode && (
<Box alignItems="Center" gap="100" style={{ color: color.Critical.Main }}>
<Icon size="50" src={Icons.Warning} filled />
<Text size="T200">
<b>
{errorCode === ErrorCode.M_FORBIDDEN
? 'Invalid Password!'
: `${errorCode}: ${error}`}
</b>
</Text>
</Box>
)}
</Box>
</Box>
<Button variant="Primary" type="submit">
<Text as="span" size="B400">
Continue
</Text>
</Button>
</Box>
</Dialog>
);
}
93 changes: 93 additions & 0 deletions src/app/components/uia-stages/SSOStage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { Box, Button, color, config, Dialog, Header, Icon, IconButton, Icons, Text } from 'folds';
import React, { useCallback, useEffect, useState } from 'react';
import { AuthType } from 'matrix-js-sdk';
import { StageComponentProps } from './types';

export function SSOStage({
ssoRedirectURL,
stageData,
submitAuthDict,
onCancel,
}: StageComponentProps & {
ssoRedirectURL: string;
}) {
const { errorCode, error, session } = stageData;
const [ssoWindow, setSSOWindow] = useState<Window>();

const handleSubmit = useCallback(() => {
submitAuthDict({
type: AuthType.Sso,
session,
});
}, [submitAuthDict, session]);

const handleContinue = () => {
const w = window.open(ssoRedirectURL, '_blank');
setSSOWindow(w ?? undefined);
};

useEffect(() => {
const handleMessage = (evt: MessageEvent) => {
if (ssoWindow && evt.data === 'authDone' && evt.source === ssoWindow) {
ssoWindow.close();
setSSOWindow(undefined);
handleSubmit();
}
};

window.addEventListener('message', handleMessage);
return () => {
window.removeEventListener('message', handleMessage);
};
}, [ssoWindow, handleSubmit]);

return (
<Dialog>
<Header
style={{
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
}}
variant="Surface"
size="500"
>
<Box grow="Yes">
<Text size="H4">SSO Login</Text>
</Box>
<IconButton size="300" onClick={onCancel} radii="300">
<Icon src={Icons.Cross} />
</IconButton>
</Header>
<Box
style={{ padding: `0 ${config.space.S400} ${config.space.S400}` }}
direction="Column"
gap="400"
>
<Text size="T200">
To perform this action you need to authenticate yourself by SSO login.
</Text>
{errorCode && (
<Box alignItems="Center" gap="100" style={{ color: color.Critical.Main }}>
<Icon size="50" src={Icons.Warning} filled />
<Text size="T200">
<b>{`${errorCode}: ${error}`}</b>
</Text>
</Box>
)}

{ssoWindow ? (
<Button variant="Primary" onClick={handleSubmit}>
<Text as="span" size="B400">
Continue
</Text>
</Button>
) : (
<Button variant="Primary" onClick={handleContinue}>
<Text as="span" size="B400">
Continue with SSO
</Text>
</Button>
)}
</Box>
</Dialog>
);
}
2 changes: 2 additions & 0 deletions src/app/components/uia-stages/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export * from './types';
export * from './DummyStage';
export * from './EmailStage';
export * from './PasswordStage';
export * from './ReCaptchaStage';
export * from './RegistrationTokenStage';
export * from './SSOStage';
export * from './TermsStage';
5 changes: 3 additions & 2 deletions src/app/features/lobby/Lobby.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { Box, Icon, IconButton, Icons, Line, Scroll, config } from 'folds';
import { useVirtualizer } from '@tanstack/react-virtual';
import { useAtom, useAtomValue } from 'jotai';
import { useNavigate } from 'react-router-dom';
import { IJoinRuleEventContent, JoinRule, RestrictedAllowType, Room } from 'matrix-js-sdk';
import { JoinRule, RestrictedAllowType, Room } from 'matrix-js-sdk';
import { RoomJoinRulesEventContent } from 'matrix-js-sdk/lib/types';
import { useSpace } from '../../hooks/useSpace';
import { Page, PageContent, PageContentCenter, PageHeroSection } from '../../components/page';
import { HierarchyItem, useSpaceHierarchy } from '../../hooks/useSpaceHierarchy';
Expand Down Expand Up @@ -258,7 +259,7 @@ export function Lobby() {
const joinRuleContent = getStateEvent(
itemRoom,
StateEvent.RoomJoinRules
)?.getContent<IJoinRuleEventContent>();
)?.getContent<RoomJoinRulesEventContent>();

if (joinRuleContent) {
const allow =
Expand Down
4 changes: 2 additions & 2 deletions src/app/features/room/RoomInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
const safeFiles = files.map(safeFile);
const fileItems: TUploadItem[] = [];

if (mx.isRoomEncrypted(roomId)) {
if (room.hasEncryptionStateEvent()) {
const encryptFiles = fulfilledPromiseSettledResult(
await Promise.allSettled(safeFiles.map((f) => encryptFile(f)))
);
Expand All @@ -178,7 +178,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
item: fileItems,
});
},
[setSelectedFiles, roomId, mx]
[setSelectedFiles, room]
);
const pickFile = useFilePicker(handleFiles, true);
const handlePaste = useFilePasteHandler(handleFiles);
Expand Down
8 changes: 5 additions & 3 deletions src/app/features/room/RoomTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,10 @@ const useTimelinePagination = (
backwards ? Direction.Backward : Direction.Forward
) ?? timelineToPaginate;
// Decrypt all event ahead of render cycle
if (mx.isRoomEncrypted(fetchedTimeline.getRoomId() ?? '')) {
const roomId = fetchedTimeline.getRoomId();
const room = roomId ? mx.getRoom(roomId) : null;

if (room?.hasEncryptionStateEvent()) {
await to(decryptAllTimelineEvent(mx, fetchedTimeline));
}

Expand Down Expand Up @@ -421,15 +424,14 @@ const getRoomUnreadInfo = (room: Room, scrollTo = false) => {
export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimelineProps) {
const mx = useMatrixClient();
const useAuthentication = useMediaAuthentication();
const encryptedRoom = mx.isRoomEncrypted(room.roomId);
const [messageLayout] = useSetting(settingsAtom, 'messageLayout');
const [messageSpacing] = useSetting(settingsAtom, 'messageSpacing');
const [hideMembershipEvents] = useSetting(settingsAtom, 'hideMembershipEvents');
const [hideNickAvatarEvents] = useSetting(settingsAtom, 'hideNickAvatarEvents');
const [mediaAutoLoad] = useSetting(settingsAtom, 'mediaAutoLoad');
const [urlPreview] = useSetting(settingsAtom, 'urlPreview');
const [encUrlPreview] = useSetting(settingsAtom, 'encUrlPreview');
const showUrlPreview = encryptedRoom ? encUrlPreview : urlPreview;
const showUrlPreview = room.hasEncryptionStateEvent() ? encUrlPreview : urlPreview;
const [showHiddenEvents] = useSetting(settingsAtom, 'showHiddenEvents');
const setReplyDraft = useSetAtom(roomIdToReplyDraftAtomFamily(room.roomId));
const powerLevels = usePowerLevelsContext();
Expand Down
Loading
Loading