Skip to content

feat: add user password change page #1866

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 3 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
lint
  • Loading branch information
f0ssel committed May 27, 2022
commit c485f1a3ef89de30b07bbfcbddbdc9edc6d6a4b9
6 changes: 4 additions & 2 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,10 @@ export const suspendUser = async (userId: TypesGen.User["id"]): Promise<TypesGen
return response.data
}

export const updateUserPassword = async (userId: TypesGen.User["id"], updatePassword: TypesGen.UpdateUserPasswordRequest): Promise<undefined> =>
axios.put(`/api/v2/users/${userId}/password`, updatePassword)
export const updateUserPassword = async (
userId: TypesGen.User["id"],
Copy link
Contributor

Choose a reason for hiding this comment

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

Praise: Nice typing!

updatePassword: TypesGen.UpdateUserPasswordRequest,
): Promise<undefined> => axios.put(`/api/v2/users/${userId}/password`, updatePassword)

export const getSiteRoles = async (): Promise<Array<TypesGen.Role>> => {
const response = await axios.get<Array<TypesGen.Role>>(`/api/v2/users/roles`)
Expand Down
16 changes: 11 additions & 5 deletions site/src/components/SettingsSecurityForm/SettingsSecurityForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TextField from "@material-ui/core/TextField"
import { FormikContextType, FormikErrors, useFormik } from "formik"
import React from "react"
import * as Yup from "yup"
import { getFormHelpers, nameValidator, onChangeTrimmed } from "../../util/formUtils"
import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils"
import { LoadingButton } from "../LoadingButton/LoadingButton"
import { Stack } from "../Stack/Stack"

Expand All @@ -28,10 +28,16 @@ export const Language = {

const validationSchema = Yup.object({
old_password: Yup.string().trim().required(Language.oldPasswordRequired),
password: Yup.string().trim().min(8, Language.passwordMinLength).max(64, Language.passwordMaxLength).required(Language.newPasswordRequired),
confirm_password: Yup.string().trim().test("passwords-match", Language.confirmPasswordMatch, function (value) {
return (this.parent as SecurityFormValues).password === value
})
password: Yup.string()
.trim()
.min(8, Language.passwordMinLength)
.max(64, Language.passwordMaxLength)
.required(Language.newPasswordRequired),
confirm_password: Yup.string()
.trim()
.test("passwords-match", Language.confirmPasswordMatch, function (value) {
return (this.parent as SecurityFormValues).password === value
}),
})

export type SecurityFormErrors = FormikErrors<SecurityFormValues>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { GlobalSnackbar } from "../../../components/GlobalSnackbar/GlobalSnackba
import * as SecurityForm from "../../../components/SettingsSecurityForm/SettingsSecurityForm"
import { renderWithAuth } from "../../../testHelpers/renderHelpers"
import * as AuthXService from "../../../xServices/auth/authXService"
import { SecurityPage, Language } from "./SecurityPage"
import { Language, SecurityPage } from "./SecurityPage"

const renderPage = () => {
return renderWithAuth(
Expand All @@ -19,7 +19,7 @@ const renderPage = () => {
const newData = {
old_password: "password1",
password: "password2",
confirm_password: "password2"
confirm_password: "password2",
}

const fillAndSubmitForm = async () => {
Expand All @@ -33,9 +33,7 @@ const fillAndSubmitForm = async () => {
describe("SecurityPage", () => {
describe("when it is a success", () => {
it("shows the success message", async () => {
jest.spyOn(API, "updateUserPassword").mockImplementationOnce((_userId, _data) =>
Promise.resolve(undefined),
)
jest.spyOn(API, "updateUserPassword").mockImplementationOnce((_userId, _data) => Promise.resolve(undefined))
const { user } = renderPage()
await fillAndSubmitForm()

Expand Down
4 changes: 3 additions & 1 deletion site/src/pages/SettingsPages/SecurityPage/SecurityPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export const SecurityPage: React.FC = () => {
const { me, updateSecurityError } = authState.context
const hasError = !!updateSecurityError
const formErrors =
hasError && isApiError(updateSecurityError) ? mapApiErrorToFieldErrors(updateSecurityError.response.data) : undefined
hasError && isApiError(updateSecurityError)
? mapApiErrorToFieldErrors(updateSecurityError.response.data)
: undefined
const hasUnknownError = hasError && !isApiError(updateSecurityError)

if (!me) {
Expand Down
4 changes: 2 additions & 2 deletions site/src/pages/UsersPage/UsersPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ describe("Users Page", () => {

// Check if the API was called correctly
expect(API.updateUserPassword).toBeCalledTimes(1)
expect(API.updateUserPassword).toBeCalledWith(MockUser.id, {password: expect.any(String), old_password: ""})
expect(API.updateUserPassword).toBeCalledWith(MockUser.id, { password: expect.any(String), old_password: "" })
})
})

Expand All @@ -220,7 +220,7 @@ describe("Users Page", () => {

// Check if the API was called correctly
expect(API.updateUserPassword).toBeCalledTimes(1)
expect(API.updateUserPassword).toBeCalledWith(MockUser.id, {password: expect.any(String), old_password: ""})
expect(API.updateUserPassword).toBeCalledWith(MockUser.id, { password: expect.any(String), old_password: "" })
})
})
})
Expand Down
2 changes: 1 addition & 1 deletion site/src/xServices/auth/authXService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export const authMachine =
data: TypesGen.User
}
updateSecurity: {
data: any
data: undefined
}
checkPermissions: {
data: TypesGen.UserAuthorizationResponse
Expand Down
5 changes: 4 additions & 1 deletion site/src/xServices/users/usersXService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,10 @@ export const usersMachine = createMachine(
throw new Error("newUserPassword not generated")
}

return API.updateUserPassword(context.userIdToResetPassword, {password: context.newUserPassword, old_password: ""})
return API.updateUserPassword(context.userIdToResetPassword, {
password: context.newUserPassword,
old_password: "",
})
},
updateUserRoles: (context, event) => {
if (!context.userIdToUpdateRoles) {
Expand Down