Skip to content
Snippets Groups Projects
Unverified Commit 1b35bcbe authored by Timothy Carambat's avatar Timothy Carambat Committed by GitHub
Browse files

Strengthen field validations on user Updates (#1201)

* Strengthen field validations on user Updates

* update writables
parent df2c01b1
No related branches found
No related tags found
No related merge requests found
...@@ -2,6 +2,23 @@ const prisma = require("../utils/prisma"); ...@@ -2,6 +2,23 @@ const prisma = require("../utils/prisma");
const { EventLogs } = require("./eventLogs"); const { EventLogs } = require("./eventLogs");
const User = { const User = {
writable: [
// Used for generic updates so we can validate keys in request body
"username",
"password",
"pfpFilename",
"role",
"suspended",
],
// validations for the above writable fields.
castColumnValue: function (key, value) {
switch (key) {
case "suspended":
return Number(Boolean(value));
default:
return String(value);
}
},
create: async function ({ username, password, role = "default" }) { create: async function ({ username, password, role = "default" }) {
const passwordCheck = this.checkPasswordComplexity(password); const passwordCheck = this.checkPasswordComplexity(password);
if (!passwordCheck.checkedOK) { if (!passwordCheck.checkedOK) {
...@@ -42,13 +59,26 @@ const User = { ...@@ -42,13 +59,26 @@ const User = {
update: async function (userId, updates = {}) { update: async function (userId, updates = {}) {
try { try {
if (!userId) throw new Error("No user id provided for update");
const currentUser = await prisma.users.findUnique({ const currentUser = await prisma.users.findUnique({
where: { id: parseInt(userId) }, where: { id: parseInt(userId) },
}); });
if (!currentUser) { if (!currentUser) return { success: false, error: "User not found" };
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]) => {
if (this.writable.includes(key)) {
updates[key] = this.castColumnValue(key, value);
return;
}
delete updates[key];
});
if (Object.keys(updates).length === 0)
return { success: false, error: "No valid updates applied." };
// Handle password specific updates
if (updates.hasOwnProperty("password")) { if (updates.hasOwnProperty("password")) {
const passwordCheck = this.checkPasswordComplexity(updates.password); const passwordCheck = this.checkPasswordComplexity(updates.password);
if (!passwordCheck.checkedOK) { if (!passwordCheck.checkedOK) {
...@@ -78,6 +108,24 @@ const User = { ...@@ -78,6 +108,24 @@ const User = {
} }
}, },
// Explicit direct update of user object.
// Only use this method when directly setting a key value
// that takes no user input for the keys being modified.
_update: async function (id = null, data = {}) {
if (!id) throw new Error("No user id provided for update");
try {
const user = await prisma.users.update({
where: { id },
data,
});
return { user, message: null };
} catch (error) {
console.error(error.message);
return { user: null, message: error.message };
}
},
get: async function (clause = {}) { get: async function (clause = {}) {
try { try {
const user = await prisma.users.findFirst({ where: clause }); const user = await prisma.users.findFirst({ where: clause });
......
...@@ -296,7 +296,7 @@ class OpenRouterLLM { ...@@ -296,7 +296,7 @@ class OpenRouterLLM {
try { try {
JSON.parse(message); JSON.parse(message);
validJSON = true; validJSON = true;
} catch { } } catch {}
if (!validJSON) { if (!validJSON) {
// It can be possible that the chunk decoding is running away // It can be possible that the chunk decoding is running away
......
...@@ -22,7 +22,7 @@ async function generateRecoveryCodes(userId) { ...@@ -22,7 +22,7 @@ async function generateRecoveryCodes(userId) {
const { error } = await RecoveryCode.createMany(newRecoveryCodes); const { error } = await RecoveryCode.createMany(newRecoveryCodes);
if (!!error) throw new Error(error); if (!!error) throw new Error(error);
const { success } = await User.update(userId, { const { user: success } = await User._update(userId, {
seen_recovery_codes: true, seen_recovery_codes: true,
}); });
if (!success) throw new Error("Failed to generate user recovery codes!"); if (!success) throw new Error("Failed to generate user recovery codes!");
...@@ -80,6 +80,11 @@ async function resetPassword(token, _newPassword = "", confirmPassword = "") { ...@@ -80,6 +80,11 @@ async function resetPassword(token, _newPassword = "", confirmPassword = "") {
// JOI password rules will be enforced inside .update. // JOI password rules will be enforced inside .update.
const { error } = await User.update(resetToken.user_id, { const { error } = await User.update(resetToken.user_id, {
password: newPassword, password: newPassword,
});
// seen_recovery_codes is not publicly writable
// so we have to do direct update here
await User._update(resetToken.user_id, {
seen_recovery_codes: false, seen_recovery_codes: false,
}); });
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment