WebFund 2013W Lecture 23
The final exam will be similar to the midterm. To prepare, make sure you understand the content on the midterm and understand the code and questions below.
Midterm
The midterm is here (without answers).
Audio
The audio from the lecture given on April 9, 2013 is here.
Code
C1: demo-auth-hash/app.js
/**
* Module dependencies.
*/
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, bcrypt = require("bcrypt") //hashing algorithm
, MongoStore = require('connect-mongo')(express) //session datastore using mongodb
, mongoose = require('mongoose') //blessed mongodb connector
, User; //User class defined below
//connect to the "users" database
mongoose.connect('mongodb://localhost/users');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
//once the DB connection is open...
db.once('open', function callback () {
//Create a mongoose Schema (document structure)
var userSchema = mongoose.Schema({
username: String,
password: String
});
//Convert this schema into an instantiable "model" Class
User = mongoose.model("User", userSchema);
});
var app = express();
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
//enable cookies
app.use(express.cookieParser());
//setup session management
app.use(express.session({
cookie: {maxAge: 60000 * 20} // 20 minutes
, secret: "Shh... I'm a secret"
, store: new MongoStore({ //use a mongo-connect store
db: "sessions"
})
}));
app.use(app.router);
app.use(require('less-middleware')({ src: __dirname + '/public' }));
app.use(express.static(path.join(__dirname, 'public')));
});
app.configure('development', function(){
app.use(express.errorHandler());
});
app.get('/', function(req, res, next){
//redirect to user page if logged in
if(req.session.username){
res.redirect("/users");
}else{
next();
}
}, routes.index);
app.get('/users', function(req, res, next){
//redirect home if not logged in
if(req.session.username){
next();
}else{
res.redirect("/");
}
}, user.list);
app.post("/register", function(req, res){
var username = req.body.username;
var password = req.body.password;
User.find({username: username}, function(err, users){
//check if the user already exists
if(users.length!=0){
res.redirect("/?error=user already exists");
return;
}
//generate a salt, with 10 rounds (2^10 iterations)
bcrypt.genSalt(10, function(err, salt) {
//hash the given password using the salt we generated
bcrypt.hash(password, salt, function(err, hash) {
//create a new instance of the mongoose User model we defined above
var newUser = new User({
username: username,
password: hash
});
//save() is a magic function from mongoose that saves this user to our DB
newUser.save(function(err, newUser){
res.send("successfully registered user: "+newUser.username);
});
});
});
});
});
app.post("/login", function(req, res){
var username = req.body.username;
var password = req.body.password;
//Search the Database for a User with the given username
User.find({username: username}, function(err, users){
//we couldn't find a user with that name
if(err || users.length==0){
res.redirect("/?error=invalid username or password");
return;
}
var user = users[0];
//compare the hash we have for the user with what this password hashes to
bcrypt.compare(password, user.password, function(err, authenticated){
if(authenticated){
req.session.username = user.username;
res.redirect("/users");
}else{
res.redirect("/?error=invalid username or password");
}
});
});
});
app.post("/logout", function(req, res){
req.session.destroy(function(err){
if(err){
console.log("Error: %s", err);
}
res.redirect("/");
});
});
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
C2: demo-auth-hash/views/layout.jade
doctype 5
html
head
title= title
script(src='/libs/jquery/jquery.min.js')
script(src='/src/home.js')
link(rel='stylesheet', href='/libs/bootstrap/css/bootstrap.min.css')
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content
C3: demo-auth-hash/views/index.jade
extends layout
block content
h1= title
p Welcome to #{title}
- if(error)
div.alert-error #{error}
p Please log in
div
form(action="/login", method="post")
div.control-group.input-append
input(type="text", name="username")
label.add-on(for="username") Username
div.control-group.input-append
input(type="password", name="password")
label.add-on(for="password") Password
button(type="submit") Login
button#register(type="button") Register
C4: demo-auth-hash/routes/index.js
/*
* GET home page.
*/
exports.index = function(req, res, next){
res.render('index', { title: 'CoolAppify.org.ly', error: req.query.error });
}
C5: blog-updated/articleprovider-mongodb.js
var Db = require('mongodb').Db;
var Connection = require('mongodb').Connection;
var Server = require('mongodb').Server;
var BSON = require('mongodb').BSON;
var ObjectID = require('mongodb').ObjectID;
ArticleProvider = function(host, port) {
this.db= new Db('node-mongo-blog', new Server(host, port, {auto_reconnect: true}), {journal: true});
this.db.open(function(){});
};
//addCommentToArticle
ArticleProvider.prototype.addCommentToArticle = function(articleId, comment, callback) {
this.getCollection(function(error, article_collection) {
if( error ) callback( error );
else {
article_collection.update(
{_id: article_collection.db.bson_serializer.ObjectID.createFromHexString(articleId)},
{"$push": {comments: comment}},
function(error, article){
if( error ) callback(error);
else callback(null, article)
});
}
});
};
//getCollection
ArticleProvider.prototype.getCollection= function(callback) {
this.db.collection('articles', function(error, article_collection) {
if( error ) callback(error);
else callback(null, article_collection);
});
};
//findAll
ArticleProvider.prototype.findAll = function(callback) {
this.getCollection(function(error, article_collection) {
if( error ) callback(error)
else {
article_collection.find().toArray(function(error, results) {
if( error ) callback(error)
else callback(null, results)
});
}
});
};
//findById
ArticleProvider.prototype.findById = function(id, callback) {
this.getCollection(function(error, article_collection) {
if( error ) callback(error)
else {
article_collection.findOne({_id: article_collection.db.bson_serializer.ObjectID.createFromHexString(id)}, function(error, result) {
if( error ) callback(error)
else callback(null, result)
});
}
});
};
//save
ArticleProvider.prototype.save = function(articles, callback) {
this.getCollection(function(error, article_collection) {
if( error ) callback(error)
else {
if( typeof(articles.length)=="undefined")
articles = [articles];
for( var i =0;i< articles.length;i++ ) {
article = articles[i];
article.created_at = new Date();
if( article.comments === undefined ) article.comments = [];
for(var j =0;j< article.comments.length; j++) {
article.comments[j].created_at = new Date();
}
}
article_collection.insert(articles, function() {
callback(null, articles);
});
}
});
};
exports.ArticleProvider = ArticleProvider;