Revise ssh host alias field handling/validation

This commit is contained in:
Tuan Dang
2025-04-27 14:34:26 -07:00
parent 3d89a7f45d
commit 44ae0519d1
5 changed files with 14 additions and 30 deletions
+3 -19
View File
@@ -1,4 +1,3 @@
import slugify from "@sindresorhus/slugify";
import { z } from "zod"; import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
@@ -8,6 +7,7 @@ import { isValidHostname } from "@app/ee/services/ssh-host/ssh-host-validators";
import { SSH_HOSTS } from "@app/lib/api-docs"; import { SSH_HOSTS } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms"; import { ms } from "@app/lib/ms";
import { publicSshCaLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { publicSshCaLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
@@ -102,15 +102,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
message: "Hostname must be a valid hostname" message: "Hostname must be a valid hostname"
}) })
.describe(SSH_HOSTS.CREATE.hostname), .describe(SSH_HOSTS.CREATE.hostname),
alias: z alias: slugSchema({ min: 0, max: 64, field: "alias" }).describe(SSH_HOSTS.CREATE.alias).default(""),
.string()
.trim()
.nullable()
.default(null)
.refine((v) => v == null || slugify(v) === v, {
message: "Alias must be a valid slug"
})
.describe(SSH_HOSTS.CREATE.alias),
userCertTtl: z userCertTtl: z
.string() .string()
.refine((val) => ms(val) > 0, "TTL must be a positive number") .refine((val) => ms(val) > 0, "TTL must be a positive number")
@@ -185,15 +177,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
}) })
.optional() .optional()
.describe(SSH_HOSTS.UPDATE.hostname), .describe(SSH_HOSTS.UPDATE.hostname),
alias: z alias: slugSchema({ min: 0, max: 64, field: "alias" }).describe(SSH_HOSTS.UPDATE.alias).optional(),
.string()
.trim()
.nullable()
.refine((v) => v == null || slugify(v) === v, {
message: "Alias must be a valid slug"
})
.optional()
.describe(SSH_HOSTS.UPDATE.alias),
userCertTtl: z userCertTtl: z
.string() .string()
.refine((val) => ms(val) > 0, "TTL must be a positive number") .refine((val) => ms(val) > 0, "TTL must be a positive number")
@@ -193,7 +193,7 @@ export const sshHostServiceFactory = ({
{ {
projectId, projectId,
hostname, hostname,
alias, alias: alias === "" ? null : alias,
userCertTtl, userCertTtl,
hostCertTtl, hostCertTtl,
userSshCaId, userSshCaId,
@@ -300,7 +300,7 @@ export const sshHostServiceFactory = ({
sshHostId, sshHostId,
{ {
hostname, hostname,
alias, alias: alias === "" ? null : alias,
userCertTtl, userCertTtl,
hostCertTtl hostCertTtl
}, },
@@ -4,7 +4,7 @@ export type TListSshHostsDTO = Omit<TProjectPermission, "projectId">;
export type TCreateSshHostDTO = { export type TCreateSshHostDTO = {
hostname: string; hostname: string;
alias: string | null; alias?: string;
userCertTtl: string; userCertTtl: string;
hostCertTtl: string; hostCertTtl: string;
loginMappings: { loginMappings: {
@@ -20,7 +20,7 @@ export type TCreateSshHostDTO = {
export type TUpdateSshHostDTO = { export type TUpdateSshHostDTO = {
sshHostId: string; sshHostId: string;
hostname?: string; hostname?: string;
alias?: string | null; alias?: string;
userCertTtl?: string; userCertTtl?: string;
hostCertTtl?: string; hostCertTtl?: string;
loginMappings?: { loginMappings?: {
+2 -2
View File
@@ -16,7 +16,7 @@ export type TSshHost = {
export type TCreateSshHostDTO = { export type TCreateSshHostDTO = {
projectId: string; projectId: string;
hostname: string; hostname: string;
alias: string | null; alias?: string;
userCertTtl?: string; userCertTtl?: string;
hostCertTtl?: string; hostCertTtl?: string;
loginMappings: { loginMappings: {
@@ -30,7 +30,7 @@ export type TCreateSshHostDTO = {
export type TUpdateSshHostDTO = { export type TUpdateSshHostDTO = {
sshHostId: string; sshHostId: string;
hostname?: string; hostname?: string;
alias?: string | null; alias?: string;
userCertTtl?: string; userCertTtl?: string;
hostCertTtl?: string; hostCertTtl?: string;
loginMappings?: { loginMappings?: {
@@ -134,15 +134,15 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
return; return;
} }
const processedAlias = alias.trim() || null; const trimmedAlias = alias.trim();
// check if there is already a different host with the same non-null alias // check if there is already a different host with the same non-null alias
if (processedAlias) { if (trimmedAlias) {
const existingAliases = const existingAliases =
sshHosts?.filter((h) => h.id !== sshHost?.id && h.alias !== null).map((h) => h.alias) || sshHosts?.filter((h) => h.id !== sshHost?.id && h.alias !== null).map((h) => h.alias) ||
[]; [];
if (existingAliases.includes(processedAlias)) { if (existingAliases.includes(trimmedAlias)) {
createNotification({ createNotification({
text: "A host with this alias already exists.", text: "A host with this alias already exists.",
type: "error" type: "error"
@@ -155,7 +155,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
await updateMutateAsync({ await updateMutateAsync({
sshHostId: sshHost.id, sshHostId: sshHost.id,
hostname, hostname,
alias: processedAlias, alias: trimmedAlias,
userCertTtl, userCertTtl,
loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({ loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({
loginUser, loginUser,
@@ -168,7 +168,7 @@ export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => {
await createMutateAsync({ await createMutateAsync({
projectId, projectId,
hostname, hostname,
alias: processedAlias, alias: trimmedAlias,
userCertTtl, userCertTtl,
loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({ loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({
loginUser, loginUser,