From 714d9c4aa7ca4fd9dc4671ea4ced5363cdb91433 Mon Sep 17 00:00:00 2001 From: kontrollanten <6680299+kontrollanten@users.noreply.github.com> Date: Tue, 1 Oct 2024 22:34:52 +0200 Subject: [PATCH 1/3] feat(API): permissive email check in reset & verification In order to not force users to be case sensitive when asking for password reset or resend email verification. When there's multiple emails where the only difference in the local is the capitalized letters, in those cases the users has to be case sensitive. closes #6570 --- .../src/users/users-command.ts | 6 +- .../src/api/check-params/users-emails.ts | 69 ++++++++++++++++++- server/core/lib/user.ts | 9 ++- .../middlewares/validators/shared/users.ts | 9 ++- .../users/shared/user-registrations.ts | 11 ++- .../users/user-email-verification.ts | 8 +-- .../middlewares/validators/users/users.ts | 4 +- server/core/models/user/user-registration.ts | 12 ++-- server/core/models/user/user.ts | 12 ++++ 9 files changed, 121 insertions(+), 19 deletions(-) diff --git a/packages/server-commands/src/users/users-command.ts b/packages/server-commands/src/users/users-command.ts index d3b11939e58..62abd113638 100644 --- a/packages/server-commands/src/users/users-command.ts +++ b/packages/server-commands/src/users/users-command.ts @@ -161,6 +161,7 @@ export class UsersCommand extends AbstractCommand { videoQuotaDaily?: number role?: UserRoleType adminFlags?: UserAdminFlagType + email?: string }) { const { username, @@ -168,7 +169,8 @@ export class UsersCommand extends AbstractCommand { password = 'password', videoQuota, videoQuotaDaily, - role = UserRole.USER + role = UserRole.USER, + email = username + '@example.com' } = options const path = '/api/v1/users' @@ -182,7 +184,7 @@ export class UsersCommand extends AbstractCommand { password, role, adminFlags, - email: username + '@example.com', + email, videoQuota, videoQuotaDaily }, diff --git a/packages/tests/src/api/check-params/users-emails.ts b/packages/tests/src/api/check-params/users-emails.ts index e382190ec85..2796dda5f27 100644 --- a/packages/tests/src/api/check-params/users-emails.ts +++ b/packages/tests/src/api/check-params/users-emails.ts @@ -28,11 +28,23 @@ describe('Test users API validators', function () { await server.config.enableSignup(true) await server.users.generate('moderator2', UserRole.MODERATOR) + await server.users.create({ username: 'user' }) + await server.users.create({ username: 'user_similar', email: 'User@example.com' }) + await server.users.generate('user2') await server.registrations.requestRegistration({ username: 'request1', registrationReason: 'tt' }) + await server.registrations.requestRegistration({ + username: 'request_1', + email: 'Request1@example.com', + registrationReason: 'tt' + }) + await server.registrations.requestRegistration({ + username: 'request2', + registrationReason: 'tt' + }) }) describe('When asking a password reset', function () { @@ -50,6 +62,39 @@ describe('Test users API validators', function () { await makePostBodyRequest({ url: server.url, path, fields }) }) + it('Should fail with wrong capitalization when multiple users with similar email exists', async function () { + const fields = { email: 'USER@example.com' } + + await makePostBodyRequest({ + url: server.url, + path, + fields, + expectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + }) + + it('Should success with correct capitalization when multiple users with similar email exists', async function () { + const fields = { email: 'User@example.com' } + + await makePostBodyRequest({ + url: server.url, + path, + fields, + expectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + }) + + it('Should success with wrong capitalization when no similar emails exists', async function () { + const fields = { email: 'USER2@example.com' } + + await makePostBodyRequest({ + url: server.url, + path, + fields, + expectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + }) + it('Should success with the correct params', async function () { const fields = { email: 'admin@example.com' } @@ -104,7 +149,29 @@ describe('Test users API validators', function () { await makePostBodyRequest({ url: server.url, path, fields }) }) - it('Should succeed with the correct params', async function () { + it('Should fail with wrong capitalization when multiple users with similar email exists', async function () { + const fields = { email: 'REQUEST1@example.com' } + + await makePostBodyRequest({ + url: server.url, + path, + fields, + expectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + }) + + it('Should success with wrong capitalization when no similar emails exists', async function () { + const fields = { email: 'REQUEST2@example.com' } + + await makePostBodyRequest({ + url: server.url, + path, + fields, + expectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + }) + + it('Should success with correct capitalization when multiple users with similar email exists', async function () { const fields = { email: 'request1@example.com' } await makePostBodyRequest({ diff --git a/server/core/lib/user.ts b/server/core/lib/user.ts index 39ec281a6ec..1076f6c5792 100644 --- a/server/core/lib/user.ts +++ b/server/core/lib/user.ts @@ -237,6 +237,12 @@ async function isUserQuotaValid (options: { return true } +function getUserByEmailPermissive (users: T[], email: string): T { + if (users.length === 1) return users[0] + + return users.find(r => r.email === email) +} + // --------------------------------------------------------------------------- export { @@ -250,7 +256,8 @@ export { sendVerifyRegistrationEmail, isUserQuotaValid, - buildUser + buildUser, + getUserByEmailPermissive } // --------------------------------------------------------------------------- diff --git a/server/core/middlewares/validators/shared/users.ts b/server/core/middlewares/validators/shared/users.ts index c2a18ffc688..d8f9dd9457a 100644 --- a/server/core/middlewares/validators/shared/users.ts +++ b/server/core/middlewares/validators/shared/users.ts @@ -1,5 +1,6 @@ import { forceNumber } from '@peertube/peertube-core-utils' import { HttpStatusCode, UserRightType } from '@peertube/peertube-models' +import { getUserByEmailPermissive } from '@server/lib/user.js' import { ActorModel } from '@server/models/actor/actor.js' import { UserModel } from '@server/models/user/user.js' import { MAccountId, MUserAccountId, MUserDefault } from '@server/types/models/index.js' @@ -10,8 +11,12 @@ export function checkUserIdExist (idArg: number | string, res: express.Response, return checkUserExist(() => UserModel.loadByIdWithChannels(id, withStats), res) } -export function checkUserEmailExist (email: string, res: express.Response, abortResponse = true) { - return checkUserExist(() => UserModel.loadByEmail(email), res, abortResponse) +export function checkUserEmailExistPermissive (email: string, res: express.Response, abortResponse = true) { + return checkUserExist(async () => { + const users = await UserModel.loadByEmailCaseInsensitive(email) + + return getUserByEmailPermissive(users, email) + }, res, abortResponse) } export async function checkUserNameOrEmailDoNotAlreadyExist (username: string, email: string, res: express.Response) { diff --git a/server/core/middlewares/validators/users/shared/user-registrations.ts b/server/core/middlewares/validators/users/shared/user-registrations.ts index ede9d6b9163..c4d40296882 100644 --- a/server/core/middlewares/validators/users/shared/user-registrations.ts +++ b/server/core/middlewares/validators/users/shared/user-registrations.ts @@ -3,14 +3,19 @@ import { UserRegistrationModel } from '@server/models/user/user-registration.js' import { MRegistration } from '@server/types/models/index.js' import { forceNumber, pick } from '@peertube/peertube-core-utils' import { HttpStatusCode } from '@peertube/peertube-models' +import { getUserByEmailPermissive } from '@server/lib/user.js' function checkRegistrationIdExist (idArg: number | string, res: express.Response) { const id = forceNumber(idArg) return checkRegistrationExist(() => UserRegistrationModel.load(id), res) } -function checkRegistrationEmailExist (email: string, res: express.Response, abortResponse = true) { - return checkRegistrationExist(() => UserRegistrationModel.loadByEmail(email), res, abortResponse) +function checkRegistrationEmailExistPermissive (email: string, res: express.Response, abortResponse = true) { + return checkRegistrationExist(async () => { + const registrations = await UserRegistrationModel.loadByEmailCaseInsensitive(email) + + return getUserByEmailPermissive(registrations, email) + }, res, abortResponse) } async function checkRegistrationHandlesDoNotAlreadyExist (options: { @@ -54,7 +59,7 @@ async function checkRegistrationExist (finder: () => Promise, res export { checkRegistrationIdExist, - checkRegistrationEmailExist, + checkRegistrationEmailExistPermissive, checkRegistrationHandlesDoNotAlreadyExist, checkRegistrationExist } diff --git a/server/core/middlewares/validators/users/user-email-verification.ts b/server/core/middlewares/validators/users/user-email-verification.ts index 38f5e65867a..0e7509cfe29 100644 --- a/server/core/middlewares/validators/users/user-email-verification.ts +++ b/server/core/middlewares/validators/users/user-email-verification.ts @@ -4,8 +4,8 @@ import { toBooleanOrNull } from '@server/helpers/custom-validators/misc.js' import { HttpStatusCode } from '@peertube/peertube-models' import { logger } from '../../../helpers/logger.js' import { Redis } from '../../../lib/redis.js' -import { areValidationErrors, checkUserEmailExist, checkUserIdExist } from '../shared/index.js' -import { checkRegistrationEmailExist, checkRegistrationIdExist } from './shared/user-registrations.js' +import { areValidationErrors, checkUserEmailExistPermissive, checkUserIdExist } from '../shared/index.js' +import { checkRegistrationEmailExistPermissive, checkRegistrationIdExist } from './shared/user-registrations.js' const usersAskSendVerifyEmailValidator = [ body('email').isEmail().not().isEmpty().withMessage('Should have a valid email'), @@ -14,8 +14,8 @@ const usersAskSendVerifyEmailValidator = [ if (areValidationErrors(req, res)) return const [ userExists, registrationExists ] = await Promise.all([ - checkUserEmailExist(req.body.email, res, false), - checkRegistrationEmailExist(req.body.email, res, false) + checkUserEmailExistPermissive(req.body.email, res, false), + checkRegistrationEmailExistPermissive(req.body.email, res, false) ]) if (!userExists && !registrationExists) { diff --git a/server/core/middlewares/validators/users/users.ts b/server/core/middlewares/validators/users/users.ts index 030b5043c4a..9f3b127c244 100644 --- a/server/core/middlewares/validators/users/users.ts +++ b/server/core/middlewares/validators/users/users.ts @@ -32,7 +32,7 @@ import { ActorModel } from '../../../models/actor/actor.js' import { areValidationErrors, checkUserCanManageAccount, - checkUserEmailExist, + checkUserEmailExistPermissive, checkUserIdExist, checkUserNameOrEmailDoNotAlreadyExist, doesVideoChannelIdExist, @@ -334,7 +334,7 @@ export const usersAskResetPasswordValidator = [ async (req: express.Request, res: express.Response, next: express.NextFunction) => { if (areValidationErrors(req, res)) return - const exists = await checkUserEmailExist(req.body.email, res, false) + const exists = await checkUserEmailExistPermissive(req.body.email, res, false) if (!exists) { logger.debug('User with email %s does not exist (asking reset password).', req.body.email) // Do not leak our emails diff --git a/server/core/models/user/user-registration.ts b/server/core/models/user/user-registration.ts index 64ff04c35c7..43af3fbbde4 100644 --- a/server/core/models/user/user-registration.ts +++ b/server/core/models/user/user-registration.ts @@ -8,7 +8,7 @@ import { isVideoChannelDisplayNameValid } from '@server/helpers/custom-validator import { cryptPassword } from '@server/helpers/peertube-crypto.js' import { USER_REGISTRATION_STATES } from '@server/initializers/constants.js' import { MRegistration, MRegistrationFormattable } from '@server/types/models/index.js' -import { FindOptions, Op, QueryTypes, WhereOptions } from 'sequelize' +import { col, FindOptions, fn, Op, QueryTypes, where, WhereOptions } from 'sequelize' import { AllowNull, BeforeCreate, @@ -129,12 +129,16 @@ export class UserRegistrationModel extends SequelizeModel return UserRegistrationModel.findByPk(id) } - static loadByEmail (email: string): Promise { + static loadByEmailCaseInsensitive (email: string): Promise { const query = { - where: { email } + where: where( + fn('LOWER', col('email')), + '=', + email.toLowerCase() + ) } - return UserRegistrationModel.findOne(query) + return UserRegistrationModel.findAll(query) } static loadByEmailOrUsername (emailOrUsername: string): Promise { diff --git a/server/core/models/user/user.ts b/server/core/models/user/user.ts index dffaff4edc3..eaf7687a4a8 100644 --- a/server/core/models/user/user.ts +++ b/server/core/models/user/user.ts @@ -673,6 +673,18 @@ export class UserModel extends SequelizeModel { return UserModel.findOne(query) } + static loadByEmailCaseInsensitive (email: string): Promise { + const query = { + where: where( + fn('LOWER', col('email')), + '=', + email.toLowerCase() + ) + } + + return UserModel.findAll(query) + } + static loadByUsernameOrEmail (username: string, email?: string): Promise { if (!email) email = username From 5e44b719403a72861b1eaf78e1ec0c6a9549050b Mon Sep 17 00:00:00 2001 From: kontrollanten <6680299+kontrollanten@users.noreply.github.com> Date: Tue, 1 Oct 2024 23:20:01 +0200 Subject: [PATCH 2/3] feat(API/login): permissive email handling Allow case insensitive email when there's no other candidate. closes #6570 --- packages/tests/src/api/users/oauth.ts | 15 ++++++++++++++- server/core/lib/auth/oauth-model.ts | 13 ++++++++++--- server/core/models/user/user.ts | 14 ++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/packages/tests/src/api/users/oauth.ts b/packages/tests/src/api/users/oauth.ts index fe50872cb1a..154a0dc561b 100644 --- a/packages/tests/src/api/users/oauth.ts +++ b/packages/tests/src/api/users/oauth.ts @@ -28,6 +28,8 @@ describe('Test oauth', function () { }) await setAccessTokensToServers([ server ]) + await server.users.create({ username: 'user1', email: 'user@example.com' }) + await server.users.create({ username: 'user2', email: 'User@example.com', password: 'AdvancedPassword' }) sqlCommand = new SQLCommand(server) }) @@ -79,7 +81,7 @@ describe('Test oauth', function () { }) it('Should not login with an invalid password', async function () { - const user = { username: server.store.user.username, password: 'mew_three' } + const user = { username: 'User@example.com', password: 'password' } const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) expectInvalidCredentials(body) @@ -87,6 +89,9 @@ describe('Test oauth', function () { it('Should be able to login', async function () { await server.login.login({ expectedStatus: HttpStatusCode.OK_200 }) + + const user = { username: 'User@example.com', password: 'AdvancedPassword' } + await server.login.login({ user, expectedStatus: HttpStatusCode.OK_200 }) }) it('Should be able to login with an insensitive username', async function () { @@ -99,6 +104,14 @@ describe('Test oauth', function () { const user3 = { username: 'ROOt', password: server.store.user.password } await server.login.login({ user: user3, expectedStatus: HttpStatusCode.OK_200 }) }) + + it('Should be able to login with an insensitive email when no similar emails exist', async function () { + const user = { username: 'ADMIN' + server.internalServerNumber + '@example.com', password: server.store.user.password } + await server.login.login({ user, expectedStatus: HttpStatusCode.OK_200 }) + + const user2 = { username: 'admin' + server.internalServerNumber + '@example.com', password: server.store.user.password } + await server.login.login({ user: user2, expectedStatus: HttpStatusCode.OK_200 }) + }) }) describe('Logout', function () { diff --git a/server/core/lib/auth/oauth-model.ts b/server/core/lib/auth/oauth-model.ts index e801b678b0e..ce1ba6fb46f 100644 --- a/server/core/lib/auth/oauth-model.ts +++ b/server/core/lib/auth/oauth-model.ts @@ -14,7 +14,7 @@ import { OAuthClientModel } from '../../models/oauth/oauth-client.js' import { OAuthTokenModel } from '../../models/oauth/oauth-token.js' import { UserModel } from '../../models/user/user.js' import { findAvailableLocalActorName } from '../local-actor.js' -import { buildUser, createUserAccountAndChannelAndPlaylist } from '../user.js' +import { buildUser, createUserAccountAndChannelAndPlaylist, getUserByEmailPermissive } from '../user.js' import { ExternalUser } from './external-auth.js' import { TokensCache } from './tokens-cache.js' @@ -87,7 +87,7 @@ async function getUser (usernameOrEmail?: string, password?: string, bypassLogin if (bypassLogin && bypassLogin.bypass === true) { logger.info('Bypassing oauth login by plugin %s.', bypassLogin.pluginName) - let user = await UserModel.loadByEmail(bypassLogin.user.email) + let user = getUserByEmailPermissive(await UserModel.loadByEmailCaseInsensitive(bypassLogin.user.email), bypassLogin.user.email) if (!user) { user = await createUserFromExternal(bypassLogin.pluginName, bypassLogin.user) @@ -119,7 +119,14 @@ async function getUser (usernameOrEmail?: string, password?: string, bypassLogin logger.debug('Getting User (username/email: ' + usernameOrEmail + ', password: ******).') - const user = await UserModel.loadByUsernameOrEmail(usernameOrEmail) + const users = await UserModel.loadByUsernameOrEmailCaseInsensitive(usernameOrEmail) + let user: MUserDefault + + if (usernameOrEmail.includes('@')) { + user = getUserByEmailPermissive(users, usernameOrEmail) + } else { + user = users[0] + } // If we don't find the user, or if the user belongs to a plugin if (!user || user.pluginAuth !== null || !password) return null diff --git a/server/core/models/user/user.ts b/server/core/models/user/user.ts index eaf7687a4a8..4d23f800e6f 100644 --- a/server/core/models/user/user.ts +++ b/server/core/models/user/user.ts @@ -701,6 +701,20 @@ export class UserModel extends SequelizeModel { return UserModel.findOne(query) } + static loadByUsernameOrEmailCaseInsensitive (usernameOrEmail: string): Promise { + const query = { + where: { + [Op.or]: [ + where(fn('lower', col('username')), fn('lower', usernameOrEmail) as any), + + where(fn('lower', col('email')), fn('lower', usernameOrEmail) as any) + ] + } + } + + return UserModel.findAll(query) + } + static loadByVideoId (videoId: number): Promise { const query = { include: [ From bd9f3de5af728adf7f933822e40aa38a9407f280 Mon Sep 17 00:00:00 2001 From: kontrollanten <6680299+kontrollanten@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:25:59 +0100 Subject: [PATCH 3/3] code review changes --- .../src/api/check-params/users-emails.ts | 22 ---------------- packages/tests/src/api/server/email.ts | 25 ++++++++++++++++++ packages/tests/src/api/users/oauth.ts | 10 ++++++- server/core/lib/auth/oauth-model.ts | 2 +- .../middlewares/validators/shared/users.ts | 5 ++-- server/core/models/user/user.ts | 26 ------------------- 6 files changed, 38 insertions(+), 52 deletions(-) diff --git a/packages/tests/src/api/check-params/users-emails.ts b/packages/tests/src/api/check-params/users-emails.ts index 2796dda5f27..9cc305658b8 100644 --- a/packages/tests/src/api/check-params/users-emails.ts +++ b/packages/tests/src/api/check-params/users-emails.ts @@ -62,17 +62,6 @@ describe('Test users API validators', function () { await makePostBodyRequest({ url: server.url, path, fields }) }) - it('Should fail with wrong capitalization when multiple users with similar email exists', async function () { - const fields = { email: 'USER@example.com' } - - await makePostBodyRequest({ - url: server.url, - path, - fields, - expectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - }) - it('Should success with correct capitalization when multiple users with similar email exists', async function () { const fields = { email: 'User@example.com' } @@ -149,17 +138,6 @@ describe('Test users API validators', function () { await makePostBodyRequest({ url: server.url, path, fields }) }) - it('Should fail with wrong capitalization when multiple users with similar email exists', async function () { - const fields = { email: 'REQUEST1@example.com' } - - await makePostBodyRequest({ - url: server.url, - path, - fields, - expectedStatus: HttpStatusCode.NO_CONTENT_204 - }) - }) - it('Should success with wrong capitalization when no similar emails exists', async function () { const fields = { email: 'REQUEST2@example.com' } diff --git a/packages/tests/src/api/server/email.ts b/packages/tests/src/api/server/email.ts index 6d3f3f3bb4f..74c68dfb9ba 100644 --- a/packages/tests/src/api/server/email.ts +++ b/packages/tests/src/api/server/email.ts @@ -31,6 +31,16 @@ describe('Test emails', function () { username: 'user_1', password: 'super_password' } + const similarUsers = [ + { + username: 'lowercase_user_1', + email: 'lowercase_user_1@example.com' + }, + { + username: 'lowercase_user__1', + email: 'Lowercase_user_1@example.com' + } + ] before(async function () { this.timeout(120000) @@ -41,6 +51,10 @@ describe('Test emails', function () { await setAccessTokensToServers([ server ]) await server.config.enableSignup(true) + for (const user of similarUsers) { + await server.users.create(user) + } + { const created = await server.users.create({ username: user.username, password: user.password }) userId = created.id @@ -101,6 +115,10 @@ describe('Test emails', function () { }) }) + it('Should fail with wrong capitalization when multiple users with similar email exists', async function () { + await server.users.askResetPassword({ email: similarUsers[0].username.toUpperCase(), expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) + }) + it('Should reset the password', async function () { await server.users.resetPassword({ userId, verificationString, password: 'super_password2' }) }) @@ -269,6 +287,13 @@ describe('Test emails', function () { describe('When verifying a user email', function () { + it('Should fail with wrong capitalization when multiple users with similar email exists', async function () { + await server.users.askSendVerifyEmail({ + email: similarUsers[0].username.toUpperCase(), + expectedStatus: HttpStatusCode.BAD_REQUEST_400 + }) + }) + it('Should ask to send the verification email', async function () { await server.users.askSendVerifyEmail({ email: 'user_1@example.com' }) diff --git a/packages/tests/src/api/users/oauth.ts b/packages/tests/src/api/users/oauth.ts index a69f5100117..2dd79dbf757 100644 --- a/packages/tests/src/api/users/oauth.ts +++ b/packages/tests/src/api/users/oauth.ts @@ -81,7 +81,7 @@ describe('Test oauth', function () { }) it('Should not login with an invalid password', async function () { - const user = { username: 'User@example.com', password: 'password' } + const user = { username: server.store.user.username, password: 'mew_three' } const body = await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) expectInvalidCredentials(body) @@ -112,6 +112,14 @@ describe('Test oauth', function () { const user2 = { username: 'admin' + server.internalServerNumber + '@example.com', password: server.store.user.password } await server.login.login({ user: user2, expectedStatus: HttpStatusCode.OK_200 }) }) + + it('Should not be able to login with an insensitive email when similar emails exist', async function () { + const user = { username: 'uSer@example.com', password: 'AdvancedPassword' } + await server.login.login({ user, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) + + const user2 = { username: 'User@example.com', password: 'AdvancedPassword' } + await server.login.login({ user: user2, expectedStatus: HttpStatusCode.OK_200 }) + }) }) describe('Logout', function () { diff --git a/server/core/lib/auth/oauth-model.ts b/server/core/lib/auth/oauth-model.ts index ce1ba6fb46f..a1236764c3c 100644 --- a/server/core/lib/auth/oauth-model.ts +++ b/server/core/lib/auth/oauth-model.ts @@ -124,7 +124,7 @@ async function getUser (usernameOrEmail?: string, password?: string, bypassLogin if (usernameOrEmail.includes('@')) { user = getUserByEmailPermissive(users, usernameOrEmail) - } else { + } else if (users.length === 1) { user = users[0] } diff --git a/server/core/middlewares/validators/shared/users.ts b/server/core/middlewares/validators/shared/users.ts index d8f9dd9457a..760b7c701b8 100644 --- a/server/core/middlewares/validators/shared/users.ts +++ b/server/core/middlewares/validators/shared/users.ts @@ -20,9 +20,10 @@ export function checkUserEmailExistPermissive (email: string, res: express.Respo } export async function checkUserNameOrEmailDoNotAlreadyExist (username: string, email: string, res: express.Response) { - const user = await UserModel.loadByUsernameOrEmail(username, email) + const existingUser = await UserModel.loadByUsernameOrEmailCaseInsensitive(username) + const existingEmail = await UserModel.loadByUsernameOrEmailCaseInsensitive(email) - if (user) { + if (existingUser.length > 0 || existingEmail.length > 0) { res.fail({ status: HttpStatusCode.CONFLICT_409, message: 'User with this username or email already exists.' diff --git a/server/core/models/user/user.ts b/server/core/models/user/user.ts index 4d23f800e6f..40e66866205 100644 --- a/server/core/models/user/user.ts +++ b/server/core/models/user/user.ts @@ -663,16 +663,6 @@ export class UserModel extends SequelizeModel { return UserModel.scope(ScopeNames.FOR_ME_API).findOne(query) } - static loadByEmail (email: string): Promise { - const query = { - where: { - email - } - } - - return UserModel.findOne(query) - } - static loadByEmailCaseInsensitive (email: string): Promise { const query = { where: where( @@ -685,22 +675,6 @@ export class UserModel extends SequelizeModel { return UserModel.findAll(query) } - static loadByUsernameOrEmail (username: string, email?: string): Promise { - if (!email) email = username - - const query = { - where: { - [Op.or]: [ - where(fn('lower', col('username')), fn('lower', username) as any), - - { email } - ] - } - } - - return UserModel.findOne(query) - } - static loadByUsernameOrEmailCaseInsensitive (usernameOrEmail: string): Promise { const query = { where: {