You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
devbug/app/controllers/Auth.controller.js

352 lines
11 KiB

/**
* @module flitter-auth/deploy/controllers/Auth
*/
const validator = require('validator')
const bcrypt = require('bcrypt')
const uuid = require('uuid/v4')
/**
* Controller for the auth and user functionality for Flitter-auth.
* @class
*/
class Auth {
/**
* Create an authenticated session.
* @param {Express/Request} req - the incoming Express request
* @param {module:flitter-auth/deploy/models/User~User} user - the user for which a session should be created
*/
create_auth_session(req, user){
req.session.auth = {
authenticated: true,
uuid: user.uuid,
user
}
req.session.auth.user.password = ""
}
/**
* Destroy the authenticated session.
* @param {Express/Request} req - the incoming Express request
*/
destroy_auth_session(req){
req.session.auth = {}
}
/**
* Serve the registration page.
* @param {Express/Request} req - the incoming Express request
* @param {Express/Response} res - the corresponding Express response
*/
register_get(req, res){
/*
* Check for auth_form errors in the session.
* If they exist, pass them along to the view.
*/
let submission_data = { error: false, data: {} }
if ( req.session && 'auth_form' in req.session && 'errors' in req.session.auth_form ){
submission_data = req.session.auth_form
delete req.session.auth_form
}
/*
* Return the registration view with the errors.
*/
_flitter.view(res, 'auth/register', { submission_data })
}
/**
* Handle the registration and create a new user.
* @param {Express/Request} req - the incoming Express request
* @param {Express/Response} res - the corresponding Express response
*/
register_post(req, res){
let username, password
/*
* Creates a session variable with the auth_form errors.
*/
const create_error_session = (field, message) => {
let auth_form = {
error: true,
errors: {
},
data: {
'username': req.body.username
}
}
auth_form.errors[field] = message
req.session.auth_form = auth_form
}
/*
* Fail if username isn't provided.
*/
if ( !( 'username' in req.body ) ){
req.session.auth_form = {
error: true,
errors: { 'username': 'Username is required.' }
}
return res.redirect('/auth/register')
}
/*
* Fail if the password or password verification aren't provided.
*/
if ( !( 'password' in req.body ) || !( 'password_verify' in req.body ) ){
req.session.auth_form = {
error: true,
errors: { 'password': 'Password and verification are required.' }
}
return res.redirect('/auth/register')
}
/*
* Validate that the username is alphanumeric.
*/
if ( !validator.isAlphanumeric( req.body.username ) ) {
/*
* Create an auth_form error in the session and
* redirect back to the registration form.
*/
create_error_session('username', 'Username must be alpha-numeric.')
return res.redirect('/auth/register')
}
/*
* Get the User model.
*/
const User = _flitter.model('User')
/*
* Check if the username is already in the
* database. If so, redirect with an error.
*/
User.find({ username: req.body.username }, (err, users) => {
if ( users.length ){
/*
* Create an auth_form error in the session and
* redirect back to the registration form.
*/
create_error_session('username', 'Username is already taken.')
return res.redirect('/auth/register')
}
username = req.body.username
/*
* Check if the password meets the minimum length requirement.
*/
if ( !validator.isLength(req.body.password, {min:8}) ){
/*
* Create an auth_form error in the session and
* redirect back to the registration form.
*/
create_error_session('password', 'Password must be at least 8 characters.')
return res.redirect('/auth/register')
}
/*
* Check that the password matches the password_verify field.
*/
if ( !validator.equals(req.body.password, req.body.password_verify) ){
/*
* Create an auth_form error in the session and
* redirect back to the registration form.
*/
create_error_session('password', 'Passwords don\'t match.')
return res.redirect('/auth/register')
}
password = req.body.password
/*
* Create the password hash.
*/
bcrypt.hash(password, 10, (err, hash) => {
/*
* Instantiate a new user object.
*/
const unique_id = uuid()
const user = new User({
username,
password: hash,
data: JSON.stringify({}),
uuid: unique_id
})
/*
* Store the new user in the database.
*/
user.save().then(()=>{
_flitter.controller('Auth').create_auth_session(req, user)
return _flitter.view(res, 'auth/register_success')
})
})
})
}
/**
* Log the user out and destroy the authenticated session.
* @param {Express/Request} req - the incoming Express request
* @param {Express/Response} res - the corresponding Express response
*/
logout( req, res ){
if ( req.session ){
_flitter.controller('Auth').destroy_auth_session(req)
}
return _flitter.view(res, 'auth/logged_out')
}
/**
* Serve the login view.
* @param {Express/Request} req - the incoming Express request
* @param {Express/Response} res - the corresponding Express response
*/
login_get( req, res ){
/*
* Check for auth_form errors in the session.
* If they exist, pass them along to the view.
*/
let submission_data = { error: false, data: {} }
if ( req.session && 'auth_form' in req.session && 'errors' in req.session.auth_form ){
submission_data = req.session.auth_form
delete req.session.auth_form
}
/*
* Return the login view with the errors.
*/
return _flitter.view(res, 'auth/login', { submission_data })
}
/**
* Process a login request.
* If it is successful, create a user session and carry on.
* @param {Express/Request} req - the incoming Express request
* @param {Express/Response} res - the corresponding Express response
*/
login_post( req, res ){
const create_error_session = (field, message) => {
let auth_form = {
error: true,
errors: {
},
data: {
'username': req.body.username
}
}
auth_form.errors[field] = message
req.session.auth_form = auth_form
}
/*
* Fail if username isn't provided.
*/
if ( !( 'username' in req.body ) ){
req.session.auth_form = {
error: true,
errors: { 'username': 'Username is required.' }
}
return res.redirect('/auth/login')
}
/*
* Fail if the password isn't provided.
*/
if ( !( 'password' in req.body ) ){
req.session.auth_form = {
error: true,
errors: { 'password': 'Password is required.' }
}
return res.redirect('/auth/login')
}
/*
* Make sure the username is still alphanum.
* Just a sanity check to prevent code injection.
*/
if ( !validator.isAlphanumeric( req.body.username ) ){
create_error_session('username', "Invalid username.")
return res.redirect('/auth/login')
}
const User = _flitter.model('User')
User.findOne({username: req.body.username}, (err, user) => {
/*
* If no user with the username is found, return
* an error back.
*/
if ( !user ){
create_error_session('username', 'Invalid username.')
return res.redirect('/auth/login')
}
/*
* Check the provided password against the stored hash.
*/
bcrypt.compare( req.body.password, user.password, (err, match) => {
/*
* Return an error if the password fails the hash.
*/
if ( !match ){
create_error_session('password', 'Invalid password.')
return res.redirect('/auth/login')
}
else {
/*
* Create an authenticated session.
*/
_flitter.controller('Auth').create_auth_session(req, user)
/*
* If a destination was provided, redirect there.
*/
if ( req.session.destination ){
const redirect_to = req.session.destination
delete req.session.destination
return res.redirect(redirect_to)
}
/*
* Otherwise, redirect to the provided sample dash.
*/
else {
return res.redirect('/auth/dash')
}
}
})
})
}
/**
* Return the dash view.
* By default, this is pretty simple. It's designed to be replaced by your app.
* @param {Express/Request} req - the incoming Express request
* @param {Express/Response} res - the corresponding Express response
*/
dash_get(req, res){
return _flitter.view(res, 'auth/dash', { user: req.session.auth.user })
}
}
module.exports = Auth