mirror of
https://github.com/tale/headplane.git
synced 2026-08-26 04:16:49 +00:00
feat(users): validate usernames before creating or renaming (#610)
This commit is contained in:
@@ -3,6 +3,7 @@ import Dialog, { DialogPanel } from "~/components/dialog";
|
||||
import Input from "~/components/input";
|
||||
import Text from "~/components/text";
|
||||
import Title from "~/components/title";
|
||||
import { USERNAME_PATTERN, USERNAME_RULE } from "~/utils/user";
|
||||
|
||||
interface CreateUserProps {
|
||||
isOidc?: boolean;
|
||||
@@ -23,7 +24,17 @@ export default function CreateUser({ isOidc, isDisabled }: CreateUserProps) {
|
||||
</Text>
|
||||
<input name="action_id" type="hidden" value="create_user" />
|
||||
<div className="flex flex-col gap-4">
|
||||
<Input required label="Username" name="username" placeholder="my-new-user" type="text" />
|
||||
<Input
|
||||
description={USERNAME_RULE}
|
||||
minLength={2}
|
||||
pattern={USERNAME_PATTERN}
|
||||
required
|
||||
title={USERNAME_RULE}
|
||||
label="Username"
|
||||
name="username"
|
||||
placeholder="my-new-user"
|
||||
type="text"
|
||||
/>
|
||||
<Input label="Display Name" name="display_name" placeholder="John Doe" type="text" />
|
||||
<Input label="Email" name="email" placeholder="name@example.com" type="email" />
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import Input from "~/components/input";
|
||||
import Text from "~/components/text";
|
||||
import Title from "~/components/title";
|
||||
import { User } from "~/types";
|
||||
import { USERNAME_PATTERN, USERNAME_RULE } from "~/utils/user";
|
||||
|
||||
interface RenameProps {
|
||||
user: User;
|
||||
@@ -10,7 +11,6 @@ interface RenameProps {
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
// TODO: Server side validation before submitting
|
||||
export default function RenameUser({ user, isOpen, setIsOpen }: RenameProps) {
|
||||
return (
|
||||
<Dialog isOpen={isOpen} onOpenChange={setIsOpen}>
|
||||
@@ -24,7 +24,11 @@ export default function RenameUser({ user, isOpen, setIsOpen }: RenameProps) {
|
||||
<input name="headscale_user_id" type="hidden" value={user.id} />
|
||||
<Input
|
||||
defaultValue={user.name}
|
||||
description={USERNAME_RULE}
|
||||
minLength={2}
|
||||
pattern={USERNAME_PATTERN}
|
||||
required
|
||||
title={USERNAME_RULE}
|
||||
label="Username"
|
||||
name="new_name"
|
||||
placeholder="my-new-name"
|
||||
|
||||
@@ -5,6 +5,7 @@ import { usersResource } from "~/server/headscale/live-store";
|
||||
import { isUserPrincipal } from "~/server/web/auth";
|
||||
import { Capabilities } from "~/server/web/roles";
|
||||
import type { Role } from "~/server/web/roles";
|
||||
import { validateUsername } from "~/utils/user";
|
||||
|
||||
import type { Route } from "./+types/overview";
|
||||
|
||||
@@ -42,6 +43,11 @@ export async function userAction({ request, context }: Route.ActionArgs) {
|
||||
});
|
||||
}
|
||||
|
||||
const nameError = validateUsername(name);
|
||||
if (nameError) {
|
||||
throw data(nameError, { status: 400 });
|
||||
}
|
||||
|
||||
await api.users.create({ name, email, displayName });
|
||||
await headscaleLiveStore.refresh(usersResource, api);
|
||||
return { message: "User created successfully" };
|
||||
@@ -65,6 +71,11 @@ export async function userAction({ request, context }: Route.ActionArgs) {
|
||||
return data({ success: false }, 400);
|
||||
}
|
||||
|
||||
const newNameError = validateUsername(newName);
|
||||
if (newNameError) {
|
||||
throw data(newNameError, { status: 400 });
|
||||
}
|
||||
|
||||
const users = await api.users.list({ id: headscaleUserId });
|
||||
const user = users.find((user) => user.id === headscaleUserId);
|
||||
if (!user) {
|
||||
|
||||
Reference in New Issue
Block a user