summaryrefslogblamecommitdiff
path: root/src/routers/tmwa/middlewares/account.js
blob: 9c3afce482556592a57fc5a892b9cc8fc3b7f9d8 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14


                                                     










                                                                                                                                                                            










                                                                              
                                                                                                 
















                                                                                                                      






                                                                                        
                                                                                                 




                                      
                                                                                                             







                                                                                      


























































































































                                                                                                                                                                                                         
const randomNumber = require("random-number-csprng");

const create_account = (req, res, next) => {
    if (!req.body || !Reflect.has(req.body, "username") ||
        !Reflect.has(req.body, "password") || !Reflect.has(req.body, "email") ||
        !req.body.username.match(/^[a-zA-Z0-9]{4,23}$/) ||
        !req.body.password.match(/^[a-zA-Z0-9]{4,23}$/) ||
        !req.body.email.match(/^$|^(?:[a-zA-Z0-9.$&+=_~-]{1,34}@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,35}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,34}[a-zA-Z0-9])?){0,9})$/) ||
        req.body.email.length >= 40)
    {
        res.status(400).json({
            status: "error",
            error: "malformed request"
        });
        req.app.locals.rate_limiting.add(req.ip);
        setTimeout(() => req.app.locals.rate_limiting.delete(req.ip), 300000);
        return;
    }

    req.app.locals.tmwa.db_pool.getConnection((err, db) => {
        if (err) {
            res.status(500).json({
                status: "error",
                error: "couldn't reach the database"
            });
            console.warn("TMWA.account: a connection with the database couldn't be established");
            return;
        }

        const query_params = {
            "USERNAME": req.body.username,
            "PASSWORD": req.body.password,
            "EMAIL": req.body.email || "a@a.com",
            "GENDER": "N",
        };

        db.query(`INSERT INTO ${req.app.locals.tmwa.db_tables.register} SET ?`, query_params, (err, rows, fields) => {
            if (err) {
                if (err.code === "ER_DUP_ENTRY") {
                    res.status(409).json({
                        status: "error",
                        error: "already exists"
                    });
                    req.app.locals.rate_limiting.add(req.ip);
                    setTimeout(() => req.app.locals.rate_limiting.delete(req.ip), 2000);
                } else {
                    res.status(500).json({
                        status: "error",
                        error: "couldn't add the user"
                    });
                    console.error("TMWA.account: an unexpected sql error occured: %s", err.code);
                }
            } else {
                res.status(201).json({
                    status: "success"
                });
                console.info("TMWA.account: an account was created: %s [%s]", query_params.USERNAME, req.ip);
                req.app.locals.rate_limiting.add(req.ip);
                setTimeout(() => req.app.locals.rate_limiting.delete(req.ip), 300000);
            }

            db.release(); // return this connection to the pool
        });
    });
};



const reset_password = async (req, res, next) => {
    if (req.body && Reflect.has(req.body, "email") &&
        Reflect.has(req.body, "username") &&
        req.body.username.match(/^[a-zA-Z0-9]{4,23}$/) &&
        req.body.email.match(/^(?:[a-zA-Z0-9.$&+=_~-]{1,34}@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,35}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,34}[a-zA-Z0-9])?){0,9})$/) &&
        req.body.email.length >= 3 && req.body.email.length < 40)
    {
        req.app.locals.tmwa.db_pool.getConnection(async (err, db) => {
            if (err) {
                res.status(500).json({
                    status: "error",
                    error: "couldn't reach the database"
                });
                console.warn("TMWA.account: a connection with the database couldn't be established");
                return;
            }

            // XXX: we might want to use uuid instead, and put the value in the url directly
            const rng = await randomNumber(1000000, 999999999999);
            const code = String(rng).padStart(12, "0");

            const query_params = { // SET
                "PASSWORD": code,
                "STATE": 3,
            };

            db.query(`UPDATE ${req.app.locals.tmwa.db_tables.register} SET ? WHERE USERNAME = ? AND EMAIL = ? AND STATE = 1`, [query_params, req.body.username, req.body.email], (err, rows, fields) => {
                if (err) {
                    res.status(500).json({
                        status: "error",
                        error: "couldn't send a password reset"
                    });
                    console.error("TMWA.account: an unexpected sql error occured: %s", err.code);
                } else {
                    res.status(200).json({
                        status: "success"
                    });
                    req.app.locals.rate_limiting.add(req.ip);
                    setTimeout(() => req.app.locals.rate_limiting.delete(req.ip), 5000);

                    // TODO: make the request expire and change the STATE back to 1 upon expiration
                }

                db.release(); // return this connection to the pool
            });
        });
        return;
    }

    if (!req.body || !Reflect.has(req.body, "username") ||
        !Reflect.has(req.body, "password") || !Reflect.has(req.body, "code") ||
        !req.body.username.match(/^[a-zA-Z0-9]{4,23}$/) ||
        !req.body.password.match(/^[a-zA-Z0-9]{4,23}$/) ||
        !req.body.code.match(/^[a-zA-Z0-9-_]{6,128}$/))
    {
        res.status(400).json({
            status: "error",
            error: "malformed request"
        });
        req.app.locals.rate_limiting.add(req.ip);
        setTimeout(() => req.app.locals.rate_limiting.delete(req.ip), 300000);
        return;
    }

    req.app.locals.tmwa.db_pool.getConnection((err, db) => {
        if (err) {
            res.status(500).json({
                status: "error",
                error: "couldn't reach the database"
            });
            console.warn("TMWA.account: a connection with the database couldn't be established");
            return;
        }

        const query_params = { // SET
            "PASSWORD": req.body.password,
            "STATE": 5,
        };

        db.query(`UPDATE ${req.app.locals.tmwa.db_tables.register} SET ? WHERE USERNAME = ? AND PASSWORD = ? AND STATE = 4`, [query_params, req.body.username, req.body.code], (err, rows, fields) => {
            if (err) {
                res.status(500).json({
                    status: "error",
                    error: "couldn't change the password"
                });
                console.error("TMWA.account: an unexpected sql error occured: %s", err.code);
            } else if (rows.affectedRows < 1) {
                res.status(403).json({
                    status: "error",
                    error: "invalid code"
                });
                req.app.locals.rate_limiting.add(req.ip);
                setTimeout(() => req.app.locals.rate_limiting.delete(req.ip), 2000);
                return;
            } else {
                res.status(200).json({
                    status: "success"
                });
                console.info("TMWA.account: a password was reset: %s [%s]", query_params.USERNAME, req.ip);
                req.app.locals.rate_limiting.add(req.ip);
                setTimeout(() => req.app.locals.rate_limiting.delete(req.ip), 300000);
            }

            db.release(); // return this connection to the pool
        });
    });
};



module.exports = exports = (req, res, next) => {
    switch(req.method) {
        case "POST":
            return create_account(req, res, next);
        case "PUT":
            return reset_password(req, res, next);
        default:
            next(); // fallthrough to default endpoint (404)
    }
};