diff --git a/lib/application.js b/lib/application.js index d616e1c0..d1c2b17e 100644 --- a/lib/application.js +++ b/lib/application.js @@ -305,7 +305,9 @@ app.enableAuth = function() { next(); } }); -} + + this.isAuthEnabled = true; +}; /** * Initialize an application from an options object or a set of JSON and JavaScript files. @@ -319,7 +321,7 @@ app.enableAuth = function() { * * **Options** * - * - `cwd` - _optional_ - the directory to use when loading JSON and JavaScript files + * - `appRootDir` - _optional_ - the directory to use when loading JSON and JavaScript files * - `models` - _optional_ - an object containing `Model` definitions * - `dataSources` - _optional_ - an object containing `DataSource` definitions * diff --git a/lib/loopback.js b/lib/loopback.js index fd96b8ea..568441d7 100644 --- a/lib/loopback.js +++ b/lib/loopback.js @@ -57,6 +57,11 @@ function createApplication() { utils.merge(app, proto); + // Create a new instance of models registry per each app instance + app.models = function() { + return proto.models.apply(this, arguments); + }; + return app; } diff --git a/lib/models/application.js b/lib/models/application.js index fe2f370c..2841a6e5 100644 --- a/lib/models/application.js +++ b/lib/models/application.js @@ -98,12 +98,13 @@ var crypto = require('crypto'); function generateKey(hmacKey, algorithm, encoding) { hmacKey = hmacKey || 'loopback'; - algorithm = algorithm || 'sha256'; - encoding = encoding || 'base64'; + algorithm = algorithm || 'sha1'; + encoding = encoding || 'hex'; var hmac = crypto.createHmac(algorithm, hmacKey); - var buf = crypto.randomBytes(64); + var buf = crypto.randomBytes(32); hmac.update(buf); - return hmac.digest('base64'); + var key = hmac.digest(encoding); + return key; } /** @@ -121,7 +122,7 @@ var Application = loopback.createModel('Application', ApplicationSchema); Application.beforeCreate = function (next) { var app = this; app.created = app.modified = new Date(); - app.id = generateKey('id', 'sha1'); + app.id = generateKey('id', 'md5'); app.clientKey = generateKey('client'); app.javaScriptKey = generateKey('javaScript'); app.restApiKey = generateKey('restApi'); @@ -208,13 +209,18 @@ Application.authenticate = function (appId, key, cb) { cb && cb(err, null); return; } - var matched = null; - ['clientKey', 'javaScriptKey', 'restApiKey', 'windowsKey', 'masterKey'].forEach(function (k) { - if (app[k] === key) { - matched = k; + var result = null; + var keyNames = ['clientKey', 'javaScriptKey', 'restApiKey', 'windowsKey', 'masterKey']; + for (var i = 0; i < keyNames.length; i++) { + if (app[keyNames[i]] === key) { + result = { + application: app, + keyType: keyNames[i] + }; + break; } - }); - cb && cb(null, matched); + } + cb && cb(null, result); }); }; diff --git a/lib/models/model.js b/lib/models/model.js index 47fad80c..78df36f0 100644 --- a/lib/models/model.js +++ b/lib/models/model.js @@ -117,10 +117,20 @@ Model.setup = function () { /*! * Get the reference to ACL in a lazy fashion to avoid race condition in require */ -var ACL = null; -function getACL() { - return ACL || (ACL = require('./acl').ACL); -} +var _aclModel = null; +Model._ACL = function getACL(ACL) { + if(ACL !== undefined) { + // The function is used as a setter + _aclModel = ACL; + } + if(_aclModel) { + return _aclModel; + } + var aclModel = require('./acl').ACL; + _aclModel = loopback.getModelByType(aclModel); + return _aclModel; +}; + /** * Check if the given access token can invoke the method @@ -137,9 +147,9 @@ function getACL() { Model.checkAccess = function(token, modelId, method, callback) { var ANONYMOUS = require('./access-token').ANONYMOUS; token = token || ANONYMOUS; - var ACL = getACL(); + var aclModel = Model._ACL(); var methodName = 'string' === typeof method? method: method && method.name; - ACL.checkAccessForToken(token, this.modelName, modelId, methodName, callback); + aclModel.checkAccessForToken(token, this.modelName, modelId, methodName, callback); }; /*! @@ -158,7 +168,7 @@ Model._getAccessTypeForMethod = function(method) { 'method is a required argument and must be a RemoteMethod object' ); - var ACL = getACL(); + var ACL = Model._ACL(); switch(method.name) { case'create': diff --git a/package.json b/package.json index d504b4f6..510327cf 100644 --- a/package.json +++ b/package.json @@ -9,35 +9,35 @@ "Platform", "mBaaS" ], - "version": "1.6.1", + "version": "1.6.2", "scripts": { "test": "mocha -R spec" }, "dependencies": { - "debug": "~0.7.2", - "express": "~3.4.0", - "strong-remoting": "~1.2.1", - "inflection": "~1.2.5", - "passport": "~0.1.17", + "debug": "~0.7.4", + "express": "~3.4.8", + "strong-remoting": "~1.2.4", + "inflection": "~1.2.7", + "passport": "~0.2.0", "passport-local": "~0.1.6", - "nodemailer": "~0.5.7", - "ejs": "~0.8.4", + "nodemailer": "~0.6.0", + "ejs": "~0.8.5", "bcryptjs": "~0.7.10", "underscore.string": "~2.3.3", - "underscore": "~1.5.2", + "underscore": "~1.6.0", "uid2": "0.0.3", - "async": "~0.2.9" + "async": "~0.2.10" }, "peerDependencies": { - "loopback-datasource-juggler": "~1.2.13" + "loopback-datasource-juggler": "~1.3.0" }, "devDependencies": { - "loopback-datasource-juggler": "~1.2.13", - "mocha": "~1.14.0", + "loopback-datasource-juggler": "~1.3.0", + "mocha": "~1.17.1", "strong-task-emitter": "0.0.x", - "supertest": "~0.8.1", - "chai": "~1.8.1", - "loopback-testing": "~0.1.0" + "supertest": "~0.9.0", + "chai": "~1.9.0", + "loopback-testing": "~0.1.2" }, "repository": { "type": "git", diff --git a/test/app.test.js b/test/app.test.js index a806f251..701d22fe 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -76,10 +76,17 @@ describe('app', function() { }); }); + describe('app.models', function() { + it('is unique per app instance', function() { + var Color = app.model('Color', { dataSource: 'db' }); + expect(app.models.Color).to.equal(Color); + var anotherApp = loopback(); + expect(anotherApp.models.Color).to.equal(undefined); + }); + }); + describe('app.boot([options])', function () { beforeEach(function () { - var app = this.app = loopback(); - app.boot({ app: { port: 3000, @@ -393,6 +400,14 @@ describe('app', function() { }); }); + describe('enableAuth', function() { + it('should set app.isAuthEnabled to true', function() { + expect(app.isAuthEnabled).to.not.equal(true); + app.enableAuth(); + expect(app.isAuthEnabled).to.equal(true); + }); + }); + describe('app.get("/", loopback.status())', function () { it('should return the status of the application', function (done) { var app = loopback(); diff --git a/test/model.application.test.js b/test/model.application.test.js index d27a4750..d7f24df1 100644 --- a/test/model.application.test.js +++ b/test/model.application.test.js @@ -121,7 +121,8 @@ describe('Application', function () { it('Authenticate with application id & clientKey', function (done) { Application.authenticate(registeredApp.id, registeredApp.clientKey, function (err, result) { - assert.equal(result, 'clientKey'); + assert.equal(result.application.id, registeredApp.id); + assert.equal(result.keyType, 'clientKey'); done(err, result); }); }); @@ -129,7 +130,8 @@ describe('Application', function () { it('Authenticate with application id & javaScriptKey', function (done) { Application.authenticate(registeredApp.id, registeredApp.javaScriptKey, function (err, result) { - assert.equal(result, 'javaScriptKey'); + assert.equal(result.application.id, registeredApp.id); + assert.equal(result.keyType, 'javaScriptKey'); done(err, result); }); }); @@ -137,7 +139,8 @@ describe('Application', function () { it('Authenticate with application id & restApiKey', function (done) { Application.authenticate(registeredApp.id, registeredApp.restApiKey, function (err, result) { - assert.equal(result, 'restApiKey'); + assert.equal(result.application.id, registeredApp.id); + assert.equal(result.keyType, 'restApiKey'); done(err, result); }); }); @@ -145,7 +148,8 @@ describe('Application', function () { it('Authenticate with application id & masterKey', function (done) { Application.authenticate(registeredApp.id, registeredApp.masterKey, function (err, result) { - assert.equal(result, 'masterKey'); + assert.equal(result.application.id, registeredApp.id); + assert.equal(result.keyType, 'masterKey'); done(err, result); }); }); @@ -153,7 +157,8 @@ describe('Application', function () { it('Authenticate with application id & windowsKey', function (done) { Application.authenticate(registeredApp.id, registeredApp.windowsKey, function (err, result) { - assert.equal(result, 'windowsKey'); + assert.equal(result.application.id, registeredApp.id); + assert.equal(result.keyType, 'windowsKey'); done(err, result); }); }); @@ -170,13 +175,14 @@ describe('Application', function () { describe('Application subclass', function () { it('should use subclass model name', function (done) { var MyApp = Application.extend('MyApp'); - MyApp.attachTo(loopback.createDataSource({connector: loopback.Memory})); - MyApp.register('rfeng', 'MyApp2', - {description: 'My second mobile application'}, function (err, result) { + var ds = loopback.createDataSource({connector: loopback.Memory}); + MyApp.attachTo(ds); + MyApp.register('rfeng', 'MyApp123', + {description: 'My 123 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.equal(app.name, 'MyApp123'); + assert.equal(app.description, 'My 123 mobile application'); assert(app.clientKey); assert(app.javaScriptKey); assert(app.restApiKey); @@ -184,14 +190,17 @@ describe('Application subclass', function () { assert(app.masterKey); assert(app.created); assert(app.modified); - MyApp.findById(app.id, function (err, myApp) { - assert(!err); - assert(myApp); - - Application.findById(app.id, function (err, myApp) { + // Remove all instances from Application model to avoid left-over data + Application.destroyAll(function () { + MyApp.findById(app.id, function (err, myApp) { assert(!err); - assert(myApp === null); - done(err, myApp); + assert(myApp); + + Application.findById(app.id, function (err, myApp) { + assert(!err); + assert(myApp === null); + done(err, myApp); + }); }); }); }); diff --git a/test/model.test.js b/test/model.test.js index 8cb112ab..b2222888 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -612,6 +612,16 @@ describe('Model', function() { } }); + describe('Model._getACLModel()', function() { + it('should return the subclass of ACL', function() { + var Model = require('../').Model; + var acl = ACL.extend('acl'); + Model._ACL(null); // Reset the ACL class for the base model + var model = Model._ACL(); + assert.equal(model, acl); + }); + }); + // describe('Model.hasAndBelongsToMany()', function() { // it("TODO: implement / document", function(done) { // /* example - diff --git a/test/support.js b/test/support.js index 394bc6d4..85aac797 100644 --- a/test/support.js +++ b/test/support.js @@ -17,7 +17,7 @@ request = require('supertest'); loopback.User.settings.saltWorkFactor = 4; beforeEach(function () { - app = loopback(); + this.app = app = loopback(); // setup default data sources loopback.setDefaultDataSourceForType('db', {