safe.b68dev.xyz/controllers/authController.js

234 lines
7.1 KiB
JavaScript

const { promisify } = require('util')
const bcrypt = require('bcrypt')
const randomstring = require('randomstring')
const perms = require('./permissionController')
const tokens = require('./tokenController')
const utils = require('./utilsController')
const config = require('./../config')
const logger = require('./../logger')
const db = require('knex')(config.database)
const self = {
compare: promisify(bcrypt.compare),
hash: promisify(bcrypt.hash)
}
self.verify = async (req, res, next) => {
const username = typeof req.body.username === 'string'
? req.body.username.trim()
: ''
if (!username)
return res.json({ success: false, description: 'No username provided.' })
const password = typeof req.body.password === 'string'
? req.body.password.trim()
: ''
if (!password)
return res.json({ success: false, description: 'No password provided.' })
try {
const user = await db.table('users')
.where('username', username)
.first()
if (!user)
return res.json({ success: false, description: 'Username does not exist.' })
if (user.enabled === false || user.enabled === 0)
return res.json({ success: false, description: 'This account has been disabled.' })
const result = await self.compare(password, user.password)
if (result === false)
return res.json({ success: false, description: 'Wrong password.' })
else
return res.json({ success: true, token: user.token })
} catch (error) {
logger.error(error)
return res.status(500).json({ success: false, description: 'An unexpected error occurred. Try again?' })
}
}
self.register = async (req, res, next) => {
if (config.enableUserAccounts === false)
return res.json({ success: false, description: 'Registration is currently disabled.' })
const username = typeof req.body.username === 'string'
? req.body.username.trim()
: ''
if (username.length < 4 || username.length > 32)
return res.json({ success: false, description: 'Username must have 4-32 characters.' })
const password = typeof req.body.password === 'string'
? req.body.password.trim()
: ''
if (password.length < 6 || password.length > 64)
return res.json({ success: false, description: 'Password must have 6-64 characters.' })
try {
const user = await db.table('users')
.where('username', username)
.first()
if (user)
return res.json({ success: false, description: 'Username already exists.' })
const hash = await self.hash(password, 10)
const token = await tokens.generateUniqueToken()
if (!token)
return res.json({ success: false, description: 'Sorry, we could not allocate a unique token. Try again?' })
await db.table('users')
.insert({
username,
password: hash,
token,
enabled: 1,
permission: perms.permissions.user
})
utils.invalidateStatsCache('users')
tokens.onHold.delete(token)
return res.json({ success: true, token })
} catch (error) {
logger.error(error)
return res.status(500).json({ success: false, description: 'An unexpected error occurred. Try again?' })
}
}
self.changePassword = async (req, res, next) => {
const user = await utils.authorize(req, res)
if (!user) return
const password = typeof req.body.password === 'string'
? req.body.password.trim()
: ''
if (password.length < 6 || password.length > 64)
return res.json({ success: false, description: 'Password must have 6-64 characters.' })
try {
const hash = await self.hash(password, 10)
await db.table('users')
.where('id', user.id)
.update('password', hash)
return res.json({ success: true })
} catch (error) {
logger.error(error)
return res.status(500).json({ success: false, description: 'An unexpected error occurred. Try again?' })
}
}
self.editUser = async (req, res, next) => {
const user = await utils.authorize(req, res)
if (!user) return
const id = parseInt(req.body.id)
if (isNaN(id))
return res.json({ success: false, description: 'No user specified.' })
try {
const target = await db.table('users')
.where('id', id)
.first()
if (!target)
return res.json({ success: false, description: 'Could not get user with the specified ID.' })
else if (!perms.higher(user, target))
return res.json({ success: false, description: 'The user is in the same or higher group as you.' })
else if (target.username === 'root')
return res.json({ success: false, description: 'Root user may not be edited.' })
const update = {}
if (req.body.username !== undefined) {
update.username = String(req.body.username).trim()
if (update.username.length < 4 || update.username.length > 32)
return res.json({ success: false, description: 'Username must have 4-32 characters.' })
}
if (req.body.enabled !== undefined)
update.enabled = Boolean(req.body.enabled)
if (req.body.group !== undefined) {
update.permission = perms.permissions[req.body.group]
if (typeof update.permission !== 'number' || update.permission < 0)
update.permission = target.permission
}
let password
if (req.body.resetPassword) {
password = randomstring.generate(16)
update.password = await self.hash(password, 10)
}
await db.table('users')
.where('id', id)
.update(update)
utils.invalidateStatsCache('users')
const response = { success: true, update }
if (password) response.password = password
return res.json(response)
} catch (error) {
logger.error(error)
return res.status(500).json({ success: false, description: 'An unexpected error occurred. Try again?' })
}
}
self.disableUser = async (req, res, next) => {
req.body = { id: req.body.id, enabled: false }
return self.editUser(req, res, next)
}
self.listUsers = async (req, res, next) => {
const user = await utils.authorize(req, res)
if (!user) return
const isadmin = perms.is(user, 'admin')
if (!isadmin)
return res.status(403).end()
try {
const count = await db.table('users')
.count('id as count')
.then(rows => rows[0].count)
if (!count)
return res.json({ success: true, users: [], count })
let offset = req.params.page
if (offset === undefined) offset = 0
const users = await db.table('users')
.limit(25)
.offset(25 * offset)
.select('id', 'username', 'enabled', 'permission')
const pointers = {}
for (const user of users) {
user.groups = perms.mapPermissions(user)
delete user.permission
user.uploads = 0
user.usage = 0
pointers[user.id] = user
}
const uploads = await db.table('files')
.whereIn('userid', Object.keys(pointers))
.select('userid', 'size')
for (const upload of uploads) {
pointers[upload.userid].uploads++
pointers[upload.userid].usage += parseInt(upload.size)
}
return res.json({ success: true, users, count })
} catch (error) {
logger.error(error)
return res.status(500).json({ success: false, description: 'An unexpected error occurred. Try again?' })
}
}
module.exports = self