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:
Mauricio Siu
2026-04-04 09:27:06 -06:00
parent 3d838aa074
commit 0cb5ee49e0
12 changed files with 106 additions and 38 deletions

View File

@@ -24,9 +24,17 @@ import {
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
const DATABASE_PASSWORD_REGEX = /^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/;
const updatePasswordSchema = z
.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"),
})
.refine((data) => data.password === data.confirmPassword, {
@@ -63,15 +71,12 @@ export const UpdateDatabasePassword = ({
setIsOpen(false);
} catch (e) {
const raw = e instanceof Error ? e.message : "Error updating password";
const noContainer = raw.match(/No running container found for \S+/);
if (noContainer) {
if (/No running container found/i.test(raw)) {
setError(
"The database container is not running. Please start the service before changing the password.",
);
} else {
setError(
"Error updating password. Please check that the container is running and try again.",
);
setError(raw);
}
} finally {
setIsPending(false);
@@ -101,7 +106,7 @@ export const UpdateDatabasePassword = ({
</DialogDescription>
</DialogHeader>
{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
database container and in Dokploy. The container must be running for
this operation to succeed.

View File

@@ -43,6 +43,8 @@ import {
apiSaveEnvironmentVariablesMariaDB,
apiSaveExternalPortMariaDB,
apiUpdateMariaDB,
DATABASE_PASSWORD_MESSAGE,
DATABASE_PASSWORD_REGEX,
environments,
mariadb as mariadbTable,
projects,
@@ -375,7 +377,12 @@ export const mariadbRouter = createTRPCRouter({
.input(
z.object({
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"),
}),
)

View File

@@ -42,6 +42,8 @@ import {
apiSaveEnvironmentVariablesMongo,
apiSaveExternalPortMongo,
apiUpdateMongo,
DATABASE_PASSWORD_MESSAGE,
DATABASE_PASSWORD_REGEX,
environments,
mongo as mongoTable,
projects,
@@ -397,7 +399,12 @@ export const mongoRouter = createTRPCRouter({
.input(
z.object({
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 }) => {

View File

@@ -42,6 +42,8 @@ import {
apiSaveEnvironmentVariablesMySql,
apiSaveExternalPortMySql,
apiUpdateMySql,
DATABASE_PASSWORD_MESSAGE,
DATABASE_PASSWORD_REGEX,
environments,
mysql as mysqlTable,
projects,
@@ -394,7 +396,12 @@ export const mysqlRouter = createTRPCRouter({
.input(
z.object({
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"),
}),
)

View File

@@ -43,6 +43,8 @@ import {
apiSaveEnvironmentVariablesPostgres,
apiSaveExternalPortPostgres,
apiUpdatePostgres,
DATABASE_PASSWORD_MESSAGE,
DATABASE_PASSWORD_REGEX,
environments,
postgres as postgresTable,
projects,
@@ -403,7 +405,12 @@ export const postgresRouter = createTRPCRouter({
.input(
z.object({
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 }) => {

View File

@@ -41,6 +41,8 @@ import {
apiSaveEnvironmentVariablesRedis,
apiSaveExternalPortRedis,
apiUpdateRedis,
DATABASE_PASSWORD_MESSAGE,
DATABASE_PASSWORD_REGEX,
environments,
projects,
redis as redisTable,
@@ -384,7 +386,12 @@ export const redisRouter = createTRPCRouter({
.input(
z.object({
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 }) => {