Initial users

This commit is contained in:
Ritchie Martori 2013-07-02 16:51:38 -07:00
parent c14ef9af8c
commit 2f13c53161
7 changed files with 194 additions and 12 deletions

View File

@ -55,9 +55,7 @@ app.model = function (Model) {
this._models.push(Model); this._models.push(Model);
Model.shared = true; Model.shared = true;
Model.app = this; Model.app = this;
if(Model._remoteHooks) { Model.emit('attached', this);
Model._remoteHooks.emit('attached', app);
}
} }
/** /**

View File

@ -216,8 +216,8 @@ asteroid.createModel = function (name, properties, options) {
}); });
} else { } else {
var args = arguments; var args = arguments;
this._remoteHooks.once('attached', function () { this.once('attached', function () {
self.beforeRemote.apply(ModelCtor, args); self.beforeRemote.apply(self, args);
}); });
} }
} }
@ -232,15 +232,12 @@ asteroid.createModel = function (name, properties, options) {
}); });
} else { } else {
var args = arguments; var args = arguments;
this._remoteHooks.once('attached', function () { this.once('attached', function () {
self.afterRemote.apply(ModelCtor, args); self.afterRemote.apply(ModelCtor, args);
}); });
} }
} }
// allow hooks to be added before attaching to an app
ModelCtor._remoteHooks = new EventEmitter();
return ModelCtor; return ModelCtor;
} }
@ -266,3 +263,4 @@ asteroid.remoteMethod = function (fn, options) {
asteroid.Model = asteroid.createModel('model'); asteroid.Model = asteroid.createModel('model');
asteroid.User = require('./models/user'); asteroid.User = require('./models/user');
asteroid.Session = require('./models/session');

34
lib/middleware/auth.js Normal file
View File

@ -0,0 +1,34 @@
/**
* Module dependencies.
*/
var asteroid = require('../asteroid')
, passport = require('passport');
/**
* Export the middleware.
*/
module.exports = auth;
/**
* Build a temp app for mounting resources.
*/
function auth() {
return function (req, res, next) {
var sub = asteroid();
// TODO clean this up
sub._models = req.app._models;
sub._remotes = req.app._remotes;
sub.use(asteroid.session({secret: 'change me'}))
sub.use(passport.initialize());
sub.use(passport.session());
sub.handle(req, res, next);
}
}

22
lib/models/session.js Normal file
View File

@ -0,0 +1,22 @@
/**
* Module Dependencies.
*/
var Model = require('../asteroid').Model
, asteroid = require('../asteroid');
/**
* Default Session properties.
*/
var properties = {
id: {type: String, required: true},
uid: {type: String},
ttl: {type: Number, ttl: true}
};
/**
* Extends from the built in `asteroid.Model` type.
*/
var Session = module.exports = Model.extend('session', properties);

View File

@ -2,7 +2,10 @@
* Module Dependencies. * Module Dependencies.
*/ */
var Model = require('../asteroid').Model; var Model = require('../asteroid').Model
, asteroid = require('../asteroid')
, passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
/** /**
* Default User properties. * Default User properties.
@ -12,7 +15,7 @@ var properties = {
id: {type: String, required: true}, id: {type: String, required: true},
realm: {type: String}, realm: {type: String},
username: {type: String, required: true}, username: {type: String, required: true},
// password: {type: String, transient: true}, // Transient property password: {type: String, transient: true}, // Transient property
hash: {type: String}, // Hash code calculated from sha256(realm, username, password, salt, macKey) hash: {type: String}, // Hash code calculated from sha256(realm, username, password, salt, macKey)
salt: {type: String}, salt: {type: String},
macKey: {type: String}, // HMAC to calculate the hash code macKey: {type: String}, // HMAC to calculate the hash code
@ -42,3 +45,89 @@ var properties = {
var User = module.exports = Model.extend('user', properties); var User = module.exports = Model.extend('user', properties);
/**
* Login a user by with the given `credentials`.
*
* User.login({username: 'foo', password: 'bar'}, function (err, session) {
* console.log(session.id);
* });
*
* @param {Object} credentials
*/
User.login = function (credentials, fn) {
var UserCtor = this;
this.findOne({username: credentials.username}, function(err, user) {
var defaultError = new Error('login failed');
if(err) {
fn(defaultError);
} else if(user) {
user.hasPassword(credentials.password, function(err, isMatch) {
if(err) {
fn(defaultError);
} else if(isMatch) {
createSession(user, fn);
} else {
fn(defaultError);
}
});
} else {
fn(defaultError);
}
});
function createSession(user, fn) {
var Session = UserCtor.settings.session || asteroid.Session;
Session.create({uid: user.id}, function (err, session) {
if(err) {
fn(err);
} else {
fn(null, session)
}
});
}
}
/**
* Compare the given `password` with the users hashed password.
*
* @param {String} password The plain text password
* @returns {Boolean}
*/
User.prototype.hasPassword = function (plain, fn) {
// TODO - bcrypt
fn(null, this.password === plain);
}
/**
* Override the extend method to setup any extended user models.
*/
User.extend = function () {
var EUser = Model.extend.apply(User, arguments);
setup(EUser);
return EUser;
}
function setup(UserModel) {
asteroid.remoteMethod(
UserModel.login,
{
accepts: [
{arg: 'credentials', type: 'object', required: true, http: {source: 'body'}}
],
returns: {arg: 'session', type: 'object'},
http: {verb: 'post'}
}
);
return UserModel;
}
setup(User);

View File

@ -10,7 +10,10 @@
"express": "~3.1.1", "express": "~3.1.1",
"jugglingdb": "git+ssh://git@github.com:strongloop/jugglingdb.git", "jugglingdb": "git+ssh://git@github.com:strongloop/jugglingdb.git",
"sl-remoting": "git+ssh://git@github.com:strongloop/sl-remoting.git", "sl-remoting": "git+ssh://git@github.com:strongloop/sl-remoting.git",
"inflection": "~1.2.5" "inflection": "~1.2.5",
"bcrypt": "~0.7.6",
"passport": "~0.1.17",
"passport-local": "~0.1.6"
}, },
"devDependencies": { "devDependencies": {
"mocha": "latest", "mocha": "latest",

38
test/user.test.js Normal file
View File

@ -0,0 +1,38 @@
var User = asteroid.User;
var passport = require('passport');
describe('User', function(){
beforeEach(function (done) {
var memory = asteroid.createDataSource({
connector: asteroid.Memory
});
asteroid.User.attachTo(memory);
asteroid.Session.attachTo(memory);
app.use(asteroid.cookieParser());
app.use(asteroid.auth());
app.use(asteroid.rest());
app.model(asteroid.User);
asteroid.User.create({email: 'foo@bar.com', password: 'bar'}, done);
});
describe('User.login', function(){
it('Login a user by providing credentials.', function(done) {
request(app)
.post('/users/login')
.expect('Content-Type', /json/)
.expect(200)
.send({email: 'foo@bar.com', password: 'bar'})
.end(function(err, res){
if(err) return done(err);
var session = res.body;
assert(session.uid);
assert(session.id);
done();
});
});
});
});