From da1cd6739b20616c3e17558486b8fe8c1d09fef0 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Mon, 22 Jul 2013 11:15:02 -0700 Subject: [PATCH] Add more docs and apis to application model --- lib/models/README.md | 125 +++++++++++++++++++++++++++++++++ lib/models/application.js | 26 ++++++- lib/models/installation.js | 4 +- test/model.application.test.js | 48 +++++++++++++ 4 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 lib/models/README.md diff --git a/lib/models/README.md b/lib/models/README.md new file mode 100644 index 00000000..f4dc044a --- /dev/null +++ b/lib/models/README.md @@ -0,0 +1,125 @@ +# Application + +Application model captures the metadata for a loopback application. + +## Each application has the following basic properties: + +* id: Automatically generated id +* name: Name of the application (required) +* description: Description of the application (optional) +* icon: URL of the icon +* status: Status of the application, such as production/sandbox/disabled +* created: Timestamp of the record being created +* modified: Timestamp of the record being modified + +## An application has the following properties linking to users: + +* owner: The user id of the developer who registers the application +* collaborators: A array of users ids who have permissions to work on this app + +## oAuth 2.0 settings + +* url: The application url +* callbackUrls: An array of preregistered callback urls for oAuth 2.0 +* permissions: An array of oAuth 2.0 scopes that can be requested by the application + +## Security keys + +The following keys are automatically generated by the application creation process. They can be reset upon request. + +* clientKey: Secret for mobile clients +* javaScriptKey: Secret for JavaScript clients +* restApiKey: Secret for REST APIs +* windowsKey: Secret for Windows applications +* masterKey: Secret for REST APIS. It bypasses model level permissions + +## Push notification settings + +The application can be configured to support multiple methods of push notifications. + +* pushSettings + + + { + pushSettings: [ + { "platform": "apns", + "apns": { + "pushOptions": { + "gateway": "gateway.sandbox.push.apple.com", + "cert": "credentials/apns_cert_dev.pem", + "key": "credentials/apns_key_dev.pem" + }, + + "feedbackOptions": { + "gateway": "feedback.sandbox.push.apple.com", + "cert": "credentials/apns_cert_dev.pem", + "key": "credentials/apns_key_dev.pem", + "batchFeedback": true, + "interval": 300 + } + }} + ]} + + +## Authentication schemes + + +* authenticationEnabled +* anonymousAllowed +* authenticationSchemes + +### Authentication scheme settings + +* scheme: Name of the authentication scheme, such as local, facebook, google, twitter, linkedin, github +* credential: Scheme-specific credentials + +## APIs for Application model + +In addition to the CRUD methods, the Application model also has the following apis: + +### Register a new application + +You can register a new application by providing the owner user id, applicaiton name, and other properties in the options object. + + Application.register('rfeng', 'MyApp1', {description: 'My first loopback application'}, function (err, result) { + var app = result; + ... + }); + +### Reset keys + +You can reset keys for a given application by id. + + Application.resetKeys(appId, function (err, result) { + var app = result; + ... + }); + +### Authenticate by appId and key + +You can authenticate an application by id and one of the keys. If successful, it calls back with the key name in the result argument. Otherwise, the keyName is null. + + Application.authenticate(appId, clientKey, function (err, keyName) { + assert.equal(keyName, 'clientKey'); + ... + }); + + +# Installation + +Installation captures the installation of the application on devices. + +Each record of installation has the following properties: + +* id: Generated id that uniquely identifies the installation +* appId: Application id +* appVersion: Application version +* userId: The current user id that logs into the application +* deviceToken: Device token +* deviceType: Device type, such as apns +* subscriptions: An Array of tags that represents subscriptions of notifications +* status: Status of the application, production/sandbox/disabled +* created: Timestamp of the recored being created +* modified: Timestamp of the recored being modified + + diff --git a/lib/models/application.js b/lib/models/application.js index fa1ae2a3..331275e9 100644 --- a/lib/models/application.js +++ b/lib/models/application.js @@ -105,7 +105,8 @@ module.exports = function (dataSource) { // Application.hasMany(AuthenticationScheme, {as: 'authenticationSchemes', foreignKey: 'appId'}); // Application.hasMany(PushNotificationSetting, {as: 'pushNotificationSettings', foreignKey: 'appId'}); - Application.afterInitialize = function () { + Application.beforeCreate = function (next) { + // console.trace(); var app = this; // use data argument to update object app.created = app.modified = new Date(); @@ -115,6 +116,7 @@ module.exports = function (dataSource) { app.restApiKey = generateKey('restApi'); app.windowsKey = generateKey('windows'); app.masterKey = generateKey('master'); + next(); }; /** @@ -170,6 +172,28 @@ module.exports = function (dataSource) { }); } + /** + * + * @param appId + * @param key + * @param cb + */ + Application.authenticate = function(appId, key, cb) { + Application.findById(appId, function(err, app) { + if(err || !app) { + cb && cb(err, null); + return; + } + var matched = null; + ['clientKey', 'javaScriptKey', 'restApiKey', 'windowsKey', 'masterKey'].forEach(function(k) { + if(app[k] === key) { + matched = k; + } + }); + cb && cb(null, matched); + }); + } + return Application; } diff --git a/lib/models/installation.js b/lib/models/installation.js index d54b3246..841383a1 100644 --- a/lib/models/installation.js +++ b/lib/models/installation.js @@ -8,8 +8,8 @@ var InstallationSchema = { appId: String, // Application id appVersion: String, // Application version userId: String, // User id - deviceToken: String, - deviceType: String, + deviceToken: String, // Device token + deviceType: String, // Device type, such as apns subscriptions: [String], status: {type: String, default: 'active'}, // Status of the application, production/sandbox/disabled diff --git a/test/model.application.test.js b/test/model.application.test.js index 7377c526..f1d8cfce 100644 --- a/test/model.application.test.js +++ b/test/model.application.test.js @@ -67,7 +67,55 @@ describe('Application', function () { assert(app.created); assert(app.modified); + registeredApp = app; done(err, result); }); }); + + it('Authenticate with application id & clientKey', function (done) { + + Application.authenticate(registeredApp.id, registeredApp.clientKey, function (err, result) { + assert.equal(result, 'clientKey'); + done(err, result); + }); + }); + + it('Authenticate with application id & javaScriptKey', function (done) { + + Application.authenticate(registeredApp.id, registeredApp.javaScriptKey, function (err, result) { + assert.equal(result, 'javaScriptKey'); + done(err, result); + }); + }); + + it('Authenticate with application id & restApiKey', function (done) { + Application.authenticate(registeredApp.id, registeredApp.restApiKey, function (err, result) { + assert.equal(result, 'restApiKey'); + done(err, result); + }); + }); + + it('Authenticate with application id & masterKey', function (done) { + Application.authenticate(registeredApp.id, registeredApp.masterKey, function (err, result) { + assert.equal(result, 'masterKey'); + done(err, result); + }); + }); + + + it('Authenticate with application id & windowsKey', function (done) { + Application.authenticate(registeredApp.id, registeredApp.windowsKey, function (err, result) { + assert.equal(result, 'windowsKey'); + done(err, result); + }); + }); + + it('Fail to authenticate with application id & invalid key', function (done) { + Application.authenticate(registeredApp.id, 'invalid-key', function (err, result) { + assert(!result); + done(err, result); + }); + }); + }); +