From 2d3658e022cceef8fe2a05494a0fac521d47d742 Mon Sep 17 00:00:00 2001 From: noahgribbin Date: Mon, 6 Mar 2017 19:39:05 -0800 Subject: [PATCH 1/5] done --- .gitignore | 65 ++++++++++++++ dump/admin/system.version.bson | Bin 0 -> 59 bytes dump/admin/system.version.metadata.json | 1 + dump/api/foods.bson | 0 dump/api/foods.metadata.json | 1 + dump/cfgram/users.bson | Bin 0 -> 164 bytes dump/cfgram/users.metadata.json | 1 + dump/listappdev/lists.bson | 0 dump/listappdev/lists.metadata.json | 1 + lib/basic-auth-middleware.js | 35 ++++++++ lib/error-middleware.js | 29 ++++++ model/user.js | 75 ++++++++++++++++ package.json | 42 +++++++++ route/auth-router.js | 36 ++++++++ server.js | 29 ++++++ test/auth-router-test.js | 115 ++++++++++++++++++++++++ 16 files changed, 430 insertions(+) create mode 100644 .gitignore create mode 100644 dump/admin/system.version.bson create mode 100644 dump/admin/system.version.metadata.json create mode 100644 dump/api/foods.bson create mode 100644 dump/api/foods.metadata.json create mode 100644 dump/cfgram/users.bson create mode 100644 dump/cfgram/users.metadata.json create mode 100644 dump/listappdev/lists.bson create mode 100644 dump/listappdev/lists.metadata.json create mode 100644 lib/basic-auth-middleware.js create mode 100644 lib/error-middleware.js create mode 100644 model/user.js create mode 100644 package.json create mode 100644 route/auth-router.js create mode 100644 server.js create mode 100644 test/auth-router-test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5702678 --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ + +# Created by https://www.gitignore.io/api/node + +### 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 +.env + + +# End of https://www.gitignore.io/api/node diff --git a/dump/admin/system.version.bson b/dump/admin/system.version.bson new file mode 100644 index 0000000000000000000000000000000000000000..15b781e1790a8defef601a0d1c9d8996dbd5ab3e GIT binary patch literal 59 zcmcC!U|?X1&rD&E0W#836H7{qQl0a23ld8*lQMHMODe-si;6Sz^B9=QpbQqELSsD> G1_l7`2M|*L literal 0 HcmV?d00001 diff --git a/dump/admin/system.version.metadata.json b/dump/admin/system.version.metadata.json new file mode 100644 index 0000000..69290bd --- /dev/null +++ b/dump/admin/system.version.metadata.json @@ -0,0 +1 @@ +{"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"admin.system.version"},{"v":{"$numberDecimal":"2"},"key":{"version":1},"name":"incompatible_with_version_32","ns":"admin.system.version"}]} \ No newline at end of file diff --git a/dump/api/foods.bson b/dump/api/foods.bson new file mode 100644 index 0000000..e69de29 diff --git a/dump/api/foods.metadata.json b/dump/api/foods.metadata.json new file mode 100644 index 0000000..5c4a06b --- /dev/null +++ b/dump/api/foods.metadata.json @@ -0,0 +1 @@ +{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"api.foods"}]} \ No newline at end of file diff --git a/dump/cfgram/users.bson b/dump/cfgram/users.bson new file mode 100644 index 0000000000000000000000000000000000000000..2f533a80e18c2d7abacaac7b17f385b42817710c GIT binary patch literal 164 zcmZ3&z`(#BpP9lCv5%>dDMCa{V5tx@~ literal 0 HcmV?d00001 diff --git a/dump/cfgram/users.metadata.json b/dump/cfgram/users.metadata.json new file mode 100644 index 0000000..cdc23a7 --- /dev/null +++ b/dump/cfgram/users.metadata.json @@ -0,0 +1 @@ +{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"cfgram.users"},{"v":2,"unique":true,"key":{"username":1},"name":"username_1","ns":"cfgram.users","background":true},{"v":2,"unique":true,"key":{"email":1},"name":"email_1","ns":"cfgram.users","background":true},{"v":2,"unique":true,"key":{"findHash":1},"name":"findHash_1","ns":"cfgram.users","background":true}]} \ No newline at end of file diff --git a/dump/listappdev/lists.bson b/dump/listappdev/lists.bson new file mode 100644 index 0000000..e69de29 diff --git a/dump/listappdev/lists.metadata.json b/dump/listappdev/lists.metadata.json new file mode 100644 index 0000000..82b5b34 --- /dev/null +++ b/dump/listappdev/lists.metadata.json @@ -0,0 +1 @@ +{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"listappdev.lists"}]} \ No newline at end of file diff --git a/lib/basic-auth-middleware.js b/lib/basic-auth-middleware.js new file mode 100644 index 0000000..a9e9cfb --- /dev/null +++ b/lib/basic-auth-middleware.js @@ -0,0 +1,35 @@ +'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/lib/error-middleware.js b/lib/error-middleware.js new file mode 100644 index 0000000..3c0f42b --- /dev/null +++ b/lib/error-middleware.js @@ -0,0 +1,29 @@ +'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); + + if (err.name === 'ValidationError') { + err = createError(400, err.message); + res.status(err.status).send(err.name); + next(); + return; + } + + if (err.status) { + res.status(err.status).send(err.name); + next(); + return; + } + + + err = createError(500, err.message); + res.status(err.status).send(err.name); + next(); +}; diff --git a/model/user.js b/model/user.js new file mode 100644 index 0000000..d0f3563 --- /dev/null +++ b/model/user.js @@ -0,0 +1,75 @@ +'use strict'; + +const crypto = require('crypto'); +const bcrypt = require('bcrypt'); +const jwt = require('jsonwebtoken'); +const mongoose = require('mongoose'); +const createError = require('http-errors'); +const Promise = require('bluebird'); +const debug = require('debug')('cfgram:user'); + +const Schema = mongoose.Schema; + +const userSchema = Schema({ + username: { type: String, required: true, unique: true }, + email: { type: String, required: true, unique: true }, + password: { type: String, required: 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/package.json b/package.json new file mode 100644 index 0000000..393ebde --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "16-basic_auth", + "version": "1.0.0", + "description": "![cf](https://i.imgur.com/7v5ASc8.png) Lab 16 - Basic Auth\r ======", + "main": "server.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "DEBUG='cfgram*' mocha", + "start": "DEBUG='cfgram*' node server.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/noahgribbin/16-basic_auth.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/noahgribbin/16-basic_auth/issues" + }, + "homepage": "https://github.com/noahgribbin/16-basic_auth#readme", + "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/route/auth-router.js b/route/auth-router.js new file mode 100644 index 0000000..424119b --- /dev/null +++ b/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 basicAuth = require('../lib/basic-auth-middleware.js'); +const createError = require('http-errors'); + +const User = require('../model/user.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(err => next(createError(400, err.message))); +}); + +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/server.js b/server.js new file mode 100644 index 0000000..c23b1b5 --- /dev/null +++ b/server.js @@ -0,0 +1,29 @@ +'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 authRouter = require('./route/auth-router.js') +const errors = require('./lib/error-middleware.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(errors); + +app.listen(PORT, () => { + debug(`server up: ${PORT}`); +}); diff --git a/test/auth-router-test.js b/test/auth-router-test.js new file mode 100644 index 0000000..5fded2b --- /dev/null +++ b/test/auth-router-test.js @@ -0,0 +1,115 @@ +'use strict'; + +const expect = require('chai').expect; +const request = require('superagent'); +const mongoose = require('mongoose'); +const Promise = require('bluebird'); +const User = require('../model/user.js'); + +mongoose.Promise = Promise; + +require('../server.js'); + +const url = `http://localhost:${process.env.PORT}`; + +const exampleUser = { + username: 'exampleuser', + password: '1234', + email: 'exampleuser@test.com' +}; + +describe('Auth Routes', function() { + describe('POST: /api/signup', function() { + describe('with a valid body', function() { + after( done => { + User.remove({}) + .then( () => done()) + .catch(done); + }); + before( 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); + console.log('\ntoken:', res.text, '\n'); + expect(res.status).to.equal(200); + expect(res.text).to.be.a('string'); + done(); + }); + }); + }); + describe('with an invalid body', function() { + it('should return 400', done => { + request.post(`${url}/api/signup`) + .send({ + username: 'exampleuseraaa', + password: '1234' + }) + .end((err, res) => { + if(err) return done(err); + expect(res.status).to.equal(400); + done(); + }); + }); + }); + }); + + describe('GET: /api/signin', function() { + 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 return a token', done => { + request.get(`${url}/api/signin`) + .auth('exampleuser', '1234') + .end((err, res) => { + if (err) return done(err); + console.log('\nuser:', this.tempUser); + console.log('\ntoken:', res.text); + expect(res.status).to.equal(200); + done(); + }); + }); + }); + describe('with an invalid 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); + }); + it('should return 401, authentication error', done => { + request.get(`${url}/api/signin`) + .auth('exampleusertest') + .end((err, res) => { + expect(res.status).to.equal(401); + done(); + }); + }); + }); + }); +}); From 056e8b66b57b86a570ede4f1d22f1839782ab240 Mon Sep 17 00:00:00 2001 From: noahgribbin Date: Tue, 7 Mar 2017 20:11:13 -0800 Subject: [PATCH 2/5] added gallery routes --- lib/basic-auth-middleware.js | 4 +- lib/bearer-auth-middleware.js | 38 +++++ model/gallery.js | 13 ++ model/user.js | 1 + route/gallery-router.js | 54 ++++++++ server.js | 2 + test/auth-router-test.js | 12 +- test/gallery-route-test.js | 252 ++++++++++++++++++++++++++++++++++ 8 files changed, 367 insertions(+), 9 deletions(-) create mode 100644 lib/bearer-auth-middleware.js create mode 100644 model/gallery.js create mode 100644 route/gallery-router.js create mode 100644 test/gallery-route-test.js diff --git a/lib/basic-auth-middleware.js b/lib/basic-auth-middleware.js index a9e9cfb..0dbd522 100644 --- a/lib/basic-auth-middleware.js +++ b/lib/basic-auth-middleware.js @@ -6,7 +6,9 @@ const debug = require('debug')('cfgram:basic-auth-middleware'); module.exports = function(req, res, next) { debug('basic auth'); + // console.log('LOOK HERE NOAH, BEFORE:', req.headers.authorization); var authHeader = req.headers.authorization; + // console.log('LOOK HERE NOAH, AFTER:', req.headers.authorization); if(!authHeader) { return next(createError(401, 'authorization header required')); } @@ -30,6 +32,6 @@ module.exports = function(req, res, next) { if(!req.auth.password){ return next(createError(401, 'password required')); } - + next(); }; diff --git a/lib/bearer-auth-middleware.js b/lib/bearer-auth-middleware.js new file mode 100644 index 0000000..491f7ba --- /dev/null +++ b/lib/bearer-auth-middleware.js @@ -0,0 +1,38 @@ +'use strict'; + +const jwt = require('jsonwebtoken'); +const createError = require('http-errors'); +const debug = require('debug')('cfgram:bearer-auth-middleware'); + +const User = require('../model/user.js'); + +module.exports = function(req, res, next) { + debug('bearer-auth'); + // console.log('req headers auth:', req.headers.authorization); + // console.log('Request:', req); + var authHeader = req.headers.authorization; + // console.log('req headers:', req.headers); + if(!authHeader) { + return next(createError(401, 'authorization header required')); + } + + var token = authHeader.split('Bearer ')[1]; + if(!token) { + return next(createError(401, 'token required')); + } + // console.log('Auth Header:', authHeader); + // console.log('token', token); + jwt.verify(token, process.env.APP_SECRET, (err, decoded) => { + if (err) return next(err); + // console.log('DECODED:', decoded); + + User.findOne({ findHash: decoded.token}) + .then( user => { + req.user = user; + next(); + }) + .catch(err => { + next(createError(401, err.message)); + }); + }); +}; diff --git a/model/gallery.js b/model/gallery.js new file mode 100644 index 0000000..22d6f75 --- /dev/null +++ b/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}, + desc: {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/model/user.js b/model/user.js index d0f3563..dd4c376 100644 --- a/model/user.js +++ b/model/user.js @@ -57,6 +57,7 @@ userSchema.methods.generateFindHash = function() { if(tries > 3) return reject(err); tries++; _generateFindHash.call(this); + }); } }); diff --git a/route/gallery-router.js b/route/gallery-router.js new file mode 100644 index 0000000..9386751 --- /dev/null +++ b/route/gallery-router.js @@ -0,0 +1,54 @@ +'use strict' + +const Router = require('express').Router; +const jsonParser = require('body-parser').json(); +const createError = require('http-errors'); +const debug = require('debug')('cfgram:gallery-router'); + +const Gallery = require('../model/gallery.js'); +const bearerAuth = require('../lib/bearer-auth-middleware.js'); + +const galleryRouter = module.exports = Router(); + +galleryRouter.post('/api/gallery', bearerAuth, jsonParser, function(req, res, next) { + debug('POST /api/gallery'); + + //the gallery will not valdate without the req.body BECAUSE the REQUIRED id was just attached to it + 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/galler/:id'); + + Gallery.findById(req.params.id) + .then( gallery => { + if (gallery.userID.toString() !== req.user._id.toString()) { + return next(createError(401, 'invalid user')); + } + res.json(gallery); + }) + .catch(next); +}); + +galleryRouter.put('/api/gallery/:id', bearerAuth, jsonParser, function(req, res, next) { + debug('PUT /api/galler/:id'); + Gallery.findByIdAndUpdate(req.params.id, res.body, {new:true}) + .then( gallery => { + if (gallery.userID.toString() !== req.user._id.toString()) { + return next(createError(401, 'invalid user')); + } + res.json(gallery); + }) + .catch(next); +}); + +galleryRouter.delete('/api/gallery/:id', function(req, res, next) { + debug('DELETE /api/galler/:id'); + + Gallery.findByIdAndRemove(req.params.id) + .then( () => res.status(204).send()) + .catch( err => next(createError(404, err.message))); +}); diff --git a/server.js b/server.js index c23b1b5..b49095e 100644 --- a/server.js +++ b/server.js @@ -9,6 +9,7 @@ const Promise = require('bluebird'); const debug = require('debug')('cfgram:server'); const authRouter = require('./route/auth-router.js') +const galleryRouter = require('./route/gallery-router.js') const errors = require('./lib/error-middleware.js') dotenv.load(); @@ -22,6 +23,7 @@ app.use(cors()); app.use(morgan('dev')); app.use(authRouter); +app.use(galleryRouter); app.use(errors); app.listen(PORT, () => { diff --git a/test/auth-router-test.js b/test/auth-router-test.js index 5fded2b..f663c80 100644 --- a/test/auth-router-test.js +++ b/test/auth-router-test.js @@ -6,8 +6,6 @@ const mongoose = require('mongoose'); const Promise = require('bluebird'); const User = require('../model/user.js'); -mongoose.Promise = Promise; - require('../server.js'); const url = `http://localhost:${process.env.PORT}`; @@ -31,15 +29,14 @@ describe('Auth Routes', function() { .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); - console.log('\ntoken:', res.text, '\n'); + // console.log('OVER HEREEEEEEE:', res); expect(res.status).to.equal(200); - expect(res.text).to.be.a('string'); + expect(res.text).to.be.a('string') ; done(); }); }); @@ -52,7 +49,7 @@ describe('Auth Routes', function() { password: '1234' }) .end((err, res) => { - if(err) return done(err); + // if(err) return done(err); expect(res.status).to.equal(400); done(); }); @@ -82,10 +79,9 @@ describe('Auth Routes', function() { it('should return a token', done => { request.get(`${url}/api/signin`) .auth('exampleuser', '1234') + // .end((err, res) => { if (err) return done(err); - console.log('\nuser:', this.tempUser); - console.log('\ntoken:', res.text); expect(res.status).to.equal(200); done(); }); diff --git a/test/gallery-route-test.js b/test/gallery-route-test.js new file mode 100644 index 0000000..4d33a04 --- /dev/null +++ b/test/gallery-route-test.js @@ -0,0 +1,252 @@ +'use strict'; + +const expect = require('chai').expect; +const request = require('superagent'); +const mongoose = require('mongoose'); +const Promise = require('bluebird'); +const User = require('../model/user.js'); +const Gallery = require('../model/gallery.js'); + +const url = `http://localhost:${process.env.PORT}`; + + +const exampleUser = { + username: 'exampleuser', + password: '1234', + email: 'exampleuser@test.com' +}; +const updatedUser = { + username: 'updateduser', + password: '1234', + email: 'exampleuser@test.com' +}; +const invalidUser = { + username: 1, + password: '1234', + email: 'exampleuser@test.com' +}; +const exampleGallery = { + name: 'test gallery', + desc: 'test gallery description', +}; + +describe('Gallery Routes', function() { + afterEach(done => { + Promise.all([ + User.remove({}), + Gallery.remove({}) + ]) + .then( () => done()) + .catch(done); + }); + + describe('POST: /api/gallery', () => { + beforeEach( done => { + User.remove({}) + .then( () => done()) + .catch(done); + }); + beforeEach( done => { + new User(exampleUser) + .generatePasswordHash(exampleUser.password) + .then( user => user.save()) + .then( user => { + this.tempUser = user; + console.log('intermediate value?:', user); + return user.generateToken(); + }) + .then( token => { + this.tempToken = token; + done(); + }) + .catch(done); + }); + afterEach( done => { + User.remove({}) + .then( () => done()) + .catch(done); + }); + it('should return a gallery', 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.desc).to.equal(exampleGallery.desc); + expect(res.body.userID).to.equal(this.tempUser._id.toString()); + expect(date).to.not.equal('invalid date'); + expect(res.status).to.equal(200); + done(); + }); + }); + it('should return 401, no token', done => { + request.post(`${url}/api/gallery`) + .send(exampleGallery) + .set({ + Authorization: `Bearer ` + }) + .end((err, res) => { + // if (err) return done(err); + expect(res.status).to.equal(401); + done(); + }); + }); + it('should return 400, invalid body', done => { + request.post(`${url}/api/gallery`) + .send({}) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + // if (err) return done(err); + expect(res.status).to.equal(400); + done(); + }); + }); + }); + describe('GET: /api/gallery/:id', function() { + beforeEach( 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); + }); + + beforeEach( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + // console.log('real gallery:', gallery._id); + this.tempGallery = gallery; + // console.log('tempGallery:', this.tempGallery._id); + done(); + }) + .catch(done); + }); + + afterEach( () => { + delete exampleGallery.userID; + }); + // after( done => { + // User.remove({}) + // .then( () => done()) + // .catch(done); + // }); + + it('should 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.desc).to.equal(exampleGallery.desc); + expect(res.body.userID).to.equal(this.tempUser._id.toString()); + expect(res.status).to.equal(200); + expect(date).to.not.equal('invalid date'); + done(); + }); + }); + it('should return 401, no token', done => { + request.get(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: 'Bearer ' + }) + .end((err, res) => { + // if (err) return done(err); + expect(res.status).to.equal(401); + done(); + }); + }); + it('should return 404', done => { + request.get(`${url}/apu/test`) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .end((err, res) => { + // if (err) return done(err); + expect(res.status).to.equal(404); + done(); + }); + }); + }); + describe('PUT: /api/gallery/:id', () => { + beforeEach(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); + }); + beforeEach( done => { + exampleGallery.userID = this.tempUser._id.toString(); + new Gallery(exampleGallery).save() + .then( gallery => { + // console.log('real gallery:', gallery._id); + this.tempGallery = gallery; + // console.log('tempGallery:', this.tempGallery._id); + done(); + }) + .catch(done); + }); + it('should update a gallery', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .send(updatedUser) + .end((err, res) => { + if (err) return done(err); + let date = new Date(res.body.created).toString(); + expect(res.status).to.equal(200); + expect(date).to.not.equal('invalid date'); + done(); + }); + }); + it('should return 401, no token', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ` + }) + .send(updatedUser) + .end((err, res) => { + // if (err) return done(err); + expect(res.status).to.equal(401); + done(); + }); + }); + it('should return 400, invalid body', done => { + request.put(`${url}/api/gallery/${this.tempGallery._id}`) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .send(invalidUser) + .end((err, res) => { + if (err) return done(err); + expect(res.status).to.equal(400); + done(); + }); + }); + }); +}); From 53308be2d36bcad433eec6a35946829586ccd821 Mon Sep 17 00:00:00 2001 From: noahgribbin Date: Wed, 8 Mar 2017 14:06:34 -0800 Subject: [PATCH 3/5] POST pic routes/test --- dump/admin/system.version.bson | Bin 59 -> 0 bytes dump/admin/system.version.metadata.json | 1 - dump/api/foods.bson | 0 dump/api/foods.metadata.json | 1 - dump/cfgram/users.bson | Bin 164 -> 0 bytes dump/cfgram/users.metadata.json | 1 - dump/listappdev/lists.bson | 0 dump/listappdev/lists.metadata.json | 1 - model/pic.js | 16 +++ package.json | 5 +- route/gallery-router.js | 2 +- route/pic-router.js | 70 ++++++++++++ server.js | 12 ++- test/data/tester.png | Bin 0 -> 5920 bytes ...y-route-test.js => gallery-router-test.js} | 7 +- test/lib/server-toggle.js | 29 +++++ test/pic-router-test.js | 102 ++++++++++++++++++ 17 files changed, 234 insertions(+), 13 deletions(-) delete mode 100644 dump/admin/system.version.bson delete mode 100644 dump/admin/system.version.metadata.json delete mode 100644 dump/api/foods.bson delete mode 100644 dump/api/foods.metadata.json delete mode 100644 dump/cfgram/users.bson delete mode 100644 dump/cfgram/users.metadata.json delete mode 100644 dump/listappdev/lists.bson delete mode 100644 dump/listappdev/lists.metadata.json create mode 100644 model/pic.js create mode 100644 route/pic-router.js create mode 100644 test/data/tester.png rename test/{gallery-route-test.js => gallery-router-test.js} (97%) create mode 100644 test/lib/server-toggle.js create mode 100644 test/pic-router-test.js diff --git a/dump/admin/system.version.bson b/dump/admin/system.version.bson deleted file mode 100644 index 15b781e1790a8defef601a0d1c9d8996dbd5ab3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59 zcmcC!U|?X1&rD&E0W#836H7{qQl0a23ld8*lQMHMODe-si;6Sz^B9=QpbQqELSsD> G1_l7`2M|*L diff --git a/dump/admin/system.version.metadata.json b/dump/admin/system.version.metadata.json deleted file mode 100644 index 69290bd..0000000 --- a/dump/admin/system.version.metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"options":{},"indexes":[{"v":1,"key":{"_id":1},"name":"_id_","ns":"admin.system.version"},{"v":{"$numberDecimal":"2"},"key":{"version":1},"name":"incompatible_with_version_32","ns":"admin.system.version"}]} \ No newline at end of file diff --git a/dump/api/foods.bson b/dump/api/foods.bson deleted file mode 100644 index e69de29..0000000 diff --git a/dump/api/foods.metadata.json b/dump/api/foods.metadata.json deleted file mode 100644 index 5c4a06b..0000000 --- a/dump/api/foods.metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"api.foods"}]} \ No newline at end of file diff --git a/dump/cfgram/users.bson b/dump/cfgram/users.bson deleted file mode 100644 index 2f533a80e18c2d7abacaac7b17f385b42817710c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmZ3&z`(#BpP9lCv5%>dDMCa{V5tx@~ diff --git a/dump/cfgram/users.metadata.json b/dump/cfgram/users.metadata.json deleted file mode 100644 index cdc23a7..0000000 --- a/dump/cfgram/users.metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"cfgram.users"},{"v":2,"unique":true,"key":{"username":1},"name":"username_1","ns":"cfgram.users","background":true},{"v":2,"unique":true,"key":{"email":1},"name":"email_1","ns":"cfgram.users","background":true},{"v":2,"unique":true,"key":{"findHash":1},"name":"findHash_1","ns":"cfgram.users","background":true}]} \ No newline at end of file diff --git a/dump/listappdev/lists.bson b/dump/listappdev/lists.bson deleted file mode 100644 index e69de29..0000000 diff --git a/dump/listappdev/lists.metadata.json b/dump/listappdev/lists.metadata.json deleted file mode 100644 index 82b5b34..0000000 --- a/dump/listappdev/lists.metadata.json +++ /dev/null @@ -1 +0,0 @@ -{"options":{},"indexes":[{"v":2,"key":{"_id":1},"name":"_id_","ns":"listappdev.lists"}]} \ No newline at end of file diff --git a/model/pic.js b/model/pic.js new file mode 100644 index 0000000..6ae6ecd --- /dev/null +++ b/model/pic.js @@ -0,0 +1,16 @@ +'use strict'; + +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const picSchema = Schema({ + name: {type: String, required: true}, + desc: {type: String, required: true}, + userID: {type: Schema.Types.ObjectId, required: true}, + galleryID: {type: Schema.Types.ObjectId, required: true}, + imageURI: {type: String, required: true, unique: true}, + objectKey: {type: String, required: true, unique: true}, + created: {type: Date, default: Date.now}, +}); + +module.exports = mongoose.model('pic', picSchema); diff --git a/package.json b/package.json index 393ebde..63a0b49 100644 --- a/package.json +++ b/package.json @@ -22,17 +22,20 @@ }, "homepage": "https://github.com/noahgribbin/16-basic_auth#readme", "dependencies": { + "aws-sdk": "^2.24.0", "bcrypt": "^1.0.2", "bluebird": "^3.5.0", "body-parser": "^1.17.1", "cors": "^2.8.1", "debug": "^2.6.1", + "del": "^2.2.2", "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" + "morgan": "^1.8.1", + "multer": "^1.3.0" }, "devDependencies": { "chai": "^3.5.0", diff --git a/route/gallery-router.js b/route/gallery-router.js index 9386751..4770fca 100644 --- a/route/gallery-router.js +++ b/route/gallery-router.js @@ -1,4 +1,4 @@ -'use strict' +'use strict'; const Router = require('express').Router; const jsonParser = require('body-parser').json(); diff --git a/route/pic-router.js b/route/pic-router.js new file mode 100644 index 0000000..5a7d3c9 --- /dev/null +++ b/route/pic-router.js @@ -0,0 +1,70 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const del = require('del'); +const AWS = require('aws-sdk'); +const multer = require('multer'); +const Router = require('express').Router; +const createError = require('http-errors'); +const debug = require('debug')('cfgram:pic-router'); + +const Pic = require('../model/pic.js'); +const Gallery = require('../model/gallery.js'); +const bearerAuth = require('../lib/bearer-auth-middleware.js'); + +AWS.config.setPromisesDependency(require('bluebird')); + +const s3 = new AWS.S3(); +const dataDir = `${__dirname}/../data`; +const upload = multer({dest: dataDir}); + +const picRouter = module.exports = Router(); + +function s3uploadProm(params) { + debug('s3uploadProm'); + + return new Promise((resolve, reject) => { + s3.upload(params, (err, s3data) => { + resolve(s3data); + }); + }); +} + +picRouter.post('/api/gallery/:galleryID/pic', bearerAuth, upload.single('image'), function(req, res, next) { + debug('POST: /api/gallery/:galleryID/pic'); + + if(!req.file) { + return next(createError(400, 'file not found')); + } + + if(!req.file.path) { + return next(createError(500, 'file not saved')); + } + + let ext = path.extname(req.file.originalname); + + let params = { + ACL: 'public-read', + Bucket: process.env.AWS_BUCKET, + Key: `${req.file.filename}${ext}`, + Body: fs.createReadStream(req.file.path) + }; + + Gallery.findById(req.params.galleryID) + .then( () => s3uploadProm(params)) + .then( s3data => { + del([`${dataDir}/*`]); + let picData = { + name: req.body.name, + desc: req.body.desc, + objectKey: s3data.Key, + imageURI: s3data.Location, + userID: req.user._id, + galleryID: req.params.galleryID + }; + return new Pic(picData).save(); + }) + .then(pic => res.json(pic)) + .catch( err => next(err)); +}); diff --git a/server.js b/server.js index b49095e..67e698b 100644 --- a/server.js +++ b/server.js @@ -8,9 +8,10 @@ const mongoose = require('mongoose'); const Promise = require('bluebird'); const debug = require('debug')('cfgram:server'); -const authRouter = require('./route/auth-router.js') -const galleryRouter = require('./route/gallery-router.js') -const errors = require('./lib/error-middleware.js') +const authRouter = require('./route/auth-router.js'); +const galleryRouter = require('./route/gallery-router.js'); +const picRouter = require('./route/pic-router.js'); +const errors = require('./lib/error-middleware.js'); dotenv.load(); @@ -24,8 +25,11 @@ app.use(morgan('dev')); app.use(authRouter); app.use(galleryRouter); +app.use(picRouter); app.use(errors); -app.listen(PORT, () => { +const server = module.exports = app.listen(PORT, () => { debug(`server up: ${PORT}`); }); + +server.isRunning = true; diff --git a/test/data/tester.png b/test/data/tester.png new file mode 100644 index 0000000000000000000000000000000000000000..04c99f2bcf29a75773e672165f5efbf0ac5e17ce GIT binary patch literal 5920 zcmV+*7vJcKP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 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 { // if (err) return done(err); @@ -227,7 +227,7 @@ describe('Gallery Routes', function() { it('should return 401, no token', done => { request.put(`${url}/api/gallery/${this.tempGallery._id}`) .set({ - Authorization: `Bearer ` + Authorization: 'Bearer ' }) .send(updatedUser) .end((err, res) => { @@ -237,6 +237,7 @@ describe('Gallery Routes', function() { }); }); it('should return 400, invalid body', done => { + // test is passing from before each maybe? request.put(`${url}/api/gallery/${this.tempGallery._id}`) .set({ Authorization: `Bearer ${this.tempToken}` diff --git a/test/lib/server-toggle.js b/test/lib/server-toggle.js new file mode 100644 index 0000000..25ff01a --- /dev/null +++ b/test/lib/server-toggle.js @@ -0,0 +1,29 @@ +'use strict'; + +const debug = require('debug')('cfgram:server-toggle'); + +module.exports = exports = {}; + +exports.serverOn = function(server, done) { + if(!server.isRunning) { + server.listen(process.env.PORT, () => { + server.isRunning = true; + debug('server up!'); + done(); + }); + return; + } + done(); +}; + +exports.serverOff = function(server, done) { + if(server.isRunning) { + server.close( err => { + server.isRunning = false; + debug('server off!'); + done(); + }); + return; + } + done(); +}; diff --git a/test/pic-router-test.js b/test/pic-router-test.js new file mode 100644 index 0000000..7cfcffe --- /dev/null +++ b/test/pic-router-test.js @@ -0,0 +1,102 @@ +'use strict'; + +//fix naming convention of tests!!!! +const expect = require('chai').expect; +const request = require('superagent'); +const debug = require('debug')('cfgram:pic-router-test'); + +const Pic = require('../model/pic.js'); +const User = require('../model/user.js'); +const Gallery = require('../model/gallery.js'); + +const serverToggle = require('./lib/server-toggle.js') +const server = require('../server.js') + +const url = `http://localhost:${process.env.PORT}`; + +const exampleUser = { + username: 'exampleser', + password: '1234', + email: 'exampleuser@test.com' +}; + +const exampleGallery = { + name: 'test gallery', + desc: 'test gallery description' +}; + +const examplePic = { + name: 'example pic', + desc: 'example pic desc', + image: `${__dirname}/data/tester.png` +}; + +describe('Pic Routes', function() { + before(done => { + serverToggle.serverOn(server, done); + }); + after( done => { + serverToggle.serverOff(server, done); + }); + afterEach( done => { + Promise.all([ + Pic.remove({}), + User.remove({}), + Gallery.remove({}) + ]) + .then( () => done()) + .catch(done); + }); + + describe('POST: /api/gallery/galleryID/pic', function() { + describe('with a valid body', 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( done => { + delete exampleGallery.userID; + done(); + }); + + it('should return a pic', done => { + request.post(`${url}/api/gallery/${this.tempGallery._id}/pic`) + .set({ + Authorization: `Bearer ${this.tempToken}` + }) + .field('name', examplePic.name) + .field('desc', examplePic.desc) + .attach('image', examplePic.image) + .end((err, res) => { + if(err) return done(err); + expect(res.body.name).to.equal(examplePic.name); + expect(res.body.desc).to.equal(examplePic.desc); + expect(res.body.galleryID).to.equal(this.tempGallery._id.toString()); + done(); + }); + }); + }); + }); +}); From ad1f4d28af9a0d70439cd117654eba190a8563ce Mon Sep 17 00:00:00 2001 From: noahgribbin Date: Thu, 9 Mar 2017 09:54:12 -0800 Subject: [PATCH 4/5] added travis yml --- .travis.yml | 19 +++++++++++++++++++ test/gallery-router-test.js | 16 ---------------- test/pic-router-test.js | 5 ++--- 3 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b27e134 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: node_js +node_js: + - 'stable' +services: + - mongodb +addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - gcc-4.8 + - g++-4.8 +env: + - CXX=g++-4.8 +sudo: required +before_script: npm i +script: + -npm test + -npm lint diff --git a/test/gallery-router-test.js b/test/gallery-router-test.js index 06b4a89..c26ebf6 100644 --- a/test/gallery-router-test.js +++ b/test/gallery-router-test.js @@ -90,7 +90,6 @@ describe('Gallery Routes', function() { Authorization: 'Bearer ' }) .end((err, res) => { - // if (err) return done(err); expect(res.status).to.equal(401); done(); }); @@ -102,7 +101,6 @@ describe('Gallery Routes', function() { Authorization: `Bearer ${this.tempToken}` }) .end((err, res) => { - // if (err) return done(err); expect(res.status).to.equal(400); done(); }); @@ -128,9 +126,7 @@ describe('Gallery Routes', function() { exampleGallery.userID = this.tempUser._id.toString(); new Gallery(exampleGallery).save() .then( gallery => { - // console.log('real gallery:', gallery._id); this.tempGallery = gallery; - // console.log('tempGallery:', this.tempGallery._id); done(); }) .catch(done); @@ -139,11 +135,6 @@ describe('Gallery Routes', function() { afterEach( () => { delete exampleGallery.userID; }); - // after( done => { - // User.remove({}) - // .then( () => done()) - // .catch(done); - // }); it('should return a gallery', done => { request.get(`${url}/api/gallery/${this.tempGallery._id}`) @@ -167,7 +158,6 @@ describe('Gallery Routes', function() { Authorization: 'Bearer ' }) .end((err, res) => { - // if (err) return done(err); expect(res.status).to.equal(401); done(); }); @@ -178,7 +168,6 @@ describe('Gallery Routes', function() { Authorization: `Bearer ${this.tempToken}` }) .end((err, res) => { - // if (err) return done(err); expect(res.status).to.equal(404); done(); }); @@ -203,9 +192,7 @@ describe('Gallery Routes', function() { exampleGallery.userID = this.tempUser._id.toString(); new Gallery(exampleGallery).save() .then( gallery => { - // console.log('real gallery:', gallery._id); this.tempGallery = gallery; - // console.log('tempGallery:', this.tempGallery._id); done(); }) .catch(done); @@ -231,20 +218,17 @@ describe('Gallery Routes', function() { }) .send(updatedUser) .end((err, res) => { - // if (err) return done(err); expect(res.status).to.equal(401); done(); }); }); it('should return 400, invalid body', done => { - // test is passing from before each maybe? request.put(`${url}/api/gallery/${this.tempGallery._id}`) .set({ Authorization: `Bearer ${this.tempToken}` }) .send(invalidUser) .end((err, res) => { - if (err) return done(err); expect(res.status).to.equal(400); done(); }); diff --git a/test/pic-router-test.js b/test/pic-router-test.js index 7cfcffe..aa7ad29 100644 --- a/test/pic-router-test.js +++ b/test/pic-router-test.js @@ -1,6 +1,5 @@ 'use strict'; -//fix naming convention of tests!!!! const expect = require('chai').expect; const request = require('superagent'); const debug = require('debug')('cfgram:pic-router-test'); @@ -9,8 +8,8 @@ const Pic = require('../model/pic.js'); const User = require('../model/user.js'); const Gallery = require('../model/gallery.js'); -const serverToggle = require('./lib/server-toggle.js') -const server = require('../server.js') +const serverToggle = require('./lib/server-toggle.js'); +const server = require('../server.js'); const url = `http://localhost:${process.env.PORT}`; From 486d11e24ffd3fd90c68364d0523f1b2e070dc2b Mon Sep 17 00:00:00 2001 From: noahgribbin Date: Thu, 9 Mar 2017 10:29:36 -0800 Subject: [PATCH 5/5] added coveralls --- .coveralls.yml | 2 ++ .travis.yml | 4 ++-- package.json | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..bc7e1df --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1,2 @@ +service_name: travis-ci +repo_token: swd8oweVDiONheCgxzP0lxarI45ODDJxd diff --git a/.travis.yml b/.travis.yml index b27e134..3d980f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,5 +15,5 @@ env: sudo: required before_script: npm i script: - -npm test - -npm lint + -npm run test + -npm run lint diff --git a/package.json b/package.json index 63a0b49..6ee1714 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "scripts": { "test": "DEBUG='cfgram*' mocha", + "coveralls": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage", "start": "DEBUG='cfgram*' node server.js" }, "repository": { @@ -38,7 +39,10 @@ "multer": "^1.3.0" }, "devDependencies": { + "aws-sdk-mock": "^1.6.1", "chai": "^3.5.0", + "coveralls": "^2.12.0", + "istanbul": "^0.4.5", "mocha": "^3.2.0", "superagent": "^3.5.0" }