This commit is contained in:
2019-06-21 17:01:34 -05:00
commit 487f0c4eeb
56 changed files with 5037 additions and 0 deletions

View File

@@ -0,0 +1,352 @@
/**
* @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

View File

@@ -0,0 +1,24 @@
/*
* Home Controller
* -------------------------------------------------------------
* Controller for the main homepage of this Flitter app. Methods here
* are used as handlers for routes specified in the route files.
*/
class Home {
/*
* Serve the main welcome page.
*/
welcome(req, res){
/*
* Return the welcome view.
* It must be passed the response.
* View parameters can be passed as an optional third
* argument to the view() method.
*/
return _flitter.view(res, 'welcome')
}
}
module.exports = Home

View File

@@ -0,0 +1,58 @@
/*
* v1 Controller
* -------------------------------------------------------------
* Put some description here!
*/
const Out = _flitter.model('v1:Out')
const Project = _flitter.model('v1:Project')
class v1 {
/*
* Serve the main page.
*/
main(req, res){
/*
* Return the main view.
* It must be passed the response.
* View parameters can be passed as an optional third
* argument to the view() method.
*/
return view(res, 'view_name')
}
async new_out( req, res, next ){
console.log(req.body)
const project = await Project.findOne({ uuid: req.params.key })
if ( !project ){
return res.status(404).send({
success: false,
error: 'Project not found with specified key.'
})
}
if ( !req.body.data ){
return res.status(400).send({
success: false,
error: 'Missing data.'
})
}
const data = JSON.parse(req.body.data)
const out = new Out({
brief: data.brief,
data: JSON.stringify(data.data),
project_id: project.id
})
await out.save()
return res.send({
success: true
})
}
}
module.exports = exports = v1

View File

@@ -0,0 +1,73 @@
/*
* v1 Controller
* -------------------------------------------------------------
* Put some description here!
*/
const Project = _flitter.model('v1:Project')
const Out = _flitter.model('v1:Out')
class v1 {
/*
* Serve the main page.
*/
async main(req, res){
const projects = await Project.find({ archived: false, user_id: req.session.auth.uuid })
/*
* Return the main view.
* It must be passed the response.
* View parameters can be passed as an optional third
* argument to the view() method.
*/
return _flitter.view(res, 'dash_v1:main', { projects })
}
new_project_show(req, res, next){
return _flitter.view(res, 'dash_v1:project', {})
}
async new_project_do(req, res, next){
if ( !req.body.name ){
return view(res, 'dash_v1:project', {errors: ['Project name is required.']})
}
const project = new Project({
name: req.body.name,
user_id: req.session.auth.uuid,
data: JSON.stringify({
created: Date.now(),
modified: Date.now()
}),
shared_user_ids: [],
})
await project.save()
return res.redirect('/dash/v1')
}
async project_view(req, res, next){
const project = await Project.findById(req.params.id)
const outs = await Out.find({ project_id: project.id }).sort('-created')
if ( !project ){
_flitter.error(res, 404, 'Project not found.')
}
return _flitter.view(res, 'dash_v1:view', { project, outs })
}
async out_view(req, res, next){
const out = await Out.findById(req.params.id)
console.log(out.data)
const pretty = JSON.stringify(JSON.parse(out.data), null, 4)
// TODO permission access check
return _flitter.view(res, 'dash_v1:out', {out, prettyd:pretty});
}
}
module.exports = exports = v1