120 lines
3.8 KiB
JavaScript
120 lines
3.8 KiB
JavaScript
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()
|
|
|
|
// 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(`${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
|