Skip to content
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 .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"
}
136 changes: 136 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Created by https://www.gitignore.io/api/osx,vim,node,macos,windows

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

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

# Icon must end with two \r

# Thumbnails

# Files that might appear in the root of a volume

# Directories potentially created on remote AFP share

### Vim ###
# swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags

### 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,vim,node,macos,windows
20 changes: 20 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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
after_success: npm run coveralls
script:
- npm test
- npm run lint
80 changes: 44 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![CF](https://camo.githubusercontent.com/70edab54bba80edb7493cad3135e9606781cbb6b/687474703a2f2f692e696d6775722e636f6d2f377635415363382e706e67) 18: Image Uploads w/ AWS S3
![CF](https://camo.githubusercontent.com/70edab54bba80edb7493cad3135e9606781cbb6b/687474703a2f2f692e696d6775722e636f6d2f377635415363382e706e67) 16: Basic Auth
===

## Submission Instructions
Expand All @@ -10,46 +10,54 @@
* write a question and observation on canvas

## Learning Objectives
* students will be able to upload static assets to AWS S3
* students will be able to retrieve a cdn url that contains the previously uploaded static asset
* students will be able to work with secret and public access keys
* students will be able to create basic authorization middleware
* students will be able to test basic authorization for signup/signin routes

## Requirements
#### Configuration
* `package.json`
* `.eslintrc`
* `.gitignore`
* `.env`
* `README.md`

#### Description
* create an AWS account
* create an AWS Access Key and Secret
* add the Access Key and Secret to your `.env` file
* create a new model that represents a file type that you want to store on AWS S3
* ex: `.mp3`, `.mp4`, `.png`, etc
* create a test that uploads one of these files to your route
* use the `aws-sdk` to assist with uploading
* use `multer` to parse the file upload request

#### Server Endpoint
* `POST` - `/api/resource/:resourceID/new-resource`

#### Tests
* `POST` - **200** - test that the upload worked and a resource object is returned

#### Bonus
* `DELETE` route - `/api/resource/:resourceID/new-resource/:new-resourceID`
* Test: `DELETE` - **204** - test to ensure the object was deleted from s3

#### Bonus: 3pts
* try using the `deleteObject` method provided by the `aws-sdk` to delete an object *(file)* from S3
* you will need to pass in a `params` object that contains the associated Bucket and AWS object key in order to delete the object from s3
* ex:
``` javascript
var params = {
Bucket: 's3-bucket-name',
Key: 'object-filename'
}
s3.deleteObject(params)
```
* don't forget to remove the resource from the DB
## Feature Tasks
* create the following directories to organize your code:
* **lib**
* **model**
* **route**
* **test**
* create an HTTP server using `express`
* using `mongoose`, create a **User** model with the following properties and options:
* `username` - *required and unique*
* `email` - *required and unique*
* `password` - *required - this must be hashed and can not stored as plain text*
* `findHash` - *unique*
* use the **npm** `debug` module to log function calls that are used within your application
* use the **express** `Router` to create a custom router for allowing users to **sign up** and **sign in**
* use the **npm** `dotenv` module to house the following environment variables:
* `PORT`
* `MONGODB_URI`
* `APP_SECRET` *(used for signing and verify tokens)*

## Server Endpoints
### `/api/signup`
* `POST` request
* the client should pass the username and password in the body of the request
* the server should respond with a token (generated using `jwt` and `findHash`
* the server should respond with **400 Bad Request** to a failed request

### `/api/signin`
* `GET` request
* the client should pass the username and password to the server using a `Basic:` authorization header
* the server should respond with a token for authenticated users
* the server should respond with **401 Unauthorized** for non-authenticated users

## Tests
* create a test that will ensure that your API returns a status code of **404** for any routes that have not been registered
* `/api/signup`
* `POST` - test **400**, if no request body has been provided or the body is invalid
* `POST` - test **200**, if the request body has been provided and is valid
* `/api/signin`
* `GET` - test **401**, if the user could not be authenticated
* `GET` - test **200**, responds with token for a request with a valid basic authorization header
36 changes: 36 additions & 0 deletions lib/basic-auth-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'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();
};
34 changes: 34 additions & 0 deletions lib/bearer-auth-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'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');

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

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

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));
});
});
};
27 changes: 27 additions & 0 deletions lib/error-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'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('message:', err.message);
console.error('name:', err.name);

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(500, err.message);
res.status(err.status).send(err.name);
};
16 changes: 16 additions & 0 deletions model/pic.js
Original file line number Diff line number Diff line change
@@ -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 },
superheroID: { type: Schema.Types.ObjectId, require: 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);
Loading