mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
refactor: Extract security logic from auth service (#5635)
This commit is contained in:
@@ -17,6 +17,7 @@ import appearanceSettings from "./appearanceSettings.json"
|
||||
import starterTemplatesPage from "./starterTemplatesPage.json"
|
||||
import starterTemplatePage from "./starterTemplatePage.json"
|
||||
import createTemplatePage from "./createTemplatePage.json"
|
||||
import userSettingsPage from "./userSettingsPage.json"
|
||||
|
||||
export const en = {
|
||||
common,
|
||||
@@ -38,4 +39,5 @@ export const en = {
|
||||
starterTemplatesPage,
|
||||
starterTemplatePage,
|
||||
createTemplatePage,
|
||||
userSettingsPage,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"securityUpdateSuccessMessage": "Updated password."
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import * as API from "../../../api/api"
|
||||
import { GlobalSnackbar } from "../../../components/GlobalSnackbar/GlobalSnackbar"
|
||||
import * as SecurityForm from "../../../components/SettingsSecurityForm/SettingsSecurityForm"
|
||||
import { renderWithAuth } from "../../../testHelpers/renderHelpers"
|
||||
import * as AuthXService from "../../../xServices/auth/authXService"
|
||||
import { SecurityPage } from "./SecurityPage"
|
||||
import i18next from "i18next"
|
||||
|
||||
@@ -47,9 +46,10 @@ describe("SecurityPage", () => {
|
||||
const { user } = renderPage()
|
||||
await fillAndSubmitForm()
|
||||
|
||||
const successMessage = await screen.findByText(
|
||||
AuthXService.Language.successSecurityUpdate,
|
||||
)
|
||||
const expectedMessage = t("securityUpdateSuccessMessage", {
|
||||
ns: "userSettingsPage",
|
||||
})
|
||||
const successMessage = await screen.findByText(expectedMessage)
|
||||
expect(successMessage).toBeDefined()
|
||||
expect(API.updateUserPassword).toBeCalledTimes(1)
|
||||
expect(API.updateUserPassword).toBeCalledWith(user.id, newData)
|
||||
|
||||
@@ -1,30 +1,34 @@
|
||||
import { useActor } from "@xstate/react"
|
||||
import React, { useContext } from "react"
|
||||
import { useMachine } from "@xstate/react"
|
||||
import { useMe } from "hooks/useMe"
|
||||
import React from "react"
|
||||
import { userSecuritySettingsMachine } from "xServices/userSecuritySettings/userSecuritySettingsXService"
|
||||
import { Section } from "../../../components/Section/Section"
|
||||
import { SecurityForm } from "../../../components/SettingsSecurityForm/SettingsSecurityForm"
|
||||
import { XServiceContext } from "../../../xServices/StateContext"
|
||||
|
||||
export const Language = {
|
||||
title: "Security",
|
||||
}
|
||||
|
||||
export const SecurityPage: React.FC = () => {
|
||||
const xServices = useContext(XServiceContext)
|
||||
const [authState, authSend] = useActor(xServices.authXService)
|
||||
const { me, updateSecurityError } = authState.context
|
||||
|
||||
if (!me) {
|
||||
throw new Error("No current user found")
|
||||
}
|
||||
const me = useMe()
|
||||
const [securityState, securitySend] = useMachine(
|
||||
userSecuritySettingsMachine,
|
||||
{
|
||||
context: {
|
||||
userId: me.id,
|
||||
},
|
||||
},
|
||||
)
|
||||
const { error } = securityState.context
|
||||
|
||||
return (
|
||||
<Section title={Language.title}>
|
||||
<SecurityForm
|
||||
updateSecurityError={updateSecurityError}
|
||||
isLoading={authState.matches("signedIn.security.updatingSecurity")}
|
||||
updateSecurityError={error}
|
||||
isLoading={securityState.matches("updatingSecurity")}
|
||||
initialValues={{ old_password: "", password: "", confirm_password: "" }}
|
||||
onSubmit={(data) => {
|
||||
authSend({
|
||||
securitySend({
|
||||
type: "UPDATE_SECURITY",
|
||||
data,
|
||||
})
|
||||
|
||||
@@ -5,7 +5,6 @@ import { displaySuccess } from "../../components/GlobalSnackbar/utils"
|
||||
|
||||
export const Language = {
|
||||
successProfileUpdate: "Updated settings.",
|
||||
successSecurityUpdate: "Updated password.",
|
||||
successRegenerateSSHKey: "SSH Key regenerated successfully",
|
||||
}
|
||||
|
||||
@@ -80,7 +79,6 @@ export interface AuthContext {
|
||||
getMethodsError?: Error | unknown
|
||||
authError?: Error | unknown
|
||||
updateProfileError?: Error | unknown
|
||||
updateSecurityError?: Error | unknown
|
||||
me?: TypesGen.User
|
||||
methods?: TypesGen.AuthMethods
|
||||
permissions?: Permissions
|
||||
@@ -95,7 +93,6 @@ export type AuthEvent =
|
||||
| { type: "SIGN_OUT" }
|
||||
| { type: "SIGN_IN"; email: string; password: string }
|
||||
| { type: "UPDATE_PROFILE"; data: TypesGen.UpdateUserProfileRequest }
|
||||
| { type: "UPDATE_SECURITY"; data: TypesGen.UpdateUserPasswordRequest }
|
||||
| { type: "GET_SSH_KEY" }
|
||||
| { type: "REGENERATE_SSH_KEY" }
|
||||
| { type: "CONFIRM_REGENERATE_SSH_KEY" }
|
||||
@@ -385,41 +382,6 @@ export const authMachine =
|
||||
},
|
||||
},
|
||||
},
|
||||
security: {
|
||||
initial: "idle",
|
||||
states: {
|
||||
idle: {
|
||||
initial: "noError",
|
||||
states: {
|
||||
noError: {},
|
||||
error: {},
|
||||
},
|
||||
on: {
|
||||
UPDATE_SECURITY: {
|
||||
target: "updatingSecurity",
|
||||
},
|
||||
},
|
||||
},
|
||||
updatingSecurity: {
|
||||
entry: "clearUpdateSecurityError",
|
||||
invoke: {
|
||||
src: "updateSecurity",
|
||||
onDone: [
|
||||
{
|
||||
actions: "notifySuccessSecurityUpdate",
|
||||
target: "#authState.signedIn.security.idle.noError",
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: "assignUpdateSecurityError",
|
||||
target: "#authState.signedIn.security.idle.error",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
on: {
|
||||
SIGN_OUT: {
|
||||
@@ -513,13 +475,6 @@ export const authMachine =
|
||||
|
||||
return API.updateProfile(context.me.id, event.data)
|
||||
},
|
||||
updateSecurity: async (context, event) => {
|
||||
if (!context.me) {
|
||||
throw new Error("No current user found")
|
||||
}
|
||||
|
||||
return API.updateUserPassword(context.me.id, event.data)
|
||||
},
|
||||
checkPermissions: async () => {
|
||||
return API.checkAuthorization({
|
||||
checks: permissionsToCheck,
|
||||
@@ -572,15 +527,6 @@ export const authMachine =
|
||||
clearUpdateProfileError: assign({
|
||||
updateProfileError: (_) => undefined,
|
||||
}),
|
||||
clearUpdateSecurityError: assign({
|
||||
updateSecurityError: (_) => undefined,
|
||||
}),
|
||||
notifySuccessSecurityUpdate: () => {
|
||||
displaySuccess(Language.successSecurityUpdate)
|
||||
},
|
||||
assignUpdateSecurityError: assign({
|
||||
updateSecurityError: (_, event) => event.data,
|
||||
}),
|
||||
assignPermissions: assign({
|
||||
// Setting event.data as Permissions to be more stricted. So we know
|
||||
// what permissions we asked for.
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { assign, createMachine } from "xstate"
|
||||
import * as API from "api/api"
|
||||
import { UpdateUserPasswordRequest } from "api/typesGenerated"
|
||||
import { displaySuccess } from "components/GlobalSnackbar/utils"
|
||||
import { t } from "i18next"
|
||||
|
||||
interface Context {
|
||||
userId: string
|
||||
error?: unknown
|
||||
}
|
||||
|
||||
type Events = { type: "UPDATE_SECURITY"; data: UpdateUserPasswordRequest }
|
||||
|
||||
export const userSecuritySettingsMachine = createMachine(
|
||||
{
|
||||
id: "userSecuritySettings",
|
||||
predictableActionArguments: true,
|
||||
schema: {
|
||||
context: {} as Context,
|
||||
events: {} as Events,
|
||||
},
|
||||
tsTypes: {} as import("./userSecuritySettingsXService.typegen").Typegen0,
|
||||
initial: "idle",
|
||||
states: {
|
||||
idle: {
|
||||
on: {
|
||||
UPDATE_SECURITY: {
|
||||
target: "updatingSecurity",
|
||||
},
|
||||
},
|
||||
},
|
||||
updatingSecurity: {
|
||||
entry: "clearError",
|
||||
invoke: {
|
||||
src: "updateSecurity",
|
||||
onDone: [
|
||||
{
|
||||
actions: "notifyUpdate",
|
||||
target: "idle",
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: "assignError",
|
||||
target: "idle",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
services: {
|
||||
updateSecurity: async ({ userId }, { data }) =>
|
||||
API.updateUserPassword(userId, data),
|
||||
},
|
||||
actions: {
|
||||
clearError: assign({
|
||||
error: (_) => undefined,
|
||||
}),
|
||||
notifyUpdate: () => {
|
||||
displaySuccess(
|
||||
t("securityUpdateSuccessMessage", { ns: "userSettingsPage" }),
|
||||
)
|
||||
},
|
||||
assignError: assign({
|
||||
error: (_, event) => event.data,
|
||||
}),
|
||||
},
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user