refactor: Extract security logic from auth service (#5635)

This commit is contained in:
Bruno Quaresma
2023-01-09 13:18:32 -07:00
committed by GitHub
parent fe16b2a06d
commit f70726b43c
6 changed files with 97 additions and 71 deletions
+2
View File
@@ -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,
}
+3
View File
@@ -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,
})
-54
View File
@@ -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,
}),
},
},
)