diff --git a/lab-regan/.env b/lab-regan/.env new file mode 100644 index 0000000..fdb9a4b --- /dev/null +++ b/lab-regan/.env @@ -0,0 +1,3 @@ + +MONGODB_URI='mongodb://localhost/cfgrammy' +APP_SECRET='bestappsecret' diff --git a/lab-regan/.eslintrc b/lab-regan/.eslintrc new file mode 100644 index 0000000..f4ed946 --- /dev/null +++ b/lab-regan/.eslintrc @@ -0,0 +1,19 @@ +{ + "rules": { + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": [ "error", "single" ], + "semi": ["error", "always"], + "linebreak-style": [ "error", "unix" ] + }, + "env": { + "es6": true, + "node": true, + }, + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true, + "impliedStrict": true + }, + "extends": "eslint:recommended" +} diff --git a/lab-regan/.gitignore b/lab-regan/.gitignore new file mode 100644 index 0000000..5ed0369 --- /dev/null +++ b/lab-regan/.gitignore @@ -0,0 +1,132 @@ + +# Created by https://www.gitignore.io/api/osx,node,linux,windows + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +###Local Files### +data/ + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file + + + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/osx,node,linux,windows diff --git a/lab-regan/README.md b/lab-regan/README.md new file mode 100644 index 0000000..b5674f8 --- /dev/null +++ b/lab-regan/README.md @@ -0,0 +1,5 @@ +Basic Authorization + +This program contains uses bcrypt in conjunction with node.js's native module crypto to securely save users login credentials. + +For Grading purposes it contains a .env file which houses a couple environment variables we need to communicate with our MongoDB database and generate tokens. diff --git a/lab-regan/gulpfile.js b/lab-regan/gulpfile.js new file mode 100644 index 0000000..c36afa2 --- /dev/null +++ b/lab-regan/gulpfile.js @@ -0,0 +1,13 @@ +'use strict'; + +const gulp = require('gulp'); +const eslint = require('gulp-eslint'); + +gulp.task('lint', function(){ + return gulp.src(['**/*.js', '!node_modules']) + .pipe(eslint()) + .pipe(eslint.format()) + .pipe(eslint.failAfterError()); +}); + +gulp.task('default', ['lint']); diff --git a/lab-regan/lib/basic-auth-middleware.js b/lab-regan/lib/basic-auth-middleware.js new file mode 100644 index 0000000..3abd199 --- /dev/null +++ b/lab-regan/lib/basic-auth-middleware.js @@ -0,0 +1,39 @@ +'use strict'; + +const createError = require('http-errors'); +const debug = require('debug')('cfgram:basic-auth-middleware'); + +module.exports = function(req, res, next){ + debug('basic auth'); + + + var authHeader = req.headers.authorization; + + if(!authHeader){ + return next(createError(401, 'authorization header required')); + }; + + var base64str = authHeader.split('Basic ')[1]; + if(!base64str){ + return next(createError(401, 'username and password required')); + }; + + var utf8str = new Buffer(base64str, 'base64').toString(); + var authArr = utf8str.split(':'); + + req.auth = { + username: authArr[0], + password: authArr[1] + }; + + if(!req.auth.username){ + return next(createError(401, 'username required')); + }; + + if(!req.auth.password){ + return next(createError(401, 'password required')); + }; + + next(); + +}; diff --git a/lab-regan/lib/bearer-auth-middleware.js b/lab-regan/lib/bearer-auth-middleware.js new file mode 100644 index 0000000..0d84ae2 --- /dev/null +++ b/lab-regan/lib/bearer-auth-middleware.js @@ -0,0 +1,33 @@ +'use strict'; + +const debug = require('debug')('cfgram:bearer-auth-middleware'); +const createError = require('http-errors'); +const jwt = require('jsonwebtoken'); + +const User = require('../model/user.js'); + +module.exports = function(req, res, next){ + debug('bearer auth middleware'); + + var authHeader = req.headers.authorization; + if(!authHeader){ + return next(createError(401, 'not fun')); + }; + var token = authHeader.split('Bearer ')[1]; + if(!token){ + return next(createError(401, 'not good very bad')); + }; + + jwt.verify(token, process.env.APP_SECRET, (err, decoded) => { + if(err) return next(err); + User.findOne({ findHash: decoded.token }) + .then( user => { + req.user = user; + next(); + }) + .catch(err => { + next(createError(401, err.message)); + }); + }); + +}; diff --git a/lab-regan/lib/error-middleware.js b/lab-regan/lib/error-middleware.js new file mode 100644 index 0000000..d2563f7 --- /dev/null +++ b/lab-regan/lib/error-middleware.js @@ -0,0 +1,37 @@ +'use strict'; + +const createError = require('http-errors'); +const debug = require('debug')('cfgram:error-middleware'); + +module.exports = function(err, req, res, next){ + debug('error-middleware'); + + console.error('msg:', err.message); + console.error('name:', err.name); + +//400 solution + if(err.message === 'data and salt arguments required'){ + err = createError(400, 'so bad'); + debug(err.status); + res.status(err.status).send(err.name); + next(); + return; + }; + + if(err.status){ + res.status(err.status).send(err.name); + next(); + return; + }; + + if(err.name === 'ValidationError'){ + err = createError(400, err.message); + res.status(err.status).send(err.name); + next(); + return; + }; + + err = createError(404, err.message); + res.status(err.status).send(err.name); + next(); +}; diff --git a/lab-regan/model/gallery.js b/lab-regan/model/gallery.js new file mode 100644 index 0000000..8e3011e --- /dev/null +++ b/lab-regan/model/gallery.js @@ -0,0 +1,13 @@ +'use strict'; + +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const gallerySchema = Schema({ + name: {type: String, required: true}, + description: {type: String, required: true}, + created: {type: Date, required: true, default: Date.now }, + userID: { type: Schema.Types.ObjectId, required: true } +}); + +module.exports = mongoose.model('gallery', gallerySchema); diff --git a/lab-regan/model/user.js b/lab-regan/model/user.js new file mode 100644 index 0000000..1c93e82 --- /dev/null +++ b/lab-regan/model/user.js @@ -0,0 +1,69 @@ +'use strict'; + +const mongoose = require('mongoose'); +const debug = require('debug')('cfgram:user'); +const Promise = require('bluebird'); +const createError = require('http-errors'); +const crypto = require('crypto'); +const bcrypt = require('bcrypt'); +const jwt = require('jsonwebtoken'); + +const Schema = mongoose.Schema; + +const userSchema = Schema({ + username: {type: String, required: true, unique: true}, + password: {type: String, required: true}, + email: {type: String, required: true, unique: true}, + findHash: {type: String, unique: true} +}); + +userSchema.methods.generatePasswordHash = function(password){ + debug('generatePasswordHash'); + return new Promise((resolve, reject) => { + bcrypt.hash(password, 10, (err, hash) => { + if(err) return reject(err); + this.password = hash; + resolve(this); + }); + }); +}; + +userSchema.methods.comparePasswordHash = function(password){ + debug('comparePasswordHash'); + return new Promise((resolve, reject) => { + bcrypt.compare(password, this.password, (err, valid) => { + if(err) return reject(err); + if(!valid) return reject(createError(401, 'wrong password')); + resolve(this); + }); + }); +}; + +userSchema.methods.generateFindHash = function(){ + debug('generateFindHash'); + return new Promise((resolve, reject) => { + let tries = 0; + _generateFindHash.call(this); + function _generateFindHash(){ + this.findHash = crypto.randomBytes(32).toString('hex'); + this.save() + .then( () => resolve(this.findHash)) + .catch( err => { + if(tries > 3) return reject(err); + tries++; + _generateFindHash.call(this); + }); + }; + }); +}; + +userSchema.methods.generateToken = function(){ + debug('generateToken'); + return new Promise((resolve, reject) => { + this.generateFindHash() + .then( findHash => resolve(jwt.sign({token: findHash}, process.env.APP_SECRET ))) + .catch( err => reject(err)); + }); +}; + +module.exports = mongoose.model('user', userSchema); diff --git a/lab-regan/package.json b/lab-regan/package.json new file mode 100644 index 0000000..c04b2ee --- /dev/null +++ b/lab-regan/package.json @@ -0,0 +1,34 @@ +{ + "name": "lab-regan", + "version": "1.0.0", + "description": "", + "main": "server.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "DEBUG='cfgram*' mocha", + "start": "DEBUG='cfgram*' node server.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "bcrypt": "^1.0.2", + "bluebird": "^3.5.0", + "body-parser": "^1.17.1", + "cors": "^2.8.1", + "debug": "^2.6.1", + "dotenv": "^4.0.0", + "express": "^4.15.2", + "http-errors": "^1.6.1", + "jsonwebtoken": "^7.3.0", + "mongoose": "^4.8.6", + "morgan": "^1.8.1" + }, + "devDependencies": { + "chai": "^3.5.0", + "mocha": "^3.2.0", + "superagent": "^3.5.0" + } +} diff --git a/lab-regan/route/auth-router.js b/lab-regan/route/auth-router.js new file mode 100644 index 0000000..497e87b --- /dev/null +++ b/lab-regan/route/auth-router.js @@ -0,0 +1,36 @@ +'use strict'; + +const jsonParser = require('body-parser').json(); +const debug = require('debug')('cfgram:auth-router'); +const Router = require('express').Router; + +const User = require('../model/user.js'); +const basicAuth = require('../lib/basic-auth-middleware.js'); + +const authRouter = module.exports = Router(); + +authRouter.post('/api/signup', jsonParser, function(req, res, next){ + debug('POST: /api/signup'); + + let password = req.body.password; + delete req.body.password; + + let user = new User(req.body); + + user.generatePasswordHash(password) + .then( user => user.save()) + .then(user => user.generateToken()) + .then(token => res.send(token)) + .catch(next); +}); + + +authRouter.get('/api/signin', basicAuth, function(req, res, next){ + debug('GET: /api/signin'); + + User.findOne({username: req.auth.username}) + .then( user => user.comparePasswordHash(req.auth.password)) + .then( user => user.generateToken()) + .then( token => res.send(token)) + .catch(next); +}); diff --git a/lab-regan/route/gallery-router.js b/lab-regan/route/gallery-router.js new file mode 100644 index 0000000..b5a030f --- /dev/null +++ b/lab-regan/route/gallery-router.js @@ -0,0 +1,54 @@ +'use strict'; + +const debug = require('debug')('cfgram:gallery-router'); +const Router = require('express').Router; +const jsonParser = require('body-parser').json(); +const createError = require('http-errors'); + +const Gallery = require('../model/gallery.js'); +const bearerAuth = require('../lib/bearer-auth-middleware.js'); + +const galleryRouter = module.exports = Router(); + +galleryRouter.delete('/api/gallery/:id', bearerAuth, function(req, res, next){ + debug('DELETE: /api/gallery/:id'); + Gallery.findByIdAndRemove(req.params.id) + .then( removed => res.json(removed)) + .catch(next); +}); + +galleryRouter.post('/api/gallery', bearerAuth, jsonParser, function(req, res, next){ + debug('POST: /api/gallery'); + req.body.userID = req.user._id; + new Gallery(req.body).save() + .then( gallery => { + res.json(gallery); + }) + .catch(next); +}); + +galleryRouter.get('/api/gallery/:id', bearerAuth, function(req, res, next){ + debug('GET: /api/gallery'); + Gallery.findById(req.params.id) + .then( gallery => { + if(gallery.userID.toString() !== req.user._id.toString()){ + return next(createError(401, 'bad user name')); + }; + res.json(gallery); + }) + .catch(next); +}); + +galleryRouter.put('/api/gallery/:id', bearerAuth, jsonParser, function(req, res, next){ + debug('PUT: /api/gallery/:id'); + if(!req.body.name){ + return next(createError(400, 'Invalid body')); + }; + + if(!req.body.description){ + return next(createError(400, 'Invalid description')); + }; + Gallery.findByIdAndUpdate(req.params.id, {$set: req.body }, {new: true}) + .then( updatedGallery => res.json(updatedGallery)) + .catch(next); +}); diff --git a/lab-regan/server.js b/lab-regan/server.js new file mode 100644 index 0000000..8fc25e4 --- /dev/null +++ b/lab-regan/server.js @@ -0,0 +1,32 @@ +'use strict'; + +const express = require('express'); +const cors = require('cors'); +const dotenv = require('dotenv'); +const morgan = require('morgan'); +const mongoose = require('mongoose'); +const Promise = require('bluebird'); +const debug = require('debug')('cfgram:server'); + +const errors = require('./lib/error-middleware.js'); +const authRouter = require('./route/auth-router.js'); +const galleryRouter = require('./route/gallery-router.js'); + + +dotenv.load(); + +const PORT = process.env.PORT || 3000; +const app = express(); + +mongoose.connect(process.env.MONGODB_URI); + +app.use(cors()); +app.use(morgan('dev')); + +app.use(authRouter); +app.use(galleryRouter); +app.use(errors); + +app.listen(PORT, () => { + debug(`server up on port ${PORT}`); +}); diff --git a/lab-regan/test/auth-route-test.js b/lab-regan/test/auth-route-test.js new file mode 100644 index 0000000..b5cf57c --- /dev/null +++ b/lab-regan/test/auth-route-test.js @@ -0,0 +1,133 @@ +'use script'; + +const debug = require('debug')('cfgram:auth-route-test'); +const expect = require('chai').expect; +const request = require('superagent'); +const User = require('../model/user.js'); + +require('../server.js'); + +const url = `http://localhost:${process.env.PORT}`; +const exampleUser = { + username: 'exampleuser', + password: '9876', + email: 'example@example.com' +}; + +describe('Auth Routes', function(){ + describe('POST: /api/signup', function(){ + describe('with a valid body', function(){ + after( done => { + User.remove({}) + .then( () => done()) + .catch(done); + }); + it('should return a token', done => { + request.post(`${url}/api/signup`) + .send(exampleUser) + .end((err, res) => { + if(err) return done(err); + expect(res.status).to.equal(200); + expect(res.text).to.be.a('string'); + done(); + }); + }); + }); + + describe('with an invalid body', function(){ + it('should return a 400', done => { + request.post(`${url}/api/signup`) + .send() + .end((err, res) => { + expect(err).to.be.an('error'); + expect(err.status).to.equal(400); + done(); + }); + }); + }); + + describe('with an invalid path', function(){ + it('should return a 404', done => { + request.post(`${url}/apr`) + .send() + .end((err, res) => { + debug(err.status); + expect(err.status).to.equal(404); + done(); + }); + }); + }); + });//end post test + + describe('GET: /api/signin', function(){ + + describe('with an invalid request', function(){ + it('should return a 404', done => { + request.get(`${url}/api/test`) + .send() + .end((err, res) => { + expect(err).to.be.an('error'); + expect(res.status).to.equal(404); + done(); + }); + }); + }); + + describe('with a valid body', function(){ + before( done => { + let user = new User(exampleUser); + user.generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then( user => { + this.tempUser = user; + done(); + }) + .catch(done); + }); + after( done => { + User.remove({}) + .then( () => done()) + .catch(done); + }); + it('should a token', done => { + request.get(`${url}/api/signin`) + .auth('exampleuser', '9876') + .end((err, res) => { + if(err) return done(err); + console.log('temporary user: ', this.tempUser); + console.log('GET: /apl/signin token ', res.text); + expect(res.status).to.equal(200); + done(); + }); + }); + });//get + + describe('with an invalid credentials (password)', function(){ + before( done => { + let user = new User(exampleUser); + user.generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then( user => { + this.tempUser = user; + done(); + }) + .catch(done); + }); + after( done => { + User.remove({}) + .then( () => done()) + .catch(done); + }); + it('should return a 401', done => { + request.get(`${url}/api/signin`) + .auth('exampleuser', '0000') + .end((err, res) => { + expect(err).to.be.an('error'); + expect(res.status).to.equal(401); + done(); + }); + }); + }); + + });//end GET /api/signin +}); diff --git a/lab-regan/test/gallery-route-test.js b/lab-regan/test/gallery-route-test.js new file mode 100644 index 0000000..ca75bb2 --- /dev/null +++ b/lab-regan/test/gallery-route-test.js @@ -0,0 +1,545 @@ +'use strict'; + +const expect = require('chai').expect; +const request = require('superagent'); +const debug = require('debug')('cfgram:gallery-route-test'); +const mongoose = require('mongoose'); +const Promise = require('bluebird'); + +const Gallery = require('../model/gallery.js'); +const User = require('../model/user.js'); + +const url = `http://localhost:${process.env.PORT}`; + +const exampleUser = { + username: 'tester name', + password: 'tester password', + email: 'test@test.com' +}; +const exampleGallery = { + name: 'test gallery', + description: 'descriptive test' +}; +const exampleUpdatedGallery = { + name: 'updated test gallery', + description: 'updated descriptive test' +}; +const exampleBadUpdatedGallery = { + description: 'updated descriptive test' +}; +const badID = 6666666666666; + +describe('Gallery Routes', function(){ + + afterEach( done => { + Promise.all([ + User.remove({}), + Gallery.remove({}) + ]) + .then( () => done()) + .catch(done); + }); + + describe('POST: /api/gallery', () => { + describe('a valid request', function(){ + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then( user => { + this.tempUser = user; + return user.generateToken(); + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + it('should return a gallery (post request)', done => { + request.post(`${url}/api/gallery`) + .send(exampleGallery) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + if(err) return done(err); + let date = new Date(res.body.created).toString(); + expect(res.body.name).to.equal(exampleGallery.name); + expect(res.body.description).to.equal(exampleGallery.description); + expect(res.body.userID).to.equal(this.tempUser._id.toString()); + expect(date).to.not.equal('invalid'); + expect(res.status).to.equal(200); + done(); + }); + }); + }); + + describe('a request with no token', function(){ + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then( user => { + this.tempUser = user; + return user.generateToken(); + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + it('should return a 401 because of no token', done => { + request.post(`${url}/api/gallery`) + .send(exampleGallery) + .end((err, res) => { + expect(res.status).to.equal(401); + done(); + }); + }); + }); + + describe('no request body or invalid request body returns 400', function(){ + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then( user => { + this.tempUser = user; + return user.generateToken(); + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + it('should return a 400 because of no token', done => { + request.post(`${url}/api/gallery`) + .send(exampleBadUpdatedGallery) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + expect(res.status).to.equal(400); + done(); + }); + }); + }); +});//end post + + + describe('GET: /api/gallery/:id', () => { + + describe('with a valid request', function(){ + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should GET return a gallery', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + if(err) return done(err); + let date = new Date(res.body.created.toString()); + expect(res.body.name).to.equal(exampleGallery.name); + expect(res.body.description).to.equal(exampleGallery.description); + expect(res.body.userID).to.equal(this.tempUser._id.toString()); + expect(date).to.not.equal('invalid date'); + expect(res.status).to.equal(200); + done(); + }); + }); + }); + + describe('with an invalid request', function(){ + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should return a 401 for no token', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .end((err, res) => { + expect(res.status).to.equal(401); + done(); + }); + }); + }); + + describe('with a valid request and no id foud', function(){ + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should return a 404 for no id found', done => { + request.get(`${url}/api/gallery/${badID}`) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + expect(res.status).to.equal(404); + done(); + }); + }); + }); + });//end GET + + + describe('PUT: /api/gallery/:id', function() { + describe('with a valid body', () => { + + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should return a 400 for invalid body', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .send(exampleUpdatedGallery) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + expect(res.body.name).to.equal(exampleUpdatedGallery.name); + expect(res.body.description).to.equal(exampleUpdatedGallery.description); + expect(res.status).to.equal(200); + done(); + }); + }); + }); + describe('with an invalid body', () => { + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should return an updated gallery (put request)', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .send(exampleBadUpdatedGallery) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + expect(res.status).to.equal(400); + done(); + }); + }); + }); + + describe('with an invalid token', () => { + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should return a 401 due to no token', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .send(exampleUpdatedGallery) + .end((err, res) => { + expect(res.status).to.equal(401); + done(); + }); + }); + }); + + describe('with an invalid id should return 404', () => { + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should return a 404 - id not found', done => { + request.put(`${url}/api/gallery/${badID}`) + .send(exampleUpdatedGallery) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + expect(res.status).to.equal(404); + done(); + }); + }); + }); +});//end PUT + +describe('DELETE: /api/gallery/:id', () => { + + describe('with a valid request', function(){ + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should DELETE and return a gallery', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + if(err) return done(err); + debug(res.body.name); + expect(res.body.name).to.equal(exampleGallery.name); + // expect(res.body.description).to.equal(exampleGallery.description); + // expect(res.body.userID).to.equal(this.tempUser._id.toString()); + // expect(date).to.not.equal('invalid date'); + expect(res.status).to.equal(200); + done(); + }); + }); + }); + + + + describe('with an invalid request', function(){ + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should return a 401 for no token', done => { + request.delete(`${url}/api/gallery/${this.tempGallery._id}`) + .end((err, res) => { + expect(res.status).to.equal(401); + done(); + }); + }); +}); + + + +describe('valid request with an id that was not found', function(){ + before( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then(user => { + this.tempUser = user; + return user.generateToken() + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + before( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + this.tempGallery = gallery; + done(); + }) + .catch(done); + }); + after( () => { + delete exampleGallery.userID; + }); + it('should return a 404 because of a bad id', done => { + request.delete(`${url}/api/gallery/${badID}`) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + expect(res.status).to.equal(404); + done(); + }); + }); + }); +});//end delete + +});