const { Controller } = require('libflitter') const zxcvbn = require('zxcvbn') class PasswordController extends Controller { static get services() { return [...super.services, 'auth', 'jobs', 'models'] } async get_resets(req, res, next) { return res.api(req.user.password_resets.map(x => { return { reset_on: x.reset_on, reason: x.reason, } })) } async get_app_passwords(req, res, next) { return res.api(req.user.app_passwords.map(x => { return { created: x.created, expires: x.expires, active: x.active, name: x.name ?? '(unnamed)', uuid: x.uuid, } })) } async create_app_password(req, res, next) { if ( !req.body.name ) return res.status(400) .message('Missing required field: name') .api() const { password, record } = await req.user.app_password(req.body.name) await req.user.save() return res.api({ password, name: req.body.name, uuid: record.uuid, }) } async delete_app_password(req, res, next) { if ( !req.params.uuid ) return res.status(400) .message('Missing required parameter: uuid') .api() const match = req.user.app_passwords.filter(x => x.uuid === req.params.uuid)[0] if ( !match ) return res.status(400) .message('App password not found with that UUID.') .api() req.user.app_passwords = req.user.app_passwords.filter(x => x.uuid !== req.params.uuid) await req.user.save() return res.api() } async reset_password(req, res, next) { if ( !req.body.password ) return res.status(400) .message('Missing required field: password') .api() // Verify password complexity const min_score = 3 const result = zxcvbn(req.body.password) if ( result.score < min_score ) return res.status(400) .message(`Password does not meet the minimum complexity score of ${min_score}.`) .api() // Make sure it's not a re-do for ( const old_pw of req.user.password_resets ) { if ( await old_pw.check(req.body.password) ) { return res.status(400) .message(`This password is a duplicate of one of your previous passwords.`) .api() } } // Create the password reset const reset = await req.user.reset_password(req.body.password) await req.user.save() if ( req.trap.has_trap() && req.trap.get_trap() === 'password_reset' ) await req.trap.end() // invalidate existing tokens and other logins const flitter = await this.auth.get_provider('flitter') await flitter.logout(req) await req.user.kickout() req.trust.unassume() return res.api() } async request_reset(req, res, next) { if ( !req.body.email ) return res.status(400) .message('Missing required field: email') .api() const User = this.models.get('auth:User') const user = await User.findOne({ email: req.body.email }) if ( user ) { const reset_queue = this.jobs.queue('password_resets') await reset_queue.add('PasswordReset', { user_id: user.id }) } return res.api({ success: true }) } } module.exports = exports = PasswordController