Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit b7c093c

Browse files
committed
Simple UI to reset cross-signing keys
1 parent f8d745d commit b7c093c

File tree

5 files changed

+202
-0
lines changed

5 files changed

+202
-0
lines changed

frontend/locales/en.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,19 @@
9494
"pagination_controls": {
9595
"total": "Total: {{totalCount}}"
9696
},
97+
"reset_cross_signing": {
98+
"button": "Allow cross-signing reset",
99+
"description": "If you have lost access to all your verified devices and your security key, you can reset your cross-signing keys. Deleting cross-signing keys is permanent, but will allow you to go through the verification process again and set up new keys.",
100+
"failure": {
101+
"description": "This might be a temporary problem, so please try again later. If the problem persists, please contact your server administrator.",
102+
"title": "Failed to allow cross-signing"
103+
},
104+
"heading": "Cross-signing reset",
105+
"success": {
106+
"description": "A client can now temporarily reset your account cross-signing keys. Follow the instructions in your client to complete the process.",
107+
"title": "Cross-signing reset temporarily allowed"
108+
}
109+
},
97110
"selectable_session": {
98111
"label": "Select session"
99112
},
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2023 The Matrix.org Foundation C.I.C.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import { Alert, Button, H3, Text } from "@vector-im/compound-web";
16+
import { atom, useAtom } from "jotai";
17+
import { atomFamily } from "jotai/utils";
18+
import { atomWithMutation } from "jotai-urql";
19+
import { useState } from "react";
20+
import { useTranslation } from "react-i18next";
21+
22+
import { graphql } from "../../gql";
23+
import BlockList from "../BlockList";
24+
import LoadingSpinner from "../LoadingSpinner";
25+
26+
const ALLOW_CROSS_SIGING_RESET_MUTATION = graphql(/* GraphQL */ `
27+
mutation AllowCrossSigningReset($userId: ID!) {
28+
allowUserCrossSigningReset(input: { userId: $userId }) {
29+
user {
30+
id
31+
}
32+
}
33+
}
34+
`);
35+
36+
const allowCrossSigningResetFamily = atomFamily((id: string) => {
37+
const allowCrossSigingReset = atomWithMutation(
38+
ALLOW_CROSS_SIGING_RESET_MUTATION,
39+
);
40+
41+
// A proxy atom which pre-sets the id variable in the mutation
42+
const allowCrossSigningResetAtom = atom(
43+
(get) => get(allowCrossSigingReset),
44+
(_get, set) => set(allowCrossSigingReset, { userId: id }),
45+
);
46+
47+
return allowCrossSigningResetAtom;
48+
});
49+
50+
const CrossSigningReset: React.FC<{ userId: string }> = ({ userId }) => {
51+
const { t } = useTranslation();
52+
const [result, allowReset] = useAtom(allowCrossSigningResetFamily(userId));
53+
const [inProgress, setInProgress] = useState(false);
54+
55+
const onClick = (): void => {
56+
if (inProgress) return;
57+
setInProgress(true);
58+
allowReset().finally(() => setInProgress(false));
59+
};
60+
61+
return (
62+
<BlockList>
63+
<H3>{t("frontend.reset_cross_signing.heading")}</H3>
64+
{!result.data && !result.error && (
65+
<>
66+
<Text className="text-justify">
67+
{t("frontend.reset_cross_signing.description")}
68+
</Text>
69+
<Button kind="destructive" disabled={inProgress} onClick={onClick}>
70+
{!!inProgress && <LoadingSpinner inline />}
71+
{t("frontend.reset_cross_signing.button")}
72+
</Button>
73+
</>
74+
)}
75+
{result.data && (
76+
<Alert
77+
type="info"
78+
title={t("frontend.reset_cross_signing.success.title")}
79+
>
80+
{t("frontend.reset_cross_signing.success.description")}
81+
</Alert>
82+
)}
83+
{result.error && (
84+
<Alert
85+
type="critical"
86+
title={t("frontend.reset_cross_signing.failure.title")}
87+
>
88+
{t("frontend.reset_cross_signing.failure.description")}
89+
</Alert>
90+
)}
91+
</BlockList>
92+
);
93+
};
94+
95+
export default CrossSigningReset;

frontend/src/components/UserProfile/UserProfile.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import { Separator } from "@vector-im/compound-web";
16+
1517
import BlockList from "../BlockList/BlockList";
1618

19+
import CrossSigningReset from "./CrossSigningReset";
1720
import UserEmailList from "./UserEmailList";
1821
import UserName from "./UserName";
1922

@@ -22,6 +25,8 @@ const UserProfile: React.FC<{ userId: string }> = ({ userId }) => {
2225
<BlockList>
2326
<UserName userId={userId} />
2427
<UserEmailList userId={userId} />
28+
<Separator />
29+
<CrossSigningReset userId={userId} />
2530
</BlockList>
2631
);
2732
};

frontend/src/gql/gql.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const documents = {
5353
types.UserGreetingDocument,
5454
"\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n violations\n email {\n id\n ...UserEmail_email\n }\n }\n }\n":
5555
types.AddEmailDocument,
56+
"\n mutation AllowCrossSigningReset($userId: ID!) {\n allowUserCrossSigningReset(input: { userId: $userId }) {\n user {\n id\n }\n }\n }\n":
57+
types.AllowCrossSigningResetDocument,
5658
"\n query UserEmailListQuery(\n $userId: ID!\n $first: Int\n $after: String\n $last: Int\n $before: String\n ) {\n user(id: $userId) {\n id\n\n emails(first: $first, after: $after, last: $last, before: $before) {\n edges {\n cursor\n node {\n id\n ...UserEmail_email\n }\n }\n totalCount\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n":
5759
types.UserEmailListQueryDocument,
5860
"\n query UserPrimaryEmail($userId: ID!) {\n user(id: $userId) {\n id\n primaryEmail {\n id\n }\n }\n }\n":
@@ -213,6 +215,12 @@ export function graphql(
213215
export function graphql(
214216
source: "\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n violations\n email {\n id\n ...UserEmail_email\n }\n }\n }\n",
215217
): (typeof documents)["\n mutation AddEmail($userId: ID!, $email: String!) {\n addEmail(input: { userId: $userId, email: $email }) {\n status\n violations\n email {\n id\n ...UserEmail_email\n }\n }\n }\n"];
218+
/**
219+
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
220+
*/
221+
export function graphql(
222+
source: "\n mutation AllowCrossSigningReset($userId: ID!) {\n allowUserCrossSigningReset(input: { userId: $userId }) {\n user {\n id\n }\n }\n }\n",
223+
): (typeof documents)["\n mutation AllowCrossSigningReset($userId: ID!) {\n allowUserCrossSigningReset(input: { userId: $userId }) {\n user {\n id\n }\n }\n }\n"];
216224
/**
217225
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
218226
*/

frontend/src/gql/graphql.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,18 @@ export type AddEmailMutation = {
14381438
};
14391439
};
14401440

1441+
export type AllowCrossSigningResetMutationVariables = Exact<{
1442+
userId: Scalars["ID"]["input"];
1443+
}>;
1444+
1445+
export type AllowCrossSigningResetMutation = {
1446+
__typename?: "Mutation";
1447+
allowUserCrossSigningReset: {
1448+
__typename?: "AllowUserCrossSigningResetPayload";
1449+
user?: { __typename?: "User"; id: string } | null;
1450+
};
1451+
};
1452+
14411453
export type UserEmailListQueryQueryVariables = Exact<{
14421454
userId: Scalars["ID"]["input"];
14431455
first?: InputMaybe<Scalars["Int"]["input"]>;
@@ -3168,6 +3180,75 @@ export const AddEmailDocument = {
31683180
},
31693181
],
31703182
} as unknown as DocumentNode<AddEmailMutation, AddEmailMutationVariables>;
3183+
export const AllowCrossSigningResetDocument = {
3184+
kind: "Document",
3185+
definitions: [
3186+
{
3187+
kind: "OperationDefinition",
3188+
operation: "mutation",
3189+
name: { kind: "Name", value: "AllowCrossSigningReset" },
3190+
variableDefinitions: [
3191+
{
3192+
kind: "VariableDefinition",
3193+
variable: {
3194+
kind: "Variable",
3195+
name: { kind: "Name", value: "userId" },
3196+
},
3197+
type: {
3198+
kind: "NonNullType",
3199+
type: { kind: "NamedType", name: { kind: "Name", value: "ID" } },
3200+
},
3201+
},
3202+
],
3203+
selectionSet: {
3204+
kind: "SelectionSet",
3205+
selections: [
3206+
{
3207+
kind: "Field",
3208+
name: { kind: "Name", value: "allowUserCrossSigningReset" },
3209+
arguments: [
3210+
{
3211+
kind: "Argument",
3212+
name: { kind: "Name", value: "input" },
3213+
value: {
3214+
kind: "ObjectValue",
3215+
fields: [
3216+
{
3217+
kind: "ObjectField",
3218+
name: { kind: "Name", value: "userId" },
3219+
value: {
3220+
kind: "Variable",
3221+
name: { kind: "Name", value: "userId" },
3222+
},
3223+
},
3224+
],
3225+
},
3226+
},
3227+
],
3228+
selectionSet: {
3229+
kind: "SelectionSet",
3230+
selections: [
3231+
{
3232+
kind: "Field",
3233+
name: { kind: "Name", value: "user" },
3234+
selectionSet: {
3235+
kind: "SelectionSet",
3236+
selections: [
3237+
{ kind: "Field", name: { kind: "Name", value: "id" } },
3238+
],
3239+
},
3240+
},
3241+
],
3242+
},
3243+
},
3244+
],
3245+
},
3246+
},
3247+
],
3248+
} as unknown as DocumentNode<
3249+
AllowCrossSigningResetMutation,
3250+
AllowCrossSigningResetMutationVariables
3251+
>;
31713252
export const UserEmailListQueryDocument = {
31723253
kind: "Document",
31733254
definitions: [

0 commit comments

Comments
 (0)