mirror of
https://github.com/coder/coder.git
synced 2025-07-09 11:45:56 +00:00
refactor: Extract ssh logic from auth service (#5670)
* refactor: Extract ssh logic from auth service * Update site/src/i18n/en/userSettingsPage.json Co-authored-by: Kira Pilot <kira@coder.com> Co-authored-by: Kira Pilot <kira@coder.com>
This commit is contained in:
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"securityUpdateSuccessMessage": "Updated password."
|
"securityUpdateSuccessMessage": "Updated password.",
|
||||||
|
"sshRegenerateSuccessMessage": "SSH Key regenerated successfully."
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,11 @@ import {
|
|||||||
MockGitSSHKey,
|
MockGitSSHKey,
|
||||||
renderWithAuth,
|
renderWithAuth,
|
||||||
} from "../../../testHelpers/renderHelpers"
|
} from "../../../testHelpers/renderHelpers"
|
||||||
import { Language as authXServiceLanguage } from "../../../xServices/auth/authXService"
|
|
||||||
import { Language as SSHKeysPageLanguage, SSHKeysPage } from "./SSHKeysPage"
|
import { Language as SSHKeysPageLanguage, SSHKeysPage } from "./SSHKeysPage"
|
||||||
import { Language as SSHKeysPageViewLanguage } from "./SSHKeysPageView"
|
import { Language as SSHKeysPageViewLanguage } from "./SSHKeysPageView"
|
||||||
|
import { i18n } from "i18n"
|
||||||
|
|
||||||
|
const { t } = i18n
|
||||||
|
|
||||||
describe("SSH keys Page", () => {
|
describe("SSH keys Page", () => {
|
||||||
it("shows the SSH key", async () => {
|
it("shows the SSH key", async () => {
|
||||||
@ -52,7 +54,10 @@ describe("SSH keys Page", () => {
|
|||||||
fireEvent.click(confirmButton)
|
fireEvent.click(confirmButton)
|
||||||
|
|
||||||
// Check if the success message is displayed
|
// Check if the success message is displayed
|
||||||
await screen.findByText(authXServiceLanguage.successRegenerateSSHKey)
|
const successMessage = t("sshRegenerateSuccessMessage", {
|
||||||
|
ns: "userSettingsPage",
|
||||||
|
})
|
||||||
|
await screen.findByText(successMessage)
|
||||||
|
|
||||||
// Check if the API was called correctly
|
// Check if the API was called correctly
|
||||||
expect(API.regenerateUserSSHKey).toBeCalledTimes(1)
|
expect(API.regenerateUserSSHKey).toBeCalledTimes(1)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { useActor } from "@xstate/react"
|
import { useMachine } from "@xstate/react"
|
||||||
import { useContext, useEffect, PropsWithChildren, FC } from "react"
|
import { PropsWithChildren, FC } from "react"
|
||||||
|
import { sshKeyMachine } from "xServices/sshKey/sshKeyXService"
|
||||||
import { ConfirmDialog } from "../../../components/Dialogs/ConfirmDialog/ConfirmDialog"
|
import { ConfirmDialog } from "../../../components/Dialogs/ConfirmDialog/ConfirmDialog"
|
||||||
import { Section } from "../../../components/SettingsLayout/Section"
|
import { Section } from "../../../components/SettingsLayout/Section"
|
||||||
import { XServiceContext } from "../../../xServices/StateContext"
|
|
||||||
import { SSHKeysPageView } from "./SSHKeysPageView"
|
import { SSHKeysPageView } from "./SSHKeysPageView"
|
||||||
|
|
||||||
export const Language = {
|
export const Language = {
|
||||||
@ -15,19 +15,13 @@ export const Language = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SSHKeysPage: FC<PropsWithChildren<unknown>> = () => {
|
export const SSHKeysPage: FC<PropsWithChildren<unknown>> = () => {
|
||||||
const xServices = useContext(XServiceContext)
|
const [sshState, sshSend] = useMachine(sshKeyMachine)
|
||||||
const [authState, authSend] = useActor(xServices.authXService)
|
const isLoading = sshState.matches("gettingSSHKey")
|
||||||
const { sshKey, getSSHKeyError, regenerateSSHKeyError } = authState.context
|
const hasLoaded = sshState.matches("loaded")
|
||||||
|
const { getSSHKeyError, regenerateSSHKeyError, sshKey } = sshState.context
|
||||||
useEffect(() => {
|
|
||||||
authSend({ type: "GET_SSH_KEY" })
|
|
||||||
}, [authSend])
|
|
||||||
|
|
||||||
const isLoading = authState.matches("signedIn.ssh.gettingSSHKey")
|
|
||||||
const hasLoaded = authState.matches("signedIn.ssh.loaded")
|
|
||||||
|
|
||||||
const onRegenerateClick = () => {
|
const onRegenerateClick = () => {
|
||||||
authSend({ type: "REGENERATE_SSH_KEY" })
|
sshSend({ type: "REGENERATE_SSH_KEY" })
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -46,17 +40,15 @@ export const SSHKeysPage: FC<PropsWithChildren<unknown>> = () => {
|
|||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
type="delete"
|
type="delete"
|
||||||
hideCancel={false}
|
hideCancel={false}
|
||||||
open={authState.matches("signedIn.ssh.loaded.confirmSSHKeyRegenerate")}
|
open={sshState.matches("confirmSSHKeyRegenerate")}
|
||||||
confirmLoading={authState.matches(
|
confirmLoading={sshState.matches("regeneratingSSHKey")}
|
||||||
"signedIn.ssh.loaded.regeneratingSSHKey",
|
|
||||||
)}
|
|
||||||
title={Language.regenerateDialogTitle}
|
title={Language.regenerateDialogTitle}
|
||||||
confirmText={Language.confirmLabel}
|
confirmText={Language.confirmLabel}
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
authSend({ type: "CONFIRM_REGENERATE_SSH_KEY" })
|
sshSend({ type: "CONFIRM_REGENERATE_SSH_KEY" })
|
||||||
}}
|
}}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
authSend({ type: "CANCEL_REGENERATE_SSH_KEY" })
|
sshSend({ type: "CANCEL_REGENERATE_SSH_KEY" })
|
||||||
}}
|
}}
|
||||||
description={<>{Language.regenerateDialogMessage}</>}
|
description={<>{Language.regenerateDialogMessage}</>}
|
||||||
/>
|
/>
|
||||||
|
@ -5,7 +5,6 @@ import { displaySuccess } from "../../components/GlobalSnackbar/utils"
|
|||||||
|
|
||||||
export const Language = {
|
export const Language = {
|
||||||
successProfileUpdate: "Updated settings.",
|
successProfileUpdate: "Updated settings.",
|
||||||
successRegenerateSSHKey: "SSH Key regenerated successfully",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checks = {
|
export const checks = {
|
||||||
@ -83,20 +82,12 @@ export interface AuthContext {
|
|||||||
methods?: TypesGen.AuthMethods
|
methods?: TypesGen.AuthMethods
|
||||||
permissions?: Permissions
|
permissions?: Permissions
|
||||||
checkPermissionsError?: Error | unknown
|
checkPermissionsError?: Error | unknown
|
||||||
// SSH
|
|
||||||
sshKey?: TypesGen.GitSSHKey
|
|
||||||
getSSHKeyError?: Error | unknown
|
|
||||||
regenerateSSHKeyError?: Error | unknown
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AuthEvent =
|
export type AuthEvent =
|
||||||
| { type: "SIGN_OUT" }
|
| { type: "SIGN_OUT" }
|
||||||
| { type: "SIGN_IN"; email: string; password: string }
|
| { type: "SIGN_IN"; email: string; password: string }
|
||||||
| { type: "UPDATE_PROFILE"; data: TypesGen.UpdateUserProfileRequest }
|
| { type: "UPDATE_PROFILE"; data: TypesGen.UpdateUserProfileRequest }
|
||||||
| { type: "GET_SSH_KEY" }
|
|
||||||
| { type: "REGENERATE_SSH_KEY" }
|
|
||||||
| { type: "CONFIRM_REGENERATE_SSH_KEY" }
|
|
||||||
| { type: "CANCEL_REGENERATE_SSH_KEY" }
|
|
||||||
| { type: "GET_AUTH_METHODS" }
|
| { type: "GET_AUTH_METHODS" }
|
||||||
|
|
||||||
export const authMachine =
|
export const authMachine =
|
||||||
@ -128,12 +119,6 @@ export const authMachine =
|
|||||||
checkPermissions: {
|
checkPermissions: {
|
||||||
data: TypesGen.AuthorizationResponse
|
data: TypesGen.AuthorizationResponse
|
||||||
}
|
}
|
||||||
getSSHKey: {
|
|
||||||
data: TypesGen.GitSSHKey
|
|
||||||
}
|
|
||||||
regenerateSSHKey: {
|
|
||||||
data: TypesGen.GitSSHKey
|
|
||||||
}
|
|
||||||
hasFirstUser: {
|
hasFirstUser: {
|
||||||
data: boolean
|
data: boolean
|
||||||
}
|
}
|
||||||
@ -279,79 +264,6 @@ export const authMachine =
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ssh: {
|
|
||||||
initial: "idle",
|
|
||||||
states: {
|
|
||||||
idle: {
|
|
||||||
on: {
|
|
||||||
GET_SSH_KEY: {
|
|
||||||
target: "gettingSSHKey",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
gettingSSHKey: {
|
|
||||||
entry: "clearGetSSHKeyError",
|
|
||||||
invoke: {
|
|
||||||
src: "getSSHKey",
|
|
||||||
onDone: [
|
|
||||||
{
|
|
||||||
actions: "assignSSHKey",
|
|
||||||
target: "loaded",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
onError: [
|
|
||||||
{
|
|
||||||
actions: "assignGetSSHKeyError",
|
|
||||||
target: "idle",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
loaded: {
|
|
||||||
initial: "idle",
|
|
||||||
states: {
|
|
||||||
idle: {
|
|
||||||
on: {
|
|
||||||
REGENERATE_SSH_KEY: {
|
|
||||||
target: "confirmSSHKeyRegenerate",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
confirmSSHKeyRegenerate: {
|
|
||||||
on: {
|
|
||||||
CANCEL_REGENERATE_SSH_KEY: {
|
|
||||||
target: "idle",
|
|
||||||
},
|
|
||||||
CONFIRM_REGENERATE_SSH_KEY: {
|
|
||||||
target: "regeneratingSSHKey",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
regeneratingSSHKey: {
|
|
||||||
entry: "clearRegenerateSSHKeyError",
|
|
||||||
invoke: {
|
|
||||||
src: "regenerateSSHKey",
|
|
||||||
onDone: [
|
|
||||||
{
|
|
||||||
actions: [
|
|
||||||
"assignSSHKey",
|
|
||||||
"notifySuccessSSHKeyRegenerated",
|
|
||||||
],
|
|
||||||
target: "idle",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
onError: [
|
|
||||||
{
|
|
||||||
actions: "assignRegenerateSSHKeyError",
|
|
||||||
target: "idle",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
initial: "idle",
|
initial: "idle",
|
||||||
states: {
|
states: {
|
||||||
@ -480,9 +392,6 @@ export const authMachine =
|
|||||||
checks: permissionsToCheck,
|
checks: permissionsToCheck,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
// SSH
|
|
||||||
getSSHKey: () => API.getUserSSHKey(),
|
|
||||||
regenerateSSHKey: () => API.regenerateUserSSHKey(),
|
|
||||||
// First user
|
// First user
|
||||||
hasFirstUser: () => API.hasFirstUser(),
|
hasFirstUser: () => API.hasFirstUser(),
|
||||||
},
|
},
|
||||||
@ -538,25 +447,6 @@ export const authMachine =
|
|||||||
clearGetPermissionsError: assign({
|
clearGetPermissionsError: assign({
|
||||||
checkPermissionsError: (_) => undefined,
|
checkPermissionsError: (_) => undefined,
|
||||||
}),
|
}),
|
||||||
// SSH
|
|
||||||
assignSSHKey: assign({
|
|
||||||
sshKey: (_, event) => event.data,
|
|
||||||
}),
|
|
||||||
assignGetSSHKeyError: assign({
|
|
||||||
getSSHKeyError: (_, event) => event.data,
|
|
||||||
}),
|
|
||||||
clearGetSSHKeyError: assign({
|
|
||||||
getSSHKeyError: (_) => undefined,
|
|
||||||
}),
|
|
||||||
assignRegenerateSSHKeyError: assign({
|
|
||||||
regenerateSSHKeyError: (_, event) => event.data,
|
|
||||||
}),
|
|
||||||
clearRegenerateSSHKeyError: assign({
|
|
||||||
regenerateSSHKeyError: (_) => undefined,
|
|
||||||
}),
|
|
||||||
notifySuccessSSHKeyRegenerated: () => {
|
|
||||||
displaySuccess(Language.successRegenerateSSHKey)
|
|
||||||
},
|
|
||||||
redirect: (_, { data }) => {
|
redirect: (_, { data }) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- data can be undefined
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- data can be undefined
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
125
site/src/xServices/sshKey/sshKeyXService.ts
Normal file
125
site/src/xServices/sshKey/sshKeyXService.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import { getUserSSHKey, regenerateUserSSHKey } from "api/api"
|
||||||
|
import { GitSSHKey } from "api/typesGenerated"
|
||||||
|
import { displaySuccess } from "components/GlobalSnackbar/utils"
|
||||||
|
import { createMachine, assign } from "xstate"
|
||||||
|
import { i18n } from "i18n"
|
||||||
|
|
||||||
|
const { t } = i18n
|
||||||
|
|
||||||
|
interface Context {
|
||||||
|
sshKey?: GitSSHKey
|
||||||
|
getSSHKeyError?: unknown
|
||||||
|
regenerateSSHKeyError?: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
type Events =
|
||||||
|
| { type: "REGENERATE_SSH_KEY" }
|
||||||
|
| { type: "CONFIRM_REGENERATE_SSH_KEY" }
|
||||||
|
| { type: "CANCEL_REGENERATE_SSH_KEY" }
|
||||||
|
|
||||||
|
export const sshKeyMachine = createMachine(
|
||||||
|
{
|
||||||
|
id: "sshKeyState",
|
||||||
|
predictableActionArguments: true,
|
||||||
|
schema: {
|
||||||
|
context: {} as Context,
|
||||||
|
events: {} as Events,
|
||||||
|
services: {} as {
|
||||||
|
getSSHKey: {
|
||||||
|
data: GitSSHKey
|
||||||
|
}
|
||||||
|
regenerateSSHKey: {
|
||||||
|
data: GitSSHKey
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tsTypes: {} as import("./sshKeyXService.typegen").Typegen0,
|
||||||
|
initial: "gettingSSHKey",
|
||||||
|
states: {
|
||||||
|
gettingSSHKey: {
|
||||||
|
entry: "clearGetSSHKeyError",
|
||||||
|
invoke: {
|
||||||
|
src: "getSSHKey",
|
||||||
|
onDone: [
|
||||||
|
{
|
||||||
|
actions: "assignSSHKey",
|
||||||
|
target: "loaded",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onError: [
|
||||||
|
{
|
||||||
|
actions: "assignGetSSHKeyError",
|
||||||
|
target: "notLoaded",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
notLoaded: {
|
||||||
|
type: "final",
|
||||||
|
},
|
||||||
|
loaded: {
|
||||||
|
on: {
|
||||||
|
REGENERATE_SSH_KEY: {
|
||||||
|
target: "confirmSSHKeyRegenerate",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
confirmSSHKeyRegenerate: {
|
||||||
|
on: {
|
||||||
|
CANCEL_REGENERATE_SSH_KEY: {
|
||||||
|
target: "loaded",
|
||||||
|
},
|
||||||
|
CONFIRM_REGENERATE_SSH_KEY: {
|
||||||
|
target: "regeneratingSSHKey",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
regeneratingSSHKey: {
|
||||||
|
entry: "clearRegenerateSSHKeyError",
|
||||||
|
invoke: {
|
||||||
|
src: "regenerateSSHKey",
|
||||||
|
onDone: [
|
||||||
|
{
|
||||||
|
actions: ["assignSSHKey", "notifySuccessSSHKeyRegenerated"],
|
||||||
|
target: "loaded",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onError: [
|
||||||
|
{
|
||||||
|
actions: "assignRegenerateSSHKeyError",
|
||||||
|
target: "loaded",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
services: {
|
||||||
|
getSSHKey: () => getUserSSHKey(),
|
||||||
|
regenerateSSHKey: () => regenerateUserSSHKey(),
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
assignSSHKey: assign({
|
||||||
|
sshKey: (_, { data }) => data,
|
||||||
|
}),
|
||||||
|
assignGetSSHKeyError: assign({
|
||||||
|
getSSHKeyError: (_, { data }) => data,
|
||||||
|
}),
|
||||||
|
clearGetSSHKeyError: assign({
|
||||||
|
getSSHKeyError: (_) => undefined,
|
||||||
|
}),
|
||||||
|
assignRegenerateSSHKeyError: assign({
|
||||||
|
regenerateSSHKeyError: (_, { data }) => data,
|
||||||
|
}),
|
||||||
|
clearRegenerateSSHKeyError: assign({
|
||||||
|
regenerateSSHKeyError: (_) => undefined,
|
||||||
|
}),
|
||||||
|
notifySuccessSSHKeyRegenerated: () => {
|
||||||
|
displaySuccess(
|
||||||
|
t("sshRegenerateSuccessMessage", { ns: "userSettingsPage" }),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
Reference in New Issue
Block a user