feat: add administrators, require administrator perms to change server config in webui #628
@ -6,7 +6,7 @@ import buildConfig from "@/static/data/buildConfig.json";
|
|||||||
|
|
||||||
import { toLoginRequest } from "@/src/helpers/loginHelpers";
|
import { toLoginRequest } from "@/src/helpers/loginHelpers";
|
||||||
import { Account } from "@/src/models/loginModel";
|
import { Account } from "@/src/models/loginModel";
|
||||||
import { createAccount, isCorrectPassword } from "@/src/services/loginService";
|
import { createAccount, isCorrectPassword, isNameTaken } from "@/src/services/loginService";
|
||||||
import { IDatabaseAccountJson, ILoginResponse } from "@/src/types/loginTypes";
|
import { IDatabaseAccountJson, ILoginResponse } from "@/src/types/loginTypes";
|
||||||
import { DTLS, groups, HUB, platformCDNs } from "@/static/fixed_responses/login_static";
|
import { DTLS, groups, HUB, platformCDNs } from "@/static/fixed_responses/login_static";
|
||||||
import { logger } from "@/src/utils/logger";
|
import { logger } from "@/src/utils/logger";
|
||||||
@ -26,10 +26,19 @@ export const loginController: RequestHandler = async (request, response) => {
|
|||||||
|
|
||||||
if (!account && config.autoCreateAccount && loginRequest.ClientType != "webui") {
|
if (!account && config.autoCreateAccount && loginRequest.ClientType != "webui") {
|
||||||
try {
|
try {
|
||||||
|
const nameFromEmail = loginRequest.email.substring(0, loginRequest.email.indexOf("@"));
|
||||||
|
let name = nameFromEmail;
|
||||||
|
if (await isNameTaken(name)) {
|
||||||
|
let suffix = 0;
|
||||||
|
do {
|
||||||
|
++suffix;
|
||||||
|
name = nameFromEmail + suffix;
|
||||||
|
} while (await isNameTaken(name));
|
||||||
|
}
|
||||||
const newAccount = await createAccount({
|
const newAccount = await createAccount({
|
||||||
email: loginRequest.email,
|
email: loginRequest.email,
|
||||||
password: loginRequest.password,
|
password: loginRequest.password,
|
||||||
DisplayName: loginRequest.email.substring(0, loginRequest.email.indexOf("@")),
|
DisplayName: name,
|
||||||
CountryCode: loginRequest.lang.toUpperCase(),
|
CountryCode: loginRequest.lang.toUpperCase(),
|
||||||
ClientType: loginRequest.ClientType,
|
ClientType: loginRequest.ClientType,
|
||||||
CrossPlatformAllowed: true,
|
CrossPlatformAllowed: true,
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import { toCreateAccount, toDatabaseAccount } from "@/src/helpers/customHelpers/customHelpers";
|
import { toCreateAccount, toDatabaseAccount } from "@/src/helpers/customHelpers/customHelpers";
|
||||||
import { createAccount } from "@/src/services/loginService";
|
import { createAccount, isNameTaken } from "@/src/services/loginService";
|
||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
|
|
||||||
const createAccountController: RequestHandler = async (req, res) => {
|
const createAccountController: RequestHandler = async (req, res) => {
|
||||||
const createAccountData = toCreateAccount(req.body);
|
const createAccountData = toCreateAccount(req.body);
|
||||||
const databaseAccount = toDatabaseAccount(createAccountData);
|
if (await isNameTaken(createAccountData.DisplayName)) {
|
||||||
|
res.status(409).json("Name already in use");
|
||||||
const account = await createAccount(databaseAccount);
|
} else {
|
||||||
|
const databaseAccount = toDatabaseAccount(createAccountData);
|
||||||
res.json(account);
|
const account = await createAccount(databaseAccount);
|
||||||
|
res.json(account);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { createAccountController };
|
export { createAccountController };
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import { RequestHandler } from "express";
|
import { RequestHandler } from "express";
|
||||||
import { getAccountForRequest } from "@/src/services/loginService";
|
import { getAccountForRequest, isNameTaken } from "@/src/services/loginService";
|
||||||
|
|
||||||
export const renameAccountController: RequestHandler = async (req, res) => {
|
export const renameAccountController: RequestHandler = async (req, res) => {
|
||||||
const account = await getAccountForRequest(req);
|
const account = await getAccountForRequest(req);
|
||||||
if (typeof req.query.newname == "string") {
|
if (typeof req.query.newname == "string") {
|
||||||
account.DisplayName = req.query.newname;
|
if (await isNameTaken(req.query.newname)) {
|
||||||
await account.save();
|
res.status(409).json("Name already in use");
|
||||||
res.end();
|
} else {
|
||||||
|
account.DisplayName = req.query.newname;
|
||||||
|
await account.save();
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
res.status(400).end();
|
res.status(400).end();
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@ export const isCorrectPassword = (requestPassword: string, databasePassword: str
|
|||||||
return requestPassword === databasePassword;
|
return requestPassword === databasePassword;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isNameTaken = async (name: string): Promise<boolean> => {
|
||||||
|
return !!(await Account.findOne({ DisplayName: name }));
|
||||||
|
};
|
||||||
|
|
||||||
export const createAccount = async (accountData: IDatabaseAccount): Promise<IDatabaseAccountJson> => {
|
export const createAccount = async (accountData: IDatabaseAccount): Promise<IDatabaseAccountJson> => {
|
||||||
|
|||||||
const account = new Account(accountData);
|
const account = new Account(accountData);
|
||||||
try {
|
try {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user
💡 Codebase verification
Based on the gathered information, I can now provide the final response:
Add unique constraint for DisplayName to prevent race conditions
The
DisplayName
field in the Account schema lacks a unique constraint, while the current implementation relies only on theisNameTaken
check. This can lead to race conditions where two concurrent requests might pass the check but conflict during save. Addunique: true
to the DisplayName field in the schema to enforce uniqueness at the database level.src/models/loginModel.ts
, update the schema definition:🔗 Analysis chain
Ensure concurrency safety.
Though the database query for checking if a name is taken (with “findOne”) is correct, be aware of potential race conditions when two requests attempt to create the same name concurrently. You may want to handle such cases or at least confirm the system’s acceptable collision behavior.
Let me gather more information about the codebase to understand how the
isNameTaken
function is used and if there are any existing concurrency controls.Let me search specifically for the Account schema definition to check if there are any database-level constraints.
🏁 Scripts executed
The following scripts were executed for the analysis:
Error: No shell scripts found
Script:
Length of output: 3589
Script:
Length of output: 3387