Skip to content

feat(site): add change OIDC UI #8182

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

Merged
merged 10 commits into from
Jun 28, 2023
Merged
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
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,5 @@
"go.testFlags": ["-short", "-coverpkg=./..."],
// We often use a version of TypeScript that's ahead of the version shipped
// with VS Code.
"typescript.tsdk": "./site/node_modules/typescript/lib",
"prettier.prettierPath": "./node_modules/prettier"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did we end up taking this out?

"typescript.tsdk": "./site/node_modules/typescript/lib"
}
4 changes: 2 additions & 2 deletions coderd/userauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -840,7 +840,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
// Convert the []interface{} we get to a []string.
groupsInterface, ok := groupsRaw.([]interface{})
if ok {
logger.Debug(ctx, "groups returned in oidc claims",
api.Logger.Debug(ctx, "groups returned in oidc claims",
slog.F("len", len(groupsInterface)),
slog.F("groups", groupsInterface),
)
Expand All @@ -861,7 +861,7 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
groups = append(groups, group)
}
} else {
logger.Debug(ctx, "groups field was an unknown type",
api.Logger.Debug(ctx, "groups field was an unknown type",
slog.F("type", fmt.Sprintf("%T", groupsRaw)),
)
}
Expand Down
33 changes: 6 additions & 27 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,33 +108,12 @@ export const login = async (
return response.data
}

export const convertToOauth = async (
email: string,
password: string,
to_login_type: string,
): Promise<TypesGen.OauthConversionResponse | undefined> => {
const payload = JSON.stringify({
email,
password,
to_login_type,
})

try {
const response = await axios.post<TypesGen.OauthConversionResponse>(
"/api/v2/users/convert-login",
payload,
{
headers: { ...CONTENT_TYPE_JSON },
},
)
return response.data
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
return undefined
}

throw error
}
export const convertToOAUTH = async (request: TypesGen.ConvertLoginRequest) => {
const response = await axios.post<TypesGen.OauthConversionResponse>(
"/api/v2/users/convert-login",
request,
)
return response.data
}

export const logout = async (): Promise<void> => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe("AccountForm", () => {
const el = await screen.findByLabelText("Username")
expect(el).toBeEnabled()
const btn = await screen.findByRole("button", {
name: /Update settings/i,
name: /Update account/i,
})
expect(btn).toBeEnabled()
})
Expand Down Expand Up @@ -61,7 +61,7 @@ describe("AccountForm", () => {
const el = await screen.findByLabelText("Username")
expect(el).toBeDisabled()
const btn = await screen.findByRole("button", {
name: /Update settings/i,
name: /Update account/i,
})
expect(btn).toBeDisabled()
})
Expand Down
12 changes: 6 additions & 6 deletions site/src/components/SettingsAccountForm/SettingsAccountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
onChangeTrimmed,
} from "../../utils/formUtils"
import { LoadingButton } from "../LoadingButton/LoadingButton"
import { Stack } from "../Stack/Stack"
import { ErrorAlert } from "components/Alert/ErrorAlert"
import { Form, FormFields } from "components/Form/Form"

export interface AccountFormValues {
username: string
Expand All @@ -18,7 +18,7 @@ export interface AccountFormValues {
export const Language = {
usernameLabel: "Username",
emailLabel: "Email",
updateSettings: "Update settings",
updateSettings: "Update account",
}

const validationSchema = Yup.object({
Expand Down Expand Up @@ -59,8 +59,8 @@ export const AccountForm: FC<React.PropsWithChildren<AccountFormProps>> = ({

return (
<>
<form onSubmit={form.handleSubmit}>
<Stack>
<Form onSubmit={form.handleSubmit}>
<FormFields>
{Boolean(updateProfileError) && (
<ErrorAlert error={updateProfileError} />
)}
Expand Down Expand Up @@ -91,8 +91,8 @@ export const AccountForm: FC<React.PropsWithChildren<AccountFormProps>> = ({
{isLoading ? "" : Language.updateSettings}
</LoadingButton>
</div>
</Stack>
</form>
</FormFields>
</Form>
</>
)
}
11 changes: 9 additions & 2 deletions site/src/components/SettingsLayout/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { SectionAction } from "../SectionAction/SectionAction"
type SectionLayout = "fixed" | "fluid"

export interface SectionProps {
// Useful for testing
id?: string
title?: ReactNode | string
description?: ReactNode
toolbar?: ReactNode
Expand All @@ -20,6 +22,7 @@ type SectionFC = FC<PropsWithChildren<SectionProps>> & {
}

export const Section: SectionFC = ({
id,
title,
description,
toolbar,
Expand All @@ -30,12 +33,16 @@ export const Section: SectionFC = ({
}) => {
const styles = useStyles({ layout })
return (
<section className={className}>
<section className={className} id={id} data-testid={id}>
<div className={styles.inner}>
{(title || description) && (
<div className={styles.header}>
<div>
{title && <Typography variant="h4">{title}</Typography>}
{title && (
<Typography variant="h4" sx={{ fontSize: 24 }}>
{title}
</Typography>
)}
{description && typeof description === "string" && (
<Typography className={styles.description}>
{description}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ const Template: Story<SecurityFormProps> = (args: SecurityFormProps) => (
export const Example = Template.bind({})
Example.args = {
isLoading: false,
initialValues: {
old_password: "",
password: "",
confirm_password: "",
},
updateSecurityError: undefined,
onSubmit: () => {
return Promise.resolve()
},
Expand All @@ -37,7 +31,7 @@ Loading.args = {
export const WithError = Template.bind({})
WithError.args = {
...Example.args,
updateSecurityError: mockApiError({
error: mockApiError({
message: "Old password is incorrect",
validations: [
{
Expand All @@ -46,7 +40,4 @@ WithError.args = {
},
],
}),
initialTouched: {
old_password: true,
},
}
48 changes: 26 additions & 22 deletions site/src/components/SettingsSecurityForm/SettingsSecurityForm.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import TextField from "@mui/material/TextField"
import { FormikContextType, FormikTouched, useFormik } from "formik"
import { FormikContextType, useFormik } from "formik"
import { FC } from "react"
import * as Yup from "yup"
import { getFormHelpers } from "../../utils/formUtils"
import { LoadingButton } from "../LoadingButton/LoadingButton"
import { Stack } from "../Stack/Stack"
import { ErrorAlert } from "components/Alert/ErrorAlert"
import { Form, FormFields } from "components/Form/Form"
import { Alert } from "components/Alert/Alert"

interface SecurityFormValues {
old_password: string
Expand Down Expand Up @@ -41,40 +42,43 @@ const validationSchema = Yup.object({
})

export interface SecurityFormProps {
disabled: boolean
isLoading: boolean
initialValues: SecurityFormValues
onSubmit: (values: SecurityFormValues) => void
updateSecurityError?: Error | unknown
// initialTouched is only used for testing the error state of the form.
initialTouched?: FormikTouched<SecurityFormValues>
error?: unknown
}

export const SecurityForm: FC<SecurityFormProps> = ({
disabled,
isLoading,
onSubmit,
initialValues,
updateSecurityError,
initialTouched,
error,
}) => {
const form: FormikContextType<SecurityFormValues> =
useFormik<SecurityFormValues>({
initialValues,
initialValues: {
old_password: "",
password: "",
confirm_password: "",
},
validationSchema,
onSubmit,
initialTouched,
})
const getFieldHelpers = getFormHelpers<SecurityFormValues>(
form,
updateSecurityError,
)
const getFieldHelpers = getFormHelpers<SecurityFormValues>(form, error)

if (disabled) {
return (
<Alert severity="info">
Password changes are only allowed for password based accounts.
</Alert>
)
}

return (
<>
<form onSubmit={form.handleSubmit}>
<Stack>
{Boolean(updateSecurityError) && (
<ErrorAlert error={updateSecurityError} />
)}
<Form onSubmit={form.handleSubmit}>
<FormFields>
{Boolean(error) && <ErrorAlert error={error} />}
<TextField
{...getFieldHelpers("old_password")}
autoComplete="old_password"
Expand Down Expand Up @@ -106,8 +110,8 @@ export const SecurityForm: FC<SecurityFormProps> = ({
{isLoading ? "" : Language.updatePassword}
</LoadingButton>
</div>
</Stack>
</form>
</FormFields>
</Form>
</>
)
}
Loading