mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-04-17 18:18:11 +00:00
New user account validations (#2037)
* force lowercase and no space for new and edit user modals * edit account modal validations * use pattern for form validation + remove validations from edit user * revert comment deletions * comment fix * update validation message * update regex allow updating by block name changes to invalid names --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
parent
d072875e43
commit
c97066526a
4 changed files with 56 additions and 9 deletions
|
@ -7,6 +7,7 @@ import { Plus, X } from "@phosphor-icons/react";
|
|||
|
||||
export default function AccountModal({ user, hideModal }) {
|
||||
const { pfp, setPfp } = usePfp();
|
||||
|
||||
const handleFileUpload = async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return false;
|
||||
|
@ -133,6 +134,10 @@ export default function AccountModal({ user, hideModal }) {
|
|||
required
|
||||
autoComplete="off"
|
||||
/>
|
||||
<p className="mt-2 text-xs text-white/60">
|
||||
Username must be only contain lowercase letters, numbers,
|
||||
underscores, and hyphens with no spaces
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
|
@ -143,10 +148,14 @@ export default function AccountModal({ user, hideModal }) {
|
|||
</label>
|
||||
<input
|
||||
name="password"
|
||||
type="password"
|
||||
type="text"
|
||||
className="bg-zinc-900 placeholder:text-white/20 border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
||||
placeholder={`${user.username}'s new password`}
|
||||
minLength={8}
|
||||
/>
|
||||
<p className="mt-2 text-xs text-white/60">
|
||||
Password must be at least 8 characters long
|
||||
</p>
|
||||
</div>
|
||||
<LanguagePreference />
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { RoleHintDisplay } from "..";
|
|||
export default function NewUserModal({ closeModal }) {
|
||||
const [error, setError] = useState(null);
|
||||
const [role, setRole] = useState("default");
|
||||
|
||||
const handleCreate = async (e) => {
|
||||
setError(null);
|
||||
e.preventDefault();
|
||||
|
@ -54,7 +55,18 @@ export default function NewUserModal({ closeModal }) {
|
|||
minLength={2}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
pattern="^[a-z0-9_-]+$"
|
||||
onInvalid={(e) =>
|
||||
e.target.setCustomValidity(
|
||||
"Username must be only contain lowercase letters, numbers, underscores, and hyphens with no spaces"
|
||||
)
|
||||
}
|
||||
onChange={(e) => e.target.setCustomValidity("")}
|
||||
/>
|
||||
<p className="mt-2 text-xs text-white/60">
|
||||
Username must be only contain lowercase letters, numbers,
|
||||
underscores, and hyphens with no spaces
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
|
@ -70,7 +82,11 @@ export default function NewUserModal({ closeModal }) {
|
|||
placeholder="User's initial password"
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
minLength={8}
|
||||
/>
|
||||
<p className="mt-2 text-xs text-white/60">
|
||||
Password must be at least 8 characters long
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
|
@ -84,10 +100,10 @@ export default function NewUserModal({ closeModal }) {
|
|||
required={true}
|
||||
defaultValue={"default"}
|
||||
onChange={(e) => setRole(e.target.value)}
|
||||
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border-gray-500 focus:ring-blue-500 focus:border-blue-500"
|
||||
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border-gray-500 focus:ring-blue-500 focus:border-blue-500 w-full"
|
||||
>
|
||||
<option value="default">Default</option>
|
||||
<option value="manager">Manager </option>
|
||||
<option value="manager">Manager</option>
|
||||
{user?.role === "admin" && (
|
||||
<option value="admin">Administrator</option>
|
||||
)}
|
||||
|
|
|
@ -52,11 +52,15 @@ export default function EditUserModal({ currentUser, user, closeModal }) {
|
|||
type="text"
|
||||
className="bg-zinc-900 placeholder:text-white/20 border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
||||
placeholder="User's username"
|
||||
minLength={2}
|
||||
defaultValue={user.username}
|
||||
minLength={2}
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<p className="mt-2 text-xs text-white/60">
|
||||
Username must be only contain lowercase letters, numbers,
|
||||
underscores, and hyphens with no spaces
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
|
@ -71,7 +75,11 @@ export default function EditUserModal({ currentUser, user, closeModal }) {
|
|||
className="bg-zinc-900 placeholder:text-white/20 border-gray-500 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
|
||||
placeholder={`${user.username}'s new password`}
|
||||
autoComplete="off"
|
||||
minLength={8}
|
||||
/>
|
||||
<p className="mt-2 text-xs text-white/60">
|
||||
Password must be at least 8 characters long
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
|
@ -85,7 +93,7 @@ export default function EditUserModal({ currentUser, user, closeModal }) {
|
|||
required={true}
|
||||
defaultValue={user.role}
|
||||
onChange={(e) => setRole(e.target.value)}
|
||||
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border-gray-500 focus:ring-blue-500 focus:border-blue-500"
|
||||
className="rounded-lg bg-zinc-900 px-4 py-2 text-sm text-white border-gray-500 focus:ring-blue-500 focus:border-blue-500 w-full"
|
||||
>
|
||||
<option value="default">Default</option>
|
||||
<option value="manager">Manager</option>
|
||||
|
|
|
@ -2,6 +2,7 @@ const prisma = require("../utils/prisma");
|
|||
const { EventLogs } = require("./eventLogs");
|
||||
|
||||
const User = {
|
||||
usernameRegex: new RegExp(/^[a-z0-9_-]+$/),
|
||||
writable: [
|
||||
// Used for generic updates so we can validate keys in request body
|
||||
"username",
|
||||
|
@ -32,7 +33,6 @@ const User = {
|
|||
return String(role);
|
||||
},
|
||||
},
|
||||
|
||||
// validations for the above writable fields.
|
||||
castColumnValue: function (key, value) {
|
||||
switch (key) {
|
||||
|
@ -55,6 +55,12 @@ const User = {
|
|||
}
|
||||
|
||||
try {
|
||||
// Do not allow new users to bypass validation
|
||||
if (!this.usernameRegex.test(username))
|
||||
throw new Error(
|
||||
"Username must be only contain lowercase letters, numbers, underscores, and hyphens with no spaces"
|
||||
);
|
||||
|
||||
const bcrypt = require("bcrypt");
|
||||
const hashedPassword = bcrypt.hashSync(password, 10);
|
||||
const user = await prisma.users.create({
|
||||
|
@ -70,7 +76,6 @@ const User = {
|
|||
return { user: null, error: error.message };
|
||||
}
|
||||
},
|
||||
|
||||
// Log the changes to a user object, but omit sensitive fields
|
||||
// that are not meant to be logged.
|
||||
loggedChanges: function (updates, prev = {}) {
|
||||
|
@ -93,7 +98,6 @@ const User = {
|
|||
where: { id: parseInt(userId) },
|
||||
});
|
||||
if (!currentUser) return { success: false, error: "User not found" };
|
||||
|
||||
// Removes non-writable fields for generic updates
|
||||
// and force-casts to the proper type;
|
||||
Object.entries(updates).forEach(([key, value]) => {
|
||||
|
@ -123,6 +127,17 @@ const User = {
|
|||
updates.password = bcrypt.hashSync(updates.password, 10);
|
||||
}
|
||||
|
||||
if (
|
||||
updates.hasOwnProperty("username") &&
|
||||
currentUser.username !== updates.username &&
|
||||
!this.usernameRegex.test(updates.username)
|
||||
)
|
||||
return {
|
||||
success: false,
|
||||
error:
|
||||
"Username must be only contain lowercase letters, numbers, underscores, and hyphens with no spaces",
|
||||
};
|
||||
|
||||
const user = await prisma.users.update({
|
||||
where: { id: parseInt(userId) },
|
||||
data: updates,
|
||||
|
@ -170,7 +185,6 @@ const User = {
|
|||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
// Returns user object with all fields
|
||||
_get: async function (clause = {}) {
|
||||
try {
|
||||
|
|
Loading…
Add table
Reference in a new issue