xo-server/src/xo.js
2013-07-10 18:11:10 +02:00

168 lines
3.5 KiB
JavaScript

var crypto = require('crypto');
var hashy = require('hashy');
var Q = require('q');
var Collection = require('./collection');
var Model = require('./model');
//////////////////////////////////////////////////////////////////////
var check = function () {
var errors;
var validator = new (require('validator').Validator)();
validator.error = function (err) {
(errors || (errors = [])).push(err);
return this;
};
var check = function (data) {
validator.check(data);
};
check.pop = function () {
var res = errors;
errors = undefined;
return res;
};
return check;
}();
//////////////////////////////////////////////////////////////////////
// Models
//////////////////////////////////////////////////////////////////////
// @todo We could also give a permission level to tokens (<=
// user.permission).
var Token = Model.extend({
// Validates model attributes.
// 'validate': function (attr) {
// check(attr.id).len(10);
// check(attr.user_id).isInt();
// return check.pop();
// },
}, {
'generate': function (user_id) {
return Q.ninvoke(crypto, 'randomBytes', 32).then(function (buf) {
return new Token({
'id': buf.toString('base64'),
'user_id': user_id,
});
});
},
});
var User = Model.extend({
'default': {
'permission': 'none',
},
// Validates model attributes.
// 'validate': function (attr) {
// check(attr.id).isInt();
// check(attr.email).isEmail();
// check(attr.pw_hash).len(40);
// check(attr.permission).isIn('none', 'read', 'write', 'admin');
// return check.pop();
// },
'setPassword': function (password) {
var self = this;
return hashy.hash(password).then(function (hash) {
self.set('pw_hash', hash);
});
},
// Checks and updates the hash if necessary.
'checkPassword': function (password) {
var hash = this.get('pw_hash');
var user = this;
return hashy.verify(password, hash).then(function (success) {
if (!success)
{
return false;
}
if (hashy.needsRehash(hash))
{
return user.setPassword(password).then(true);
}
return true;
});
},
'hasPermission': function (permission) {
var perms = {
'none': 0,
'read': 1,
'write': 2,
'admin': 3,
};
return (perms[this.get('permission')] >= perms[permission]);
},
});
var Server = Model.extend({
'validate': function () {
},
});
//////////////////////////////////////////////////////////////////////
// Collections
//////////////////////////////////////////////////////////////////////
var Tokens = Collection.extend({
'model': Token,
'generate': function (user_id) {
var self = this;
return Token.generate(user_id).then(function (token) {
return self.add(token);
});
}
});
var Users = Collection.extend({
'model': User,
});
var Servers = Collection.extend({
'model': Server,
});
//////////////////////////////////////////////////////////////////////
function Xo()
{
this.servers = new Servers();
this.tokens = new Tokens();
this.users = new Users();
this.users.add({
'email': 'bob@gmail.com',
'pw_hash': '$2a$10$PsSOXflmnNMEOd0I5ohJQ.cLty0R29koYydD0FBKO9Rb7.jvCelZq',
}).done();
// This events are used to automatically close connections if the
// associated credentials are invalidated.
this.tokens.on('remove', function (token_ids) {
token_ids.each(function (token_id) {
this.emit('token.revoked:'+ token_id);
});
});
this.users.on('remove', function (user_ids) {
user_ids.each(function (user_id) {
this.emit('user.revoked:'+ user_id);
});
});
}
require('util').inherits(Xo, require('events').EventEmitter);
module.exports = function () {
return new Xo();
};