Skip to content

Day three Zachary #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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:
- cd lab-zachary
- npm i
script:
- npm test
- npm run lint
21 changes: 21 additions & 0 deletions lab-zachary/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"rules": {
"no-console": "off",
"indent": [ "error", 2 ],
"quotes": [ "error", "single" ],
"semi": ["error", "always"],
"linebreak-style": [ "error", "unix" ]
},
"env": {
"es6": true,
"node": true,
"mocha": true,
"jasmine": true
},
"ecmaFeatures": {
"modules": true,
"experimentalObjectRestSpread": true,
"impliedStrict": true
},
"extends": "eslint:recommended"
}
130 changes: 130 additions & 0 deletions lab-zachary/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@

# Created by https://www.gitignore.io/api/node,osx,windows,linux

### 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*

### 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


### 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
.DS_Store

### 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/node,osx,windows,linux
73 changes: 73 additions & 0 deletions lab-zachary/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Basic and Bearer Auth with an Express API

This app implements Basic and Bearer Auth with API endpoints to allow for user signup and sign in, and to create, read, update and delete galleries associated with that user.

# System Requirements

- Terminal.app on macOS or equivalent
- node.js and npm package manager installed


### Installation

Clone the repository to your local server
```sh
https://github.com/zcrumbo/16-basic_auth/tree/day-one
```

Install the dependencies -

```sh
$ npm i
```

[HTTPie](https://httpie.org/) will be required to run the HTTP requests from your terminal window. You will need to install this with [Homebrew][1] on macOS. It is also easier to see the results of all operations by running mocha tests with the command
```sh
$ mocha
```
or use the npm script to run all tests with debug
```sh
$ npm test
```
Start the server with debug

```sh
$ npm start
```
If you want to use the debug and nodemon modules, run the npm script:
```
npm start
```

### Connecting

Commands can be sent to the api/signin and the api/signup endpoints. Once signed up, commands can be sent with the token generated during signup to api/gallery and api/gallery/:id endpoints for full CRUD functionality


```sh
$ POST /api/signup/ with JSON body {username='testname' password='password' email='email@email.com'} #signs up for the api and returns a unqique token that must be used in future api calls

$ GET /api/signin with basic auth header username:password #signs into the API

$ POST /api/gallery with token #creates a new gallery

$ GET /api/gallery/:galleryID with token #retrieve your gallery

$ PUT /api/gallery/:galleryID with token and JSON body {name:'galleryName', desc: 'description'} #updates specified gallery

$ DELETE /api/gallery/:galleryID with token #deletes specified gallery

```

Sending the following requests to the server will have the results below:

* `404` response with 'not found' for unregistered endpoints and nonexistent gallery IDs
* `401` response with 'unauthorized' for bad credentials
* `200` response with a proper signin and gallery creation
* `400` response with 'bad request' if no request body was provided or the body was invalid
* `200` response with the body content for requests with valid bodies, endpoints and ids
* `204` response for successful deletions


[1]:https://brew.sh/

23 changes: 23 additions & 0 deletions lab-zachary/gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

const gulp = require('gulp');
const eslint = require('gulp-eslint');
const mocha = require ('gulp-mocha');

gulp.task('test', function(){
gulp.src('./test/*-test.js', { read: false})
.pipe(mocha({ reporter: 'spec'}));
});

gulp.task('lint', function() {
return gulp.src(['**/*.js', '!node_modules'])
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError());
});

gulp.task('dev', function(){
gulp.watch(['**/*.js', '!node_modules/**'], ['lint']);
});

gulp.task('default', ['dev', 'lint', 'test']);
29 changes: 29 additions & 0 deletions lab-zachary/lib/basic-auth-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'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, 'auth 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(); //one method to decode auth header
//var utf8str = base64str.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();
};
32 changes: 32 additions & 0 deletions lab-zachary/lib/bearer-auth-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

const createError = require('http-errors');
const jwt = require('jsonwebtoken');
const debug = require('debug')('cfgram:bearer-auth-middleware');
const User = require('../model/user.js');

//get req, res, next from other middleware and decipher the auth token in the auth header, then attach it to the request body

module.exports = function( req, res, next) {
debug('bearer auth');

var authHeader = req.headers.authorization;
if (!authHeader) return next(createError(401, 'Authorization headers required'));

var token = authHeader.split('Bearer ')[1];
if (!token) return next(createError(401, 'token required'));

//validate token and use jwt verify user

jwt.verify(token, process.env.APP_SECRET, function(err, decoded) {
if (err) return next(createError(401, 'token validation error'));
if (!decoded) return next(createError(401, 'invalid token'));
User.findOne({findHash: decoded.token})
.then( user => {
req.user = user;
next();
})
.catch( err => next(createError(401, err.message)));
});
};

32 changes: 32 additions & 0 deletions lab-zachary/lib/error-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

const createError = require('http-errors');
const debug = require('debug')('cfgram:error-middleware');

module.exports = function(err, req, res, next){
debug('error-middleware');

if(err.status) {
debug('user error');

console.error('message:', err.message);
console.error('name:', err.name);

res.status(err.status).send(err.message);
next();
return;
}

if (err.message === 'ValidationError'){
debug('Mongoose Error');
err = createError(400, err.message);
res.status(err.status).send(err.message);
next();
return;
}

err = createError(500, 'Internal Server Error');
res.status(err.status).send(err.message);
next();
return;
};
28 changes: 28 additions & 0 deletions lab-zachary/lib/s3-methods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

const AWS = require('aws-sdk');
const debug = require('debug')('cfgram:s3-methods');
const s3 = new AWS.S3();

AWS.config.setPromisesDependency(require('bluebird'));

module.exports = exports = {};

exports.uploadObjectProm = function(params) {
return new Promise((resolve, reject) => {
s3.upload(params, (err, data) => {
if (err) console.error(reject);
resolve(data);
});
});
};

exports.deleteObjectProm = function(params) {
debug('deleteS3Prom') ;
return new Promise((resolve, reject) => {
s3.deleteObject(params, function(err, data) {
if (reject) console.error(err);
resolve(data);
});
});
};
Loading