From 836df227c683fba3d86de19c93c09def021da031 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Thu, 18 Jul 2013 11:44:25 -0700 Subject: [PATCH] Add more functions and tests for Application model --- example/mobile-models/app.js | 2 +- lib/models/acl.js | 37 +++++++++++------ lib/models/application.js | 52 +++++++++++++++++++++--- lib/models/index.js | 7 ++++ test/model.application.test.js | 73 ++++++++++++++++++++++++++++++++++ 5 files changed, 153 insertions(+), 18 deletions(-) create mode 100644 test/model.application.test.js diff --git a/example/mobile-models/app.js b/example/mobile-models/app.js index f671f360..e7e3c582 100644 --- a/example/mobile-models/app.js +++ b/example/mobile-models/app.js @@ -36,7 +36,7 @@ Application.create(data, function(err, data) { }); -Application.register('MyApp', 'My first mobile application', 'rfeng', function (err, result) { +Application.register('rfeng', 'MyApp', {description: 'My first mobile application'}, function (err, result) { console.log(result.toObject()); result.resetKeys(function (err, result) { diff --git a/lib/models/acl.js b/lib/models/acl.js index 42c80a04..7c70af43 100644 --- a/lib/models/acl.js +++ b/lib/models/acl.js @@ -20,19 +20,32 @@ Factors to be authorized against: Class level permissions, for example, Album * model name: Album * methods -// blog posts -allow: ['owner', 'admin'] to: '*' // allow owner's of posts and admins to do anything -allow: '*' to: ['find', 'read'] // allow everyone to read and find -// comments -allow '*' to: ['find', 'read'] // read aka findById -allow 'user' to: ['create'] -allow ['owner', 'admin'] to: '*' -// users only section -allow: '*' to: ['find', 'read', 'create'] -allow: 'owner' to: ['*.destroy', '*.save'] +URL/Route level permissions + * url pattern + * application id + * ip addresses + * http headers -// scopes +Map to oAuth 2.0 scopes -// URL level permissions */ + +var ACLSchema = { + model: String, // The model name + properties: [String], // A list of property names + methods: [String], // A list of methods + roles: [String], // A list of roles + permission: {type: String, enum: ['Allow', 'Deny']}, // Allow/Deny + status: String, // Enabled/disabled + created: Date, + modified: Date +} + +// readAccess, writeAccess --> public, userId, role + +module.exports = function(dataSource) { + dataSource = dataSource || new require('loopback-data').ModelBuilder(); + var ACL = dataSource.define('ACL', ACLSchema); + return ACL; +} \ No newline at end of file diff --git a/lib/models/application.js b/lib/models/application.js index e66e29da..fa1ae2a3 100644 --- a/lib/models/application.js +++ b/lib/models/application.js @@ -1,3 +1,5 @@ +var assert = require('assert'); + // Authentication schemes var AuthenticationSchemeSchema = { scheme: String, // local, facebook, google, twitter, linkedin, github @@ -33,7 +35,7 @@ var PushNotificationSettingSchema = { var ApplicationSchema = { // Basic information - id: {type: String, required: true}, // The id + id: {type: String, required: true, generated: true, id: true}, // The id name: {type: String, required: true}, // The name description: String, // The description icon: String, // The icon image url @@ -79,9 +81,10 @@ var ApplicationSchema = { var crypto = require('crypto'); -function generateKey(hmacKey, algorithm) { +function generateKey(hmacKey, algorithm, encoding) { hmacKey = hmacKey || 'loopback'; algorithm = algorithm || 'sha256'; + encoding = encoding || 'base64'; var hmac = crypto.createHmac(algorithm, hmacKey); var buf = crypto.randomBytes(64); hmac.update(buf); @@ -114,20 +117,59 @@ module.exports = function (dataSource) { app.masterKey = generateKey('master'); }; - // Register a new application - Application.register = function (name, description, owner, cb) { - Application.create({name: name, description: description, owner: owner}, cb); + /** + * Register a new application + * @param owner Owner's user id + * @param name Name of the application + * @param options Other options + * @param cb Callback function + */ + Application.register = function (owner, name, options, cb) { + assert(owner, 'owner is required'); + assert(name, 'name is required'); + + if(typeof options === 'function' && !cb) { + cb = options; + options = {}; + } + var props = {owner: owner, name: name}; + for(var p in options) { + if(!(p in props)) { + props[p] = options[p]; + } + } + Application.create(props, cb); } + /** + * Reset keys for the application instance + * @param cb + */ Application.prototype.resetKeys = function(cb) { this.clientKey = generateKey('client'); this.javaScriptKey = generateKey('javaScript'); this.restApiKey = generateKey('restApi'); this.windowsKey = generateKey('windows'); this.masterKey = generateKey('master'); + this.modified = new Date(); this.save(cb); } + /** + * Reset keys for a given application by the appId + * @param appId + * @param cb + */ + Application.resetKeys = function(appId, cb) { + Application.findById(appId, function(err, app) { + if(err) { + cb && cb(err, app); + return; + } + app.resetKeys(cb); + }); + } + return Application; } diff --git a/lib/models/index.js b/lib/models/index.js index 35f6a412..1ab7d7f4 100644 --- a/lib/models/index.js +++ b/lib/models/index.js @@ -1,6 +1,13 @@ +exports.Model = require('./model'); +exports.Email = require('./email'); +exports.User = require('./user'); +exports.Session = require('./session'); + exports.Application = require('./application'); exports.ACL = require('./acl'); exports.Role = require('./role'); exports.Installation = require('./installation'); + + diff --git a/test/model.application.test.js b/test/model.application.test.js new file mode 100644 index 00000000..7377c526 --- /dev/null +++ b/test/model.application.test.js @@ -0,0 +1,73 @@ +var models = require('../lib/models'); +var loopback = require(('../')); +var assert = require('assert'); + +var dataSource = loopback.createDataSource('db', {connector: loopback.Memory}); + +var Application = models.Application(dataSource); + +describe('Application', function () { + var registeredApp = null; + + it('Create a new application', function (done) { + + Application.create({owner: 'rfeng', name: 'MyApp1', description: 'My first mobile application'}, function (err, result) { + var app = result; + assert.equal(app.owner, 'rfeng'); + assert.equal(app.name, 'MyApp1'); + assert.equal(app.description, 'My first mobile application'); + assert(app.clientKey); + assert(app.javaScriptKey); + assert(app.restApiKey); + assert(app.windowsKey); + assert(app.masterKey); + assert(app.created); + assert(app.modified); + done(err, result); + }); + }); + + it('Register a new application', function (done) { + + Application.register('rfeng', 'MyApp2', {description: 'My second mobile application'}, function (err, result) { + var app = result; + assert.equal(app.owner, 'rfeng'); + assert.equal(app.name, 'MyApp2'); + assert.equal(app.description, 'My second mobile application'); + assert(app.clientKey); + assert(app.javaScriptKey); + assert(app.restApiKey); + assert(app.windowsKey); + assert(app.masterKey); + assert(app.created); + assert(app.modified); + registeredApp = app; + done(err, result); + }); + }); + + it('Reset keys', function (done) { + + Application.resetKeys(registeredApp.id, function (err, result) { + var app = result; + assert.equal(app.owner, 'rfeng'); + assert.equal(app.name, 'MyApp2'); + assert.equal(app.description, 'My second mobile application'); + assert(app.clientKey); + assert(app.javaScriptKey); + assert(app.restApiKey); + assert(app.windowsKey); + assert(app.masterKey); + + assert(app.clientKey !== registeredApp.clientKey); + assert(app.javaScriptKey !== registeredApp.javaScriptKey); + assert(app.restApiKey !== registeredApp.restApiKey); + assert(app.windowsKey !== registeredApp.windowsKey); + assert(app.masterKey !== registeredApp.masterKey); + + assert(app.created); + assert(app.modified); + done(err, result); + }); + }); +});