mirror of
https://github.com/zhigang1992/deployd.git
synced 2026-05-25 16:11:26 +08:00
Merge branch 'master' of github.com:Deployd/Deployd
This commit is contained in:
21
lib/app.js
21
lib/app.js
@@ -32,6 +32,27 @@ app.configure(function(){
|
||||
app.use(express.static(__dirname + '/../public'));
|
||||
});
|
||||
|
||||
app.get('/routes', function(req, res) {
|
||||
var routes = []
|
||||
, format = ' - '
|
||||
, method
|
||||
, supported = {GET:1, POST:1, DELETE:1, PUT:1}
|
||||
;
|
||||
|
||||
app.routes.all().forEach(function(route){
|
||||
method = route.method.toUpperCase();
|
||||
if(supported[method]) {
|
||||
routes.push(
|
||||
method
|
||||
+ format.substr(method.length, format.length)
|
||||
+ route.path
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
res.send(routes);
|
||||
});
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
res.send({home: true});
|
||||
});
|
||||
|
||||
11
lib/db.js
11
lib/db.js
@@ -29,7 +29,12 @@ function collection(db, model, fn) {
|
||||
module.exports = {
|
||||
|
||||
find: ready(function(model) {
|
||||
var query = model.toQuery() || {};
|
||||
var query = model.toQuery() || {}
|
||||
, id = model.get('_id')
|
||||
, _id = id && ObjectID(id)
|
||||
;
|
||||
|
||||
if(_id) query._id = _id;
|
||||
|
||||
collection(db, model, function(err, collection) {
|
||||
// TODO limit 1 for models, allow all for collections
|
||||
@@ -44,11 +49,15 @@ module.exports = {
|
||||
var query = model.toQuery()
|
||||
, changes = model.attributes
|
||||
, options = {safe: true, upsert: true}
|
||||
, id = model.get('_id')
|
||||
, _id = id && ObjectID(id)
|
||||
, callback = function(err, result) {
|
||||
model.refresh(changes);
|
||||
}
|
||||
;
|
||||
|
||||
if(_id && query) query._id = _id;
|
||||
|
||||
collection(db, model, function(err, collection) {
|
||||
if(query) {
|
||||
collection.update(query, changes, options, callback);
|
||||
|
||||
127
lib/model.js
127
lib/model.js
@@ -67,25 +67,37 @@ Model = module.exports = emitter.spawn({
|
||||
},
|
||||
|
||||
isNew: function() {
|
||||
return !this._id;
|
||||
return !this.get('_id');
|
||||
},
|
||||
|
||||
isReady: function() {
|
||||
return this.state === states.ready;
|
||||
},
|
||||
|
||||
save: function() {
|
||||
this.sync(states.write);
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
fetch: function() {
|
||||
this.sync(states.read);
|
||||
return this;
|
||||
},
|
||||
|
||||
save: function() {
|
||||
var model = this
|
||||
, state = states.write
|
||||
;
|
||||
|
||||
model.isAllowed(state, function() {
|
||||
model.sync(state);
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
this.sync(states.remove);
|
||||
var model = this
|
||||
, state = states.remove
|
||||
;
|
||||
|
||||
model.isAllowed(state, function() {
|
||||
model.sync(state);
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
@@ -104,10 +116,14 @@ Model = module.exports = emitter.spawn({
|
||||
model.attributes = {};
|
||||
model.set(changes || model.attributes);
|
||||
|
||||
model.isAllowed(model.state, function() {
|
||||
if(model.state === states.read) {
|
||||
model.isAllowed(model.state, function() {
|
||||
model.sync(states.ready);
|
||||
});
|
||||
} else {
|
||||
model.sync(states.ready);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return model;
|
||||
},
|
||||
|
||||
@@ -117,41 +133,74 @@ Model = module.exports = emitter.spawn({
|
||||
read: 'root',
|
||||
write: 'root',
|
||||
remove: 'root',
|
||||
create: 'root',
|
||||
special: {
|
||||
_id: 'root'
|
||||
_id: {read: 'root', write: 'root'}
|
||||
}
|
||||
},
|
||||
|
||||
isAllowed: function(action, fn) {
|
||||
if(action === 'write' && this.isNew()) action = 'create';
|
||||
|
||||
var permissions = this.allowed
|
||||
, special = permissions.special
|
||||
, rights = permissions[action]
|
||||
, requiresUser = rights === 'user'
|
||||
, actor = this.actor()
|
||||
, allowed = true
|
||||
, model = this
|
||||
;
|
||||
|
||||
if(requiresUser && !actor) {
|
||||
model.error('Only logged in users can ' + action);
|
||||
fn();
|
||||
return;
|
||||
}
|
||||
|
||||
if(permissions && rights && rights !== 'public') {
|
||||
|
||||
if(rights === 'creator') {
|
||||
if(actor !== this.get('creator') && actor === this.get('_id')) {
|
||||
model.error('The current user must be the creator to ' + action, 'Not Allowed');
|
||||
}
|
||||
} else {
|
||||
// check permission against actor
|
||||
Model
|
||||
.spawn({collection: 'users', allowed: false})
|
||||
.set({_id: actor})
|
||||
.notify(function(json) {
|
||||
allowed = !!(json.groups && json.groups[rights]);
|
||||
if(!allowed)
|
||||
model.error('The current user does not have permissions to ' + action, 'Not Allowed');
|
||||
fn();
|
||||
})
|
||||
.fetch()
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
// check permission against actor
|
||||
Model
|
||||
.spawn({collection: 'users', allowed: false})
|
||||
.set({_id: actor})
|
||||
.notify(function(json) {
|
||||
// TODO cache groups on the req
|
||||
var groups = json.groups
|
||||
, root = groups && groups.root
|
||||
, isCreator = actor === this.get('creator') || this.get('_id')
|
||||
, requiresCreator = rights === 'creator' && !model.isNew() && rights === 'creator'
|
||||
, allowed = root || (groups && groups[rights]) || (requiresCreator && isCreator)
|
||||
;
|
||||
|
||||
if(special) {
|
||||
Object.getOwnPropertyNames(special).forEach(function(key) {
|
||||
var perms = special[key]
|
||||
, right = perms[action]
|
||||
, allowed = right === 'public' || (groups && groups[right]) || root
|
||||
;
|
||||
|
||||
if(!allowed) {
|
||||
if(action === 'read') {
|
||||
// TODO build select object where {key: 0}
|
||||
delete model.attributes[key];
|
||||
} else {
|
||||
model.error('The current user cannot ' + action + ' the key: ', key, 'Not Allowed');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(requiresCreator && !isCreator) {
|
||||
model.error('The current user must be the creator to ' + action, 'Not Allowed');
|
||||
}
|
||||
|
||||
if(!allowed)
|
||||
model.error('The current user does not have permissions to ' + action, 'Not Allowed');
|
||||
fn();
|
||||
})
|
||||
.fetch()
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// default to responding
|
||||
@@ -214,7 +263,8 @@ Model = module.exports = emitter.spawn({
|
||||
if(_self.attributes[p] != changes[p]) {
|
||||
_self.attributes[p] = changes[p];
|
||||
|
||||
if(p !== '_id') {
|
||||
// TODO make '$' inspection less coupled and more secure
|
||||
if(p !== '_id' && p.substr(0,1) != '$') {
|
||||
_self.isValid(p, changes[p]);
|
||||
}
|
||||
|
||||
@@ -348,11 +398,18 @@ Model = module.exports = emitter.spawn({
|
||||
|
||||
});
|
||||
|
||||
var spawn = module.exports.spawn;
|
||||
var spawn = module.exports.spawn
|
||||
, _models = {}
|
||||
;
|
||||
|
||||
module.exports.refreshSettings = function(collection) {
|
||||
_models[collection].updateSettings();
|
||||
}
|
||||
|
||||
module.exports.spawn = function(model) {
|
||||
var instance = spawn.apply(this, arguments);
|
||||
if(model && model.collection) {
|
||||
_models[instance.collection] = instance;
|
||||
instance.updateSettings();
|
||||
}
|
||||
return instance;
|
||||
|
||||
@@ -15,7 +15,8 @@ module.exports = Model.spawn({
|
||||
allowed: {
|
||||
read: 'creator',
|
||||
write: 'creator',
|
||||
remove: 'creator'
|
||||
remove: 'creator',
|
||||
create: 'user'
|
||||
},
|
||||
|
||||
save: function() {
|
||||
|
||||
32
lib/plugins/graph/index.js
Normal file
32
lib/plugins/graph/index.js
Normal file
@@ -0,0 +1,32 @@
|
||||
var Settings = require('../settings/settings')
|
||||
, Setting = require('../settings/settings')
|
||||
, Model = require('../../model')
|
||||
;
|
||||
|
||||
var models = module.exports.models = {};
|
||||
var refresh = module.exports.refresh = function() {
|
||||
Settings
|
||||
.spawn()
|
||||
.find({plugin: 'graph'})
|
||||
.notify(function(model) {
|
||||
var m = models[model.name] = Model.spawn();
|
||||
m.description = model.description;
|
||||
m.allowed = model.allowed;
|
||||
m.collection = model.collection;
|
||||
m.plugin = model.plugin;
|
||||
})
|
||||
.fetch()
|
||||
;
|
||||
}
|
||||
|
||||
// initial refresh
|
||||
refresh();
|
||||
|
||||
// create or update general graph settings
|
||||
Setting
|
||||
.spawn()
|
||||
.set({plugin: 'graph', name: 'graph'})
|
||||
.save()
|
||||
;
|
||||
|
||||
|
||||
@@ -2,17 +2,10 @@ var app = require('../../app')
|
||||
, config = require('../../config').load()
|
||||
, Settings = require('./settings')
|
||||
, Setting = require('./setting')
|
||||
, graph = require('../graph')
|
||||
, Model = require('../../model')
|
||||
;
|
||||
|
||||
app.post('/setting', function (req, res) {
|
||||
Setting
|
||||
.spawn()
|
||||
.set(req.body)
|
||||
.notify(res)
|
||||
.save()
|
||||
;
|
||||
});
|
||||
|
||||
app.get('/settings', function(req, res) {
|
||||
Settings
|
||||
.spawn()
|
||||
@@ -26,9 +19,13 @@ app.post('/settings', function(req, res) {
|
||||
Settings
|
||||
.spawn()
|
||||
.for(req)
|
||||
.find({name: req.body.name, plugin: req.body.name})
|
||||
.find({name: req.body.name, plugin: req.body.plugin})
|
||||
.set(req.body)
|
||||
.notify(res)
|
||||
.notify(function(json) {
|
||||
if(req.body.plugin === 'graph') graph.refresh();
|
||||
else if(json.collection) Model.refreshSettings(json.collection);
|
||||
res.send(json);
|
||||
})
|
||||
.save()
|
||||
;
|
||||
});
|
||||
@@ -37,7 +34,7 @@ app.get('/settings/:name', function(req, res) {
|
||||
Setting
|
||||
.spawn()
|
||||
.for(req)
|
||||
.find({name: req.param('name')})
|
||||
.find({plugin: req.param('name')})
|
||||
.notify(res)
|
||||
.fetch()
|
||||
;
|
||||
|
||||
34
lib/plugins/user/group.js
Normal file
34
lib/plugins/user/group.js
Normal file
@@ -0,0 +1,34 @@
|
||||
var Model = require('../../model');
|
||||
|
||||
var Group = module.exports = Model.spawn({
|
||||
|
||||
collection: 'groups',
|
||||
plugin: 'user',
|
||||
|
||||
description: {
|
||||
name: {type: 'string', unique: true},
|
||||
creator: 'string'
|
||||
},
|
||||
|
||||
allowed: {
|
||||
read: 'root',
|
||||
write: 'creator',
|
||||
remove: 'creator',
|
||||
create: 'root'
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var defaults = ['root', 'admin', 'public'];
|
||||
|
||||
defaults.forEach(function(group) {
|
||||
var g = {name: group, creator: 'root'};
|
||||
|
||||
Group
|
||||
.spawn()
|
||||
.unlock()
|
||||
.find(g)
|
||||
.set(g)
|
||||
.save()
|
||||
;
|
||||
});
|
||||
@@ -1,6 +1,8 @@
|
||||
var app = require('../../app')
|
||||
, Group = require('./group')
|
||||
, User = require('./user')
|
||||
, Users = require('./users')
|
||||
, ObjectID = require('mongodb').BSON
|
||||
;
|
||||
|
||||
function user(action, params, req, res) {
|
||||
@@ -41,12 +43,31 @@ app.del('/me', function(req, res) {
|
||||
if(u) user('remove', u, req, res);
|
||||
});
|
||||
|
||||
app.get('/user/:email', function(req, res) {
|
||||
user('fetch', {email: req.param('email')}, req, res);
|
||||
app.get('/user/:id', function(req, res) {
|
||||
User
|
||||
.spawn()
|
||||
.for(req)
|
||||
.find({_id: req.param('id')})
|
||||
.notify(res)
|
||||
.fetch()
|
||||
;
|
||||
});
|
||||
|
||||
app.post('/user/:email/group', function(req, res) {
|
||||
user('save', {email: req.param('email'), group: req.param('group')}, req, res);
|
||||
var changes = {}
|
||||
, group = req.body && req.body.group
|
||||
;
|
||||
|
||||
// TODO validate group
|
||||
changes['groups.' + group] = 1;
|
||||
User
|
||||
.spawn()
|
||||
.for(req)
|
||||
.find({email: req.param('email')})
|
||||
.set({$set: changes})
|
||||
.notify(res)
|
||||
.save()
|
||||
;
|
||||
});
|
||||
|
||||
app.get('/users', function(req, res) {
|
||||
|
||||
@@ -15,13 +15,18 @@ module.exports = Model.spawn({
|
||||
name: 'string',
|
||||
auth: 'string',
|
||||
removed: 'boolean',
|
||||
group: 'string'
|
||||
groups: 'object'
|
||||
},
|
||||
|
||||
allowed: {
|
||||
read: 'creator',
|
||||
read: 'public',
|
||||
write: 'creator',
|
||||
remove: 'creator'
|
||||
remove: 'creator',
|
||||
create: 'public',
|
||||
special: {
|
||||
groups: {read: 'public', write: 'root'},
|
||||
email: {read: 'creator'}
|
||||
}
|
||||
},
|
||||
|
||||
toJSON: function() {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"main": "index.js",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
},
|
||||
},
|
||||
"dependencies": {
|
||||
"express": ">= 2.0.0",
|
||||
"underscore": ">= 1.0.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>JSON-RPC Demo for JQuery Terminal Emulator</title>
|
||||
<title>Deployd REPL</title>
|
||||
<meta name="author" content="Jakub Jankiewicz - jcubic@onet.pl"/>
|
||||
<meta name="Description" content="Demonstration for JQuery Terminal Emulator using call automaticly JSON-RPC service (in php) with authentication."/>
|
||||
<link rel="sitemap" type="application/xml" title="Sitemap" href=""/>
|
||||
@@ -23,31 +23,58 @@ jQuery(document).ready(function($) {
|
||||
$('body, html').scrollTop($('html').height())
|
||||
}
|
||||
|
||||
function help(argument) {
|
||||
terminal.echo('----------------------------------------------------------------------');
|
||||
terminal.echo('Usage:');
|
||||
terminal.echo("\t settings - show a list of app and plugin settings");
|
||||
terminal.echo("\t routes - show a list of app urls");
|
||||
terminal.echo("\t users - show a list of app users");
|
||||
terminal.echo("\t me - show the currently logged in user");
|
||||
terminal.echo("\t logout - go back to the dashboard");
|
||||
terminal.echo("\t clear - clear the output");
|
||||
terminal.echo("\t help - show this help message");
|
||||
terminal.echo(' ')
|
||||
terminal.echo('Code Examples:');
|
||||
terminal.echo("\t d('/settings')");
|
||||
terminal.echo("\t d('/users')");
|
||||
terminal.echo("\t d('/user', {name: 'Joe', email: 'j@joes.com', password: '1234'})");
|
||||
terminal.echo("\t d('/user/login', {email: 'j@joes.com', password: '1234'})");
|
||||
terminal.echo("\t d('/search/users', {email: 'j@joes.com'})");
|
||||
terminal.echo("\t d('/search/groups', {})");
|
||||
terminal.echo('----------------------------------------------------------------------');
|
||||
}
|
||||
|
||||
var terminal = $('#terminal').terminal(function(command, term) {
|
||||
switch(command) {
|
||||
case 'exit':
|
||||
case 'logout':
|
||||
window.location = '/dashboard';
|
||||
break;
|
||||
case 'help':
|
||||
help();
|
||||
break;
|
||||
case 'me':
|
||||
d('/me');
|
||||
break;
|
||||
case 'routes':
|
||||
d('/routes');
|
||||
break;
|
||||
case 'users':
|
||||
d('/users');
|
||||
break;
|
||||
case 'settings':
|
||||
d('/settings');
|
||||
break;
|
||||
default:
|
||||
eval(command);
|
||||
var result = eval(command);
|
||||
result && terminal.echo(JSON.stringify(result));
|
||||
break;
|
||||
}
|
||||
}, {
|
||||
greetings: 'Deployd REPL',
|
||||
prompt: 'deployd >'
|
||||
prompt: 'deployd $'
|
||||
});
|
||||
|
||||
terminal.echo('----------------------------------------------------------------------');
|
||||
terminal.echo('Examples:');
|
||||
terminal.echo("\t d('/settings') or just 'settings'");
|
||||
terminal.echo("\t d('/users')");
|
||||
terminal.echo("\t d('/user', {name: 'Joe', email: 'j@joes.com', password: '1234'})");
|
||||
terminal.echo("\t d('/user/login', {email: 'j@joes.com', password: '1234'})");
|
||||
terminal.echo("\t d('/search/users', {email: 'j@joes.com'})");
|
||||
terminal.echo('----------------------------------------------------------------------');
|
||||
help();
|
||||
|
||||
jQuery.ajaxSetup({
|
||||
complete: function(res) {
|
||||
|
||||
@@ -33,16 +33,15 @@ var tests = {
|
||||
}
|
||||
},
|
||||
|
||||
'2. find user by id': {
|
||||
route: '/user/' + user.email,
|
||||
'3. add a user to group': {
|
||||
route: '/user/' + user.email + '/group',
|
||||
data: {group: 'root'},
|
||||
expect: {
|
||||
_id: 'toExist',
|
||||
password: 'toNotExist',
|
||||
errors: 'toNotExist'
|
||||
}
|
||||
},
|
||||
|
||||
'3. login a user': {
|
||||
'4. login a user': {
|
||||
route: '/user/login',
|
||||
data: user,
|
||||
expect: {
|
||||
@@ -56,7 +55,7 @@ var tests = {
|
||||
}
|
||||
},
|
||||
|
||||
'4. get current user': {
|
||||
'5. get current user': {
|
||||
route: '/me',
|
||||
expect: {
|
||||
email: user.email,
|
||||
@@ -66,7 +65,7 @@ var tests = {
|
||||
}
|
||||
},
|
||||
|
||||
'5. searching users': {
|
||||
'6. searching users': {
|
||||
route: '/search?type=users&find={"email": "skawful@gmail.com"}',
|
||||
expect: {
|
||||
results: 'toExist',
|
||||
@@ -110,15 +109,7 @@ var tests = {
|
||||
}
|
||||
},
|
||||
|
||||
'10. add a user to group': {
|
||||
route: '/user/' + user.email + '/group',
|
||||
data: {group: 'author'},
|
||||
expect: {
|
||||
group: 'author'
|
||||
}
|
||||
},
|
||||
|
||||
'11. only 1 user per email': {
|
||||
'10. only 1 user per email': {
|
||||
route: '/search/users',
|
||||
data: {email: user.email},
|
||||
expect: {
|
||||
@@ -126,7 +117,7 @@ var tests = {
|
||||
}
|
||||
},
|
||||
|
||||
'12. only 1 app per name': {
|
||||
'11. only 1 app per name': {
|
||||
route: '/search/apps',
|
||||
data: {name: app.name},
|
||||
expect: {
|
||||
@@ -134,20 +125,20 @@ var tests = {
|
||||
}
|
||||
},
|
||||
|
||||
'13. only 1 user': {
|
||||
'12. only 1 user': {
|
||||
route: '/search/users',
|
||||
data: {},
|
||||
expect: {
|
||||
results: 'toContainOne'
|
||||
}
|
||||
},
|
||||
|
||||
'14. delete a user': {
|
||||
route: '/me?method=delete',
|
||||
expect: {
|
||||
errors: 'toNotExist'
|
||||
}
|
||||
}
|
||||
|
||||
// '13. delete a user': {
|
||||
// route: '/me?method=delete',
|
||||
// expect: {
|
||||
// errors: 'toNotExist'
|
||||
// }
|
||||
// }
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user