const { Controller } = require('libflitter') const zxcvbn = require('zxcvbn') class PasswordController extends Controller { static get services() { return [...super.services, 'auth', 'jobs', 'models', 'activity'] } 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 ?? req.T('common.unnamed'), uuid: x.uuid, } })) } async create_app_password(req, res, next) { if ( !req.body.name ) return res.status(400) .message(`${req.T('api.missing_field')} name`) .api() const { password, record } = await req.user.app_password(req.body.name) await req.user.save() await this.activity.app_password_created({ req, name: req.body.name }) 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(`${req.T('api.missing_field')} uuid`) .api() const match = req.user.app_passwords.filter(x => x.uuid === req.params.uuid)[0] if ( !match ) return res.status(400) .message(req.T('api.app_pw_not_found')) .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(`${req.T('api.missing_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(req.T('auth.password_complexity_fail').replace('MIN_SCORE', 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(req.T('auth.duplicate_pw')) .api() } } // Create the password reset const reset = await req.user.reset_password(req.body.password) await req.user.save() await this.activity.password_reset({ req, ip: req.ip }) if ( req.trap.has_trap() && req.trap.get_trap() === 'password_reset' ) await req.trap.end() if ( req.session.registrant_flow ) { await req.trap.begin('registrant_flow', { session_only: true }) } // invalidate existing tokens and other logins await req.user.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(`${req.T('api.missing_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