diff options
author | Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> | 2021-10-05 19:33:23 +0200 |
---|---|---|
committer | Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> | 2021-10-05 19:33:23 +0200 |
commit | 8b56eb5fbc3a2034598cdaae5b1e08198b19e8ac (patch) | |
tree | 776c82db14ab7d2df90972b8ddf066f5e5422ba4 /api | |
parent | :arrow_up: update mnjsf (diff) | |
download | server-8b56eb5fbc3a2034598cdaae5b1e08198b19e8ac.tar.xz |
:sparkles: add User.register() method
Diffstat (limited to 'api')
-rw-r--r-- | api/src/routes/auth/register.ts | 187 |
1 files changed, 60 insertions, 127 deletions
diff --git a/api/src/routes/auth/register.ts b/api/src/routes/auth/register.ts index 1344c994..c016c949 100644 --- a/api/src/routes/auth/register.ts +++ b/api/src/routes/auth/register.ts @@ -1,8 +1,8 @@ import { Request, Response, Router } from "express"; -import { trimSpecial, User, Snowflake, Config, defaultSettings, generateToken, Invite, adjustEmail } from "@fosscord/util"; -import bcrypt from "bcrypt"; -import { FieldErrors, route, getIpAdress, IPAnalysis, isProxy } from "@fosscord/api"; +import { Config, generateToken, Invite, FieldErrors, User, adjustEmail, trimSpecial } from "@fosscord/util"; +import { route, getIpAdress, IPAnalysis, isProxy } from "@fosscord/api"; import "missing-native-js-functions"; +import bcrypt from "bcrypt"; import { HTTPError } from "lambert-server"; const router: Router = Router(); @@ -34,22 +34,27 @@ export interface RegisterSchema { } router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Response) => { - let { - email, - username, - password, - consent, - fingerprint, - invite, - date_of_birth, - gift_code_sku_id, // ? what is this - captcha_key - } = req.body; - - // get register Config + const body = req.body as RegisterSchema; const { register, security } = Config.get(); const ip = getIpAdress(req); + // email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick + let email = adjustEmail(body.email); + + // check if registration is allowed + if (!register.allowNewRegistration) { + throw FieldErrors({ + email: { code: "REGISTRATION_DISABLED", message: req.t("auth:register.REGISTRATION_DISABLED") } + }); + } + + // check if the user agreed to the Terms of Service + if (!body.consent) { + throw FieldErrors({ + consent: { code: "CONSENT_REQUIRED", message: req.t("auth:register.CONSENT_REQUIRED") } + }); + } + if (register.disabled) { throw FieldErrors({ email: { @@ -59,6 +64,33 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re }); } + if (register.requireCaptcha && security.captcha.enabled) { + if (!body.captcha_key) { + const { sitekey, service } = security.captcha; + return res?.status(400).json({ + captcha_key: ["captcha-required"], + captcha_sitekey: sitekey, + captcha_service: service + }); + } + + // TODO: check captcha + } + + if (!register.allowMultipleAccounts) { + // TODO: check if fingerprint was eligible generated + const exists = await User.findOne({ where: { fingerprints: body.fingerprint } }); + + if (exists) { + throw FieldErrors({ + email: { + code: "EMAIL_ALREADY_REGISTERED", + message: req.t("auth:register.EMAIL_ALREADY_REGISTERED") + } + }); + } + } + if (register.blockProxies) { if (isProxy(await IPAnalysis(ip))) { console.log(`proxy ${ip} blocked from registration`); @@ -66,36 +98,15 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re } } - console.log("register", req.body.email, req.body.username, ip); + console.log("register", body.email, body.username, ip); // TODO: gift_code_sku_id? // TODO: check password strength - // email will be slightly modified version of the user supplied email -> e.g. protection against GMail Trick - email = adjustEmail(email); - - // trim special uf8 control characters -> Backspace, Newline, ... - username = trimSpecial(username); - - // discriminator will be randomly generated - let discriminator = ""; - - // check if registration is allowed - if (!register.allowNewRegistration) { - throw FieldErrors({ - email: { code: "REGISTRATION_DISABLED", message: req.t("auth:register.REGISTRATION_DISABLED") } - }); - } - - // check if the user agreed to the Terms of Service - if (!consent) { - throw FieldErrors({ - consent: { code: "CONSENT_REQUIRED", message: req.t("auth:register.CONSENT_REQUIRED") } - }); - } - if (email) { // replace all dots and chars after +, if its a gmail.com email - if (!email) throw FieldErrors({ email: { code: "INVALID_EMAIL", message: req.t("auth:register.INVALID_EMAIL") } }); + if (!email) { + throw FieldErrors({ email: { code: "INVALID_EMAIL", message: req?.t("auth:register.INVALID_EMAIL") } }); + } // check if there is already an account with this email const exists = await User.findOneOrFail({ email: email }).catch((e) => {}); @@ -114,17 +125,17 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re }); } - if (register.dateOfBirth.required && !date_of_birth) { + if (register.dateOfBirth.required && !body.date_of_birth) { throw FieldErrors({ date_of_birth: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") } }); } else if (register.dateOfBirth.minimum) { const minimum = new Date(); minimum.setFullYear(minimum.getFullYear() - register.dateOfBirth.minimum); - date_of_birth = new Date(date_of_birth); + body.date_of_birth = new Date(body.date_of_birth as Date); // higher is younger - if (date_of_birth > minimum) { + if (body.date_of_birth > minimum) { throw FieldErrors({ date_of_birth: { code: "DATE_OF_BIRTH_UNDERAGE", @@ -134,98 +145,20 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re } } - if (!register.allowMultipleAccounts) { - // TODO: check if fingerprint was eligible generated - const exists = await User.findOne({ where: { fingerprints: fingerprint } }); - - if (exists) { - throw FieldErrors({ - email: { - code: "EMAIL_ALREADY_REGISTERED", - message: req.t("auth:register.EMAIL_ALREADY_REGISTERED") - } - }); - } - } - - if (register.requireCaptcha && security.captcha.enabled) { - if (!captcha_key) { - const { sitekey, service } = security.captcha; - return res.status(400).json({ - captcha_key: ["captcha-required"], - captcha_sitekey: sitekey, - captcha_service: service - }); - } - - // TODO: check captcha - } - - if (password) { + if (body.password) { // the salt is saved in the password refer to bcrypt docs - password = await bcrypt.hash(password, 12); + body.password = await bcrypt.hash(body.password, 12); } else if (register.password.required) { throw FieldErrors({ password: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") } }); } - let exists; - // randomly generates a discriminator between 1 and 9999 and checks max five times if it already exists - // if it all five times already exists, abort with USERNAME_TOO_MANY_USERS error - // else just continue - // TODO: is there any better way to generate a random discriminator only once, without checking if it already exists in the mongodb database? - for (let tries = 0; tries < 5; tries++) { - discriminator = Math.randomIntBetween(1, 9999).toString().padStart(4, "0"); - exists = await User.findOne({ where: { discriminator, username: username }, select: ["id"] }); - if (!exists) break; - } - - if (exists) { - throw FieldErrors({ - username: { - code: "USERNAME_TOO_MANY_USERS", - message: req.t("auth:register.USERNAME_TOO_MANY_USERS") - } - }); - } + const user = await User.register({ ...body, req }); - // TODO: save date_of_birth - // appearently discord doesn't save the date of birth and just calculate if nsfw is allowed - // if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false - - const user = await new User({ - created_at: new Date(), - username: username, - discriminator, - id: Snowflake.generate(), - bot: false, - system: false, - desktop: false, - mobile: false, - premium: true, - premium_type: 2, - bio: "", - mfa_enabled: false, - verified: true, - disabled: false, - deleted: false, - email: email, - rights: "0", - nsfw_allowed: true, // TODO: depending on age - public_flags: "0", - flags: "0", // TODO: generate - data: { - hash: password, - valid_tokens_since: new Date() - }, - settings: { ...defaultSettings, locale: req.language || "en-US" }, - fingerprints: [] - }).save(); - - if (invite) { + if (body.invite) { // await to fail if the invite doesn't exist (necessary for requireInvite to work properly) (username only signups are possible) - await Invite.joinGuild(user.id, invite); + await Invite.joinGuild(user.id, body.invite); } else if (register.requireInvite) { // require invite to register -> e.g. for organizations to send invites to their employees throw FieldErrors({ |