Skip to content

Commit a353043

Browse files
committed
End
1 parent 75d7865 commit a353043

File tree

4 files changed

+322
-266
lines changed

4 files changed

+322
-266
lines changed

site/src/components/SettingsSecurityForm/SettingsSecurityForm.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getFormHelpers } from "../../utils/formUtils"
66
import { LoadingButton } from "../LoadingButton/LoadingButton"
77
import { ErrorAlert } from "components/Alert/ErrorAlert"
88
import { Form, FormFields } from "components/Form/Form"
9+
import { Alert } from "components/Alert/Alert"
910

1011
interface SecurityFormValues {
1112
old_password: string
@@ -41,6 +42,7 @@ const validationSchema = Yup.object({
4142
})
4243

4344
export interface SecurityFormProps {
45+
disabled?: boolean
4446
isLoading: boolean
4547
initialValues: SecurityFormValues
4648
onSubmit: (values: SecurityFormValues) => void
@@ -50,6 +52,7 @@ export interface SecurityFormProps {
5052
}
5153

5254
export const SecurityForm: FC<SecurityFormProps> = ({
55+
disabled,
5356
isLoading,
5457
onSubmit,
5558
initialValues,
@@ -68,6 +71,14 @@ export const SecurityForm: FC<SecurityFormProps> = ({
6871
updateSecurityError,
6972
)
7073

74+
if (disabled) {
75+
return (
76+
<Alert severity="info">
77+
Password changes are only allowed for password based accounts.
78+
</Alert>
79+
)
80+
}
81+
7182
return (
7283
<>
7384
<Form onSubmit={form.handleSubmit}>

site/src/pages/UserSettingsPage/AccountPage/AccountPage.tsx

Lines changed: 14 additions & 249 deletions
Original file line numberDiff line numberDiff line change
@@ -1,270 +1,35 @@
1-
import { ComponentProps, FC, useState } from "react"
1+
import { FC } from "react"
22
import { Section } from "../../../components/SettingsLayout/Section"
33
import { AccountForm } from "../../../components/SettingsAccountForm/SettingsAccountForm"
44
import { useAuth } from "components/AuthProvider/AuthProvider"
55
import { useMe } from "hooks/useMe"
66
import { usePermissions } from "hooks/usePermissions"
7-
import TextField from "@mui/material/TextField"
8-
import Box from "@mui/material/Box"
9-
import GitHubIcon from "@mui/icons-material/GitHub"
10-
import KeyIcon from "@mui/icons-material/VpnKey"
11-
import Button from "@mui/material/Button"
12-
import { useLocation } from "react-router-dom"
13-
import { retrieveRedirect } from "utils/redirect"
14-
import Typography from "@mui/material/Typography"
15-
import { convertToOAUTH, getAuthMethods } from "api/api"
16-
import { AuthMethods, LoginType } from "api/typesGenerated"
17-
import Skeleton from "@mui/material/Skeleton"
18-
import { Stack } from "components/Stack/Stack"
19-
import { useMutation, useQuery } from "@tanstack/react-query"
20-
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"
21-
import { getErrorMessage } from "api/errors"
22-
23-
type LoginTypeConfirmation =
24-
| {
25-
open: false
26-
selectedType: undefined
27-
}
28-
| {
29-
open: true
30-
selectedType: LoginType
31-
}
327

338
export const AccountPage: FC = () => {
349
const [authState, authSend] = useAuth()
3510
const me = useMe()
3611
const permissions = usePermissions()
3712
const { updateProfileError } = authState.context
3813
const canEditUsers = permissions && permissions.updateUsers
39-
const location = useLocation()
40-
const redirectTo = retrieveRedirect(location.search)
41-
const [loginTypeConfirmation, setLoginTypeConfirmation] =
42-
useState<LoginTypeConfirmation>({ open: false, selectedType: undefined })
43-
const { data: authMethods } = useQuery({
44-
queryKey: ["authMethods"],
45-
queryFn: getAuthMethods,
46-
})
47-
const loginTypeMutation = useMutation(convertToOAUTH, {
48-
onSuccess: (data) => {
49-
window.location.href = `/api/v2/users/oidc/callback?oidc_merge_state=${
50-
data.state_string
51-
}&redirect=${encodeURIComponent(redirectTo)}`
52-
},
53-
})
5414

5515
return (
56-
<Stack spacing={8}>
57-
<Section title="Account" description="Update your account info">
58-
<AccountForm
59-
editable={Boolean(canEditUsers)}
60-
email={me.email}
61-
updateProfileError={updateProfileError}
62-
isLoading={authState.matches("signedIn.profile.updatingProfile")}
63-
initialValues={{
64-
username: me.username,
65-
}}
66-
onSubmit={(data) => {
67-
authSend({
68-
type: "UPDATE_PROFILE",
69-
data,
70-
})
71-
}}
72-
/>
73-
</Section>
74-
75-
<Section
76-
title="Single Sign On"
77-
description="Authenticate in Coder using one-click"
78-
>
79-
<Box display="grid" gap="16px">
80-
{authMethods ? (
81-
authMethods.me_login_type === "password" ? (
82-
<>
83-
{authMethods.github.enabled && (
84-
<GitHubButton
85-
disabled={loginTypeMutation.isLoading}
86-
onClick={() =>
87-
setLoginTypeConfirmation({
88-
open: true,
89-
selectedType: "github",
90-
})
91-
}
92-
>
93-
GitHub
94-
</GitHubButton>
95-
)}
96-
{authMethods.oidc.enabled && (
97-
<OIDCButton
98-
authMethods={authMethods}
99-
disabled={loginTypeMutation.isLoading}
100-
onClick={() =>
101-
setLoginTypeConfirmation({
102-
open: true,
103-
selectedType: "oidc",
104-
})
105-
}
106-
>
107-
{getOIDCLabel(authMethods)}
108-
</OIDCButton>
109-
)}
110-
</>
111-
) : (
112-
<>
113-
{authMethods.me_login_type === "github" && (
114-
<GitHubButton disabled>
115-
Authenticated with GitHub
116-
</GitHubButton>
117-
)}
118-
119-
{authMethods.me_login_type === "oidc" && (
120-
<OIDCButton authMethods={authMethods} disabled>
121-
Authenticated with {getOIDCLabel(authMethods)}
122-
</OIDCButton>
123-
)}
124-
</>
125-
)
126-
) : (
127-
<>
128-
<Skeleton
129-
variant="rectangular"
130-
sx={{ height: 40, borderRadius: 1 }}
131-
/>
132-
<Skeleton
133-
variant="rectangular"
134-
sx={{ height: 40, borderRadius: 1 }}
135-
/>
136-
</>
137-
)}
138-
</Box>
139-
</Section>
140-
141-
<ConfirmLoginTypeChangeModal
142-
open={loginTypeConfirmation.open}
143-
error={loginTypeMutation.error}
144-
// We still want to show it loading when it is success so the modal is
145-
// not going to close or change until the oauth redirect
146-
loading={loginTypeMutation.isLoading || loginTypeMutation.isSuccess}
147-
onClose={() => {
148-
setLoginTypeConfirmation({ open: false, selectedType: undefined })
149-
loginTypeMutation.reset()
16+
<Section title="Account" description="Update your account info">
17+
<AccountForm
18+
editable={Boolean(canEditUsers)}
19+
email={me.email}
20+
updateProfileError={updateProfileError}
21+
isLoading={authState.matches("signedIn.profile.updatingProfile")}
22+
initialValues={{
23+
username: me.username,
15024
}}
151-
onConfirm={(password) => {
152-
if (!loginTypeConfirmation.selectedType) {
153-
throw new Error("No login type selected")
154-
}
155-
loginTypeMutation.mutate({
156-
to_login_type: loginTypeConfirmation.selectedType,
157-
email: me.email,
158-
password,
25+
onSubmit={(data) => {
26+
authSend({
27+
type: "UPDATE_PROFILE",
28+
data,
15929
})
16030
}}
16131
/>
162-
</Stack>
163-
)
164-
}
165-
166-
const GitHubButton = (props: ComponentProps<typeof Button>) => {
167-
return (
168-
<Button
169-
startIcon={<GitHubIcon sx={{ width: 16, height: 16 }} />}
170-
fullWidth
171-
type="submit"
172-
size="large"
173-
{...props}
174-
/>
175-
)
176-
}
177-
178-
const OIDCButton = ({
179-
authMethods,
180-
...buttonProps
181-
}: ComponentProps<typeof Button> & { authMethods: AuthMethods }) => {
182-
return (
183-
<Button
184-
size="large"
185-
startIcon={
186-
authMethods.oidc.iconUrl ? (
187-
<Box
188-
component="img"
189-
alt="Open ID Connect icon"
190-
src={authMethods.oidc.iconUrl}
191-
sx={{ width: 16, height: 16 }}
192-
/>
193-
) : (
194-
<KeyIcon sx={{ width: 16, height: 16 }} />
195-
)
196-
}
197-
fullWidth
198-
type="submit"
199-
{...buttonProps}
200-
/>
201-
)
202-
}
203-
204-
const getOIDCLabel = (authMethods: AuthMethods) => {
205-
return authMethods.oidc.signInText || "OpenID Connect"
206-
}
207-
208-
const ConfirmLoginTypeChangeModal = ({
209-
open,
210-
loading,
211-
error,
212-
onClose,
213-
onConfirm,
214-
}: {
215-
open: boolean
216-
loading: boolean
217-
error: unknown
218-
onClose: () => void
219-
onConfirm: (password: string) => void
220-
}) => {
221-
const [password, setPassword] = useState("")
222-
223-
const handleConfirm = () => {
224-
onConfirm(password)
225-
}
226-
227-
return (
228-
<ConfirmDialog
229-
open={open}
230-
onClose={() => {
231-
onClose()
232-
}}
233-
onConfirm={handleConfirm}
234-
hideCancel={false}
235-
cancelText="Cancel"
236-
confirmText="Update"
237-
title="Change login type"
238-
confirmLoading={loading}
239-
description={
240-
<Stack>
241-
<Typography>
242-
After changing your login type, you will not be able to change it
243-
again. Are you sure you want to proceed and change your login type?
244-
</Typography>
245-
<TextField
246-
autoFocus
247-
onKeyDown={(event) => {
248-
if (event.key === "Enter") {
249-
handleConfirm()
250-
}
251-
}}
252-
error={Boolean(error)}
253-
helperText={
254-
error
255-
? getErrorMessage(error, "Your password is incorrect")
256-
: undefined
257-
}
258-
name="confirm-password"
259-
id="confirm-password"
260-
value={password}
261-
onChange={(e) => setPassword(e.currentTarget.value)}
262-
label="Confirm your password"
263-
type="password"
264-
/>
265-
</Stack>
266-
}
267-
/>
32+
</Section>
26833
)
26934
}
27035

site/src/pages/UserSettingsPage/SecurityPage/SecurityPage.tsx

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ import { FC } from "react"
44
import { userSecuritySettingsMachine } from "xServices/userSecuritySettings/userSecuritySettingsXService"
55
import { Section } from "../../../components/SettingsLayout/Section"
66
import { SecurityForm } from "../../../components/SettingsSecurityForm/SettingsSecurityForm"
7-
8-
export const Language = {
9-
title: "Security",
10-
}
7+
import { useQuery } from "@tanstack/react-query"
8+
import { getAuthMethods } from "api/api"
9+
import {
10+
SingleSignOnSection,
11+
useSingleSignOnSection,
12+
} from "./SingleSignOnSection"
13+
import { Loader } from "components/Loader/Loader"
14+
import { Stack } from "components/Stack/Stack"
1115

1216
export const SecurityPage: FC = () => {
1317
const me = useMe()
@@ -20,21 +24,38 @@ export const SecurityPage: FC = () => {
2024
},
2125
)
2226
const { error } = securityState.context
27+
const { data: authMethods } = useQuery({
28+
queryKey: ["authMethods"],
29+
queryFn: getAuthMethods,
30+
})
31+
const singleSignOnSection = useSingleSignOnSection()
32+
33+
if (!authMethods) {
34+
return <Loader />
35+
}
2336

2437
return (
25-
<Section title={Language.title} description="Update your account password">
26-
<SecurityForm
27-
updateSecurityError={error}
28-
isLoading={securityState.matches("updatingSecurity")}
29-
initialValues={{ old_password: "", password: "", confirm_password: "" }}
30-
onSubmit={(data) => {
31-
securitySend({
32-
type: "UPDATE_SECURITY",
33-
data,
34-
})
35-
}}
36-
/>
37-
</Section>
38+
<Stack spacing={6}>
39+
<Section title="Security" description="Update your account password">
40+
<SecurityForm
41+
disabled={authMethods.me_login_type !== "password"}
42+
updateSecurityError={error}
43+
isLoading={securityState.matches("updatingSecurity")}
44+
initialValues={{
45+
old_password: "",
46+
password: "",
47+
confirm_password: "",
48+
}}
49+
onSubmit={(data) => {
50+
securitySend({
51+
type: "UPDATE_SECURITY",
52+
data,
53+
})
54+
}}
55+
/>
56+
</Section>
57+
<SingleSignOnSection authMethods={authMethods} {...singleSignOnSection} />
58+
</Stack>
3859
)
3960
}
4061

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy