mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-13 15:35:17 +00:00
feat(password-validation): enhance password validation across database routers
- Updated password validation in MariaDB, MongoDB, MySQL, Postgres, and Redis routers to enforce a regex pattern that restricts invalid characters. - Introduced a consistent error message for invalid passwords to improve user guidance and ensure database compatibility. - Refactored password validation logic in the schema files to utilize shared constants for regex and messages, promoting code reuse and maintainability.
This commit is contained in:
@@ -24,9 +24,17 @@ import {
|
|||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
||||||
|
const DATABASE_PASSWORD_REGEX = /^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/;
|
||||||
|
|
||||||
const updatePasswordSchema = z
|
const updatePasswordSchema = z
|
||||||
.object({
|
.object({
|
||||||
password: z.string().min(1, "Password is required"),
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(1, "Password is required")
|
||||||
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
|
message:
|
||||||
|
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters",
|
||||||
|
}),
|
||||||
confirmPassword: z.string().min(1, "Please confirm the password"),
|
confirmPassword: z.string().min(1, "Please confirm the password"),
|
||||||
})
|
})
|
||||||
.refine((data) => data.password === data.confirmPassword, {
|
.refine((data) => data.password === data.confirmPassword, {
|
||||||
@@ -63,15 +71,12 @@ export const UpdateDatabasePassword = ({
|
|||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const raw = e instanceof Error ? e.message : "Error updating password";
|
const raw = e instanceof Error ? e.message : "Error updating password";
|
||||||
const noContainer = raw.match(/No running container found for \S+/);
|
if (/No running container found/i.test(raw)) {
|
||||||
if (noContainer) {
|
|
||||||
setError(
|
setError(
|
||||||
"The database container is not running. Please start the service before changing the password.",
|
"The database container is not running. Please start the service before changing the password.",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setError(
|
setError(raw);
|
||||||
"Error updating password. Please check that the container is running and try again.",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsPending(false);
|
setIsPending(false);
|
||||||
@@ -101,7 +106,7 @@ export const UpdateDatabasePassword = ({
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
{error && <AlertBlock type="error">{error}</AlertBlock>}
|
{error && <AlertBlock type="error">{error}</AlertBlock>}
|
||||||
<AlertBlock type="warning">
|
<AlertBlock type="warning" className="my-4">
|
||||||
This will change the {label.toLowerCase()} both in the running
|
This will change the {label.toLowerCase()} both in the running
|
||||||
database container and in Dokploy. The container must be running for
|
database container and in Dokploy. The container must be running for
|
||||||
this operation to succeed.
|
this operation to succeed.
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ import {
|
|||||||
apiSaveEnvironmentVariablesMariaDB,
|
apiSaveEnvironmentVariablesMariaDB,
|
||||||
apiSaveExternalPortMariaDB,
|
apiSaveExternalPortMariaDB,
|
||||||
apiUpdateMariaDB,
|
apiUpdateMariaDB,
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
environments,
|
environments,
|
||||||
mariadb as mariadbTable,
|
mariadb as mariadbTable,
|
||||||
projects,
|
projects,
|
||||||
@@ -375,7 +377,12 @@ export const mariadbRouter = createTRPCRouter({
|
|||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
mariadbId: z.string().min(1),
|
mariadbId: z.string().min(1),
|
||||||
password: z.string().min(1),
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
|
}),
|
||||||
type: z.enum(["user", "root"]).default("user"),
|
type: z.enum(["user", "root"]).default("user"),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ import {
|
|||||||
apiSaveEnvironmentVariablesMongo,
|
apiSaveEnvironmentVariablesMongo,
|
||||||
apiSaveExternalPortMongo,
|
apiSaveExternalPortMongo,
|
||||||
apiUpdateMongo,
|
apiUpdateMongo,
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
environments,
|
environments,
|
||||||
mongo as mongoTable,
|
mongo as mongoTable,
|
||||||
projects,
|
projects,
|
||||||
@@ -397,7 +399,12 @@ export const mongoRouter = createTRPCRouter({
|
|||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
mongoId: z.string().min(1),
|
mongoId: z.string().min(1),
|
||||||
password: z.string().min(1),
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ import {
|
|||||||
apiSaveEnvironmentVariablesMySql,
|
apiSaveEnvironmentVariablesMySql,
|
||||||
apiSaveExternalPortMySql,
|
apiSaveExternalPortMySql,
|
||||||
apiUpdateMySql,
|
apiUpdateMySql,
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
environments,
|
environments,
|
||||||
mysql as mysqlTable,
|
mysql as mysqlTable,
|
||||||
projects,
|
projects,
|
||||||
@@ -394,7 +396,12 @@ export const mysqlRouter = createTRPCRouter({
|
|||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
mysqlId: z.string().min(1),
|
mysqlId: z.string().min(1),
|
||||||
password: z.string().min(1),
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
|
}),
|
||||||
type: z.enum(["user", "root"]).default("user"),
|
type: z.enum(["user", "root"]).default("user"),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ import {
|
|||||||
apiSaveEnvironmentVariablesPostgres,
|
apiSaveEnvironmentVariablesPostgres,
|
||||||
apiSaveExternalPortPostgres,
|
apiSaveExternalPortPostgres,
|
||||||
apiUpdatePostgres,
|
apiUpdatePostgres,
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
environments,
|
environments,
|
||||||
postgres as postgresTable,
|
postgres as postgresTable,
|
||||||
projects,
|
projects,
|
||||||
@@ -403,7 +405,12 @@ export const postgresRouter = createTRPCRouter({
|
|||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
postgresId: z.string().min(1),
|
postgresId: z.string().min(1),
|
||||||
password: z.string().min(1),
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ import {
|
|||||||
apiSaveEnvironmentVariablesRedis,
|
apiSaveEnvironmentVariablesRedis,
|
||||||
apiSaveExternalPortRedis,
|
apiSaveExternalPortRedis,
|
||||||
apiUpdateRedis,
|
apiUpdateRedis,
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
environments,
|
environments,
|
||||||
projects,
|
projects,
|
||||||
redis as redisTable,
|
redis as redisTable,
|
||||||
@@ -384,7 +386,12 @@ export const redisRouter = createTRPCRouter({
|
|||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
redisId: z.string().min(1),
|
redisId: z.string().min(1),
|
||||||
password: z.string().min(1),
|
password: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
|||||||
@@ -34,7 +34,11 @@ import {
|
|||||||
type UpdateConfigSwarm,
|
type UpdateConfigSwarm,
|
||||||
UpdateConfigSwarmSchema,
|
UpdateConfigSwarmSchema,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { generateAppName } from "./utils";
|
import {
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
|
generateAppName,
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
export const libsql = pgTable("libsql", {
|
export const libsql = pgTable("libsql", {
|
||||||
libsqlId: text("libsqlId")
|
libsqlId: text("libsqlId")
|
||||||
@@ -111,9 +115,8 @@ const createSchema = createInsertSchema(libsql, {
|
|||||||
databaseUser: z.string().min(1),
|
databaseUser: z.string().min(1),
|
||||||
databasePassword: z
|
databasePassword: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
message:
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
|
||||||
}),
|
}),
|
||||||
sqldNode: z.enum(sqldNode.enumValues),
|
sqldNode: z.enum(sqldNode.enumValues),
|
||||||
sqldPrimaryUrl: z.string().nullable(),
|
sqldPrimaryUrl: z.string().nullable(),
|
||||||
|
|||||||
@@ -28,7 +28,13 @@ import {
|
|||||||
type UpdateConfigSwarm,
|
type UpdateConfigSwarm,
|
||||||
UpdateConfigSwarmSchema,
|
UpdateConfigSwarmSchema,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils";
|
import {
|
||||||
|
APP_NAME_MESSAGE,
|
||||||
|
APP_NAME_REGEX,
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
|
generateAppName,
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
export const mariadb = pgTable("mariadb", {
|
export const mariadb = pgTable("mariadb", {
|
||||||
mariadbId: text("mariadbId")
|
mariadbId: text("mariadbId")
|
||||||
@@ -110,15 +116,13 @@ const createSchema = createInsertSchema(mariadb, {
|
|||||||
databaseUser: z.string().min(1),
|
databaseUser: z.string().min(1),
|
||||||
databasePassword: z
|
databasePassword: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
message:
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
|
||||||
}),
|
}),
|
||||||
databaseRootPassword: z
|
databaseRootPassword: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
message:
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
dockerImage: z.string().default("mariadb:6"),
|
dockerImage: z.string().default("mariadb:6"),
|
||||||
|
|||||||
@@ -35,7 +35,13 @@ import {
|
|||||||
type UpdateConfigSwarm,
|
type UpdateConfigSwarm,
|
||||||
UpdateConfigSwarmSchema,
|
UpdateConfigSwarmSchema,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils";
|
import {
|
||||||
|
APP_NAME_MESSAGE,
|
||||||
|
APP_NAME_REGEX,
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
|
generateAppName,
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
export const mongo = pgTable("mongo", {
|
export const mongo = pgTable("mongo", {
|
||||||
mongoId: text("mongoId")
|
mongoId: text("mongoId")
|
||||||
@@ -112,9 +118,8 @@ const createSchema = createInsertSchema(mongo, {
|
|||||||
name: z.string().min(1),
|
name: z.string().min(1),
|
||||||
databasePassword: z
|
databasePassword: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
message:
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
|
||||||
}),
|
}),
|
||||||
databaseUser: z.string().min(1),
|
databaseUser: z.string().min(1),
|
||||||
dockerImage: z.string().default("mongo:15"),
|
dockerImage: z.string().default("mongo:15"),
|
||||||
|
|||||||
@@ -28,7 +28,13 @@ import {
|
|||||||
type UpdateConfigSwarm,
|
type UpdateConfigSwarm,
|
||||||
UpdateConfigSwarmSchema,
|
UpdateConfigSwarmSchema,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils";
|
import {
|
||||||
|
APP_NAME_MESSAGE,
|
||||||
|
APP_NAME_REGEX,
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
|
generateAppName,
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
export const mysql = pgTable("mysql", {
|
export const mysql = pgTable("mysql", {
|
||||||
mysqlId: text("mysqlId")
|
mysqlId: text("mysqlId")
|
||||||
@@ -108,15 +114,13 @@ const createSchema = createInsertSchema(mysql, {
|
|||||||
databaseUser: z.string().min(1),
|
databaseUser: z.string().min(1),
|
||||||
databasePassword: z
|
databasePassword: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
message:
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
|
||||||
}),
|
}),
|
||||||
databaseRootPassword: z
|
databaseRootPassword: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
message:
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
dockerImage: z.string().default("mysql:8"),
|
dockerImage: z.string().default("mysql:8"),
|
||||||
|
|||||||
@@ -28,7 +28,13 @@ import {
|
|||||||
type UpdateConfigSwarm,
|
type UpdateConfigSwarm,
|
||||||
UpdateConfigSwarmSchema,
|
UpdateConfigSwarmSchema,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { APP_NAME_MESSAGE, APP_NAME_REGEX, generateAppName } from "./utils";
|
import {
|
||||||
|
APP_NAME_MESSAGE,
|
||||||
|
APP_NAME_REGEX,
|
||||||
|
DATABASE_PASSWORD_MESSAGE,
|
||||||
|
DATABASE_PASSWORD_REGEX,
|
||||||
|
generateAppName,
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
export const postgres = pgTable("postgres", {
|
export const postgres = pgTable("postgres", {
|
||||||
postgresId: text("postgresId")
|
postgresId: text("postgresId")
|
||||||
@@ -105,9 +111,8 @@ const createSchema = createInsertSchema(postgres, {
|
|||||||
.optional(),
|
.optional(),
|
||||||
databasePassword: z
|
databasePassword: z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
.regex(DATABASE_PASSWORD_REGEX, {
|
||||||
message:
|
message: DATABASE_PASSWORD_MESSAGE,
|
||||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
|
||||||
}),
|
}),
|
||||||
databaseName: z.string().min(1),
|
databaseName: z.string().min(1),
|
||||||
databaseUser: z.string().min(1),
|
databaseUser: z.string().min(1),
|
||||||
|
|||||||
@@ -12,6 +12,13 @@ export const APP_NAME_REGEX = /^[a-zA-Z0-9._-]+$/;
|
|||||||
export const APP_NAME_MESSAGE =
|
export const APP_NAME_MESSAGE =
|
||||||
"App name can only contain letters, numbers, dots, underscores and hyphens";
|
"App name can only contain letters, numbers, dots, underscores and hyphens";
|
||||||
|
|
||||||
|
/** Database password: blocks shell-dangerous characters like $ ! ' " \ / and spaces. */
|
||||||
|
export const DATABASE_PASSWORD_REGEX =
|
||||||
|
/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/;
|
||||||
|
|
||||||
|
export const DATABASE_PASSWORD_MESSAGE =
|
||||||
|
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility";
|
||||||
|
|
||||||
export const generateAppName = (type: string) => {
|
export const generateAppName = (type: string) => {
|
||||||
const verb = faker.hacker.verb().replace(/ /g, "-");
|
const verb = faker.hacker.verb().replace(/ /g, "-");
|
||||||
const adjective = faker.hacker.adjective().replace(/ /g, "-");
|
const adjective = faker.hacker.adjective().replace(/ /g, "-");
|
||||||
|
|||||||
Reference in New Issue
Block a user