import bcrypt from 'bcrypt' import crypto from 'crypto' import jwt from 'jsonwebtoken' import { Router } from 'express' import { glauth } from '../libs/database.js' import execawait from '../libs/execawait.js' export const SECRET = process.env.TK_SECRET ?? crypto.generateKeySync("aes", { length: 128 }).export().toString() const AUTH = Router() let valid = {} /** * This is needed because GLAuth (GLAuth's bcrypt) and Node (Node's bcrypt) read hashes differently. * @param {string} hash The hash to be converted to hex. * @returns {string} hex of hash */ function hash2hex(hash) { return hash .split('') .map(e => e .charCodeAt(0) .toString(16) ) .join('') } AUTH.post('/register', async (req, res) => { const { captcha, password, username } = req.body if (!username || !password || !captcha) return(res.send(`Not entered:${username ? '' : ' username,'}${password ? '' : ' password,'}${captcha ? '' : ' captcha'}`)) if (!valid[captcha]) return(res.send("Invalid captcha!")) const captchaAge = Math.abs((valid[captcha].getTime() - new Date().getTime())/1000) if (captchaAge > 600) return(res.send("Invalid captcha!")) // Expire the captcha. delete valid[captcha] // Does the username match the requirements? if (!(/^\w{2,20}$/.test(username))) return(res.send("Username does not match the requirements")) if ((await glauth.query("SELECT * FROM users WHERE name = $1::text", [ username ])).rowCount) return(res.send("User already exists")) bcrypt.hash(password, 12).then( hash => { const hexHash = hash2hex(hash) glauth.query( "INSERT INTO users(name, mail, primarygroup, passbcrypt, qam_pass) VALUES($1::text, $2::text, 0, $3::text, $4::text)", [ username, `${username}@qwik.space`, hexHash, hash ] ).then( () => res.send("Account registered!") ).catch( err => res.json({ _: "Sorry an error occured!", err }) ) } ) }) AUTH.post('/login', async (req, res) => { const { password, username } = req.body // Was input sent? if (!username || !password ) return(res.send(`Not entered:${username ? '' : ' username,'}${password ? '' : ' password'}`)) const user = (await glauth.query("SELECT * FROM users WHERE name = $1::text", [ username ])).rows[0] if (!user) return(res.send("User doesn't exist!")) bcrypt.compare(password, user.qam_pass).then( async match => { if (!match) return res.send("Password's is incorrect!") const bearer = { name: user.name, id: user.uidnumber, group: (await glauth.query("SELECT name FROM groups WHERE gidnumber = $1::int", [ user.primarygroup ])).rows[0].name } const token = jwt.sign( bearer, SECRET, { expiresIn: '1h' } ) const fourHoursInMillis = 60 * 60 * 1000 res.cookie( 'api-token', token, { expires: new Date(Date.now() + fourHoursInMillis), httpOnly: true, signed: true } ).redirect('/manager') } ) }) AUTH.post('/changePass', async (req, res) => { const { newPass, oldPass } = req.body // Was input sent? if (!newPass|| !oldPass ) return(res.send(`Not entered:${newPass ? '' : ' new password,'}${oldPass ? '' : ' old password'}`)) const token = jwt.verify(req.signedCookies["api-token"], SECRET) if (!token) return res.send("Token error! Please sign in ag ain anusername = token.named retry...") const username = token.name const user = (await glauth.query("SELECT * FROM users WHERE name = $1::text", [ username ])).rows[0] if (!user) return(res.send("User doesn't exist!")) bcrypt.compare(oldPass, user.qam_pass).then( async match => { if (!match) return res.send("Password's is incorrect!") const newPassHash = await bcrypt.hash(newPass, 12) const newPassHexHash = hash2hex(newPassHash) glauth.query("UPDATE users SET qam_pass = $1::text, passbcrypt = $2::text WHERE name = $3::text", [ newPassHash, newPassHexHash, username ]) .then( () => res.send("Successfully changed password!") ).catch( () => res.send("Database error while changing password!") ) } ) }) AUTH.get('/captcha', async (req, res) => { const captcha = crypto.randomBytes(3).toString('hex') await execawait(`./captcha.sh ${captcha} > captcha.png`) // Make it valid for 10 minutes valid[captcha] = new Date(Date.now() + 10 * 60 * 1000) // Send the captcha image res.contentType('image/png') .sendFile('captcha.png', { root: './' }) }) export default AUTH