diff --git a/common/models/application.js b/common/models/application.js index 4f3a066d..22892eb1 100644 --- a/common/models/application.js +++ b/common/models/application.js @@ -1,95 +1,5 @@ -var loopback = require('../../lib/loopback'); var assert = require('assert'); -// Authentication schemes -var AuthenticationSchemeSchema = { - scheme: String, // local, facebook, google, twitter, linkedin, github - credential: Object // Scheme-specific credentials -}; - -// See https://github.com/argon/node-apn/blob/master/doc/apn.markdown -var APNSSettingSchema = { - /** - * production or development mode. It denotes what default APNS servers to be - * used to send notifications - * - true (production mode) - * - push: gateway.push.apple.com:2195 - * - feedback: feedback.push.apple.com:2196 - * - false (development mode, the default) - * - push: gateway.sandbox.push.apple.com:2195 - * - feedback: feedback.sandbox.push.apple.com:2196 - */ - production: Boolean, - certData: String, // The certificate data loaded from the cert.pem file - keyData: String, // The key data loaded from the key.pem file - - pushOptions: {type: { - gateway: String, - port: Number - }}, - - feedbackOptions: {type: { - gateway: String, - port: Number, - batchFeedback: Boolean, - interval: Number - }} -}; - -var GcmSettingsSchema = { - serverApiKey: String -}; - -// Push notification settings -var PushNotificationSettingSchema = { - apns: APNSSettingSchema, - gcm: GcmSettingsSchema -}; - -/*! - * Data model for Application - */ -var ApplicationSchema = { - id: {type: String, id: true}, - // Basic information - name: {type: String, required: true}, // The name - description: String, // The description - icon: String, // The icon image url - - owner: String, // The user id of the developer who registers the application - collaborators: [String], // A list of users ids who have permissions to work on this app - - // EMail - email: String, // e-mail address - emailVerified: Boolean, // Is the e-mail verified - - // oAuth 2.0 settings - url: String, // The application url - callbackUrls: [String], // oAuth 2.0 code/token callback url - permissions: [String], // A list of permissions required by the application - - // Keys - clientKey: String, - javaScriptKey: String, - restApiKey: String, - windowsKey: String, - masterKey: String, - - // Push notification - pushSettings: PushNotificationSettingSchema, - - // User Authentication - authenticationEnabled: {type: Boolean, default: true}, - anonymousAllowed: {type: Boolean, default: true}, - authenticationSchemes: [AuthenticationSchemeSchema], - - status: {type: String, default: 'sandbox'}, // Status of the application, production/sandbox/disabled - - // Timestamps - created: {type: Date, default: Date}, - modified: {type: Date, default: Date} -}; - /*! * Application management functions */ @@ -109,7 +19,7 @@ function generateKey(hmacKey, algorithm, encoding) { /** * Manage client applications and organize their users. - * + * * @property {String} id Generated ID. * @property {String} name Name; required. * @property {String} description Text description @@ -122,7 +32,10 @@ function generateKey(hmacKey, algorithm, encoding) { * @property {String} status Status of the application; Either `production`, `sandbox` (default), or `disabled`. * @property {Date} created Date Application object was created. Default: current date. * @property {Date} modified Date Application object was modified. Default: current date. - * + * + * @property {Object} pushSettings.apns APNS configuration, see the options + * below and also + * https://github.com/argon/node-apn/blob/master/doc/apn.markdown * @property {Boolean} pushSettings.apns.production Whether to use production Apple Push Notification Service (APNS) servers to send push notifications. * If true, uses `gateway.push.apple.com:2195` and `feedback.push.apple.com:2196`. * If false, uses `gateway.sandbox.push.apple.com:2195` and `feedback.sandbox.push.apple.com:2196` @@ -135,120 +48,140 @@ function generateKey(hmacKey, algorithm, encoding) { * @property {Boolean} pushSettings.apns.feedbackOptions.batchFeedback (APNS). * @property {Number} pushSettings.apns.feedbackOptions.interval (APNS). * @property {String} pushSettings.gcm.serverApiKey: Google Cloud Messaging API key. - * - * @class - * @inherits {Model} - */ - -var Application = loopback.PersistedModel.extend('Application', ApplicationSchema); - -/*! - * A hook to generate keys before creation - * @param next - */ -Application.beforeCreate = function (next) { - var app = this; - app.created = app.modified = new Date(); - app.id = generateKey('id', 'md5'); - app.clientKey = generateKey('client'); - app.javaScriptKey = generateKey('javaScript'); - app.restApiKey = generateKey('restApi'); - app.windowsKey = generateKey('windows'); - app.masterKey = generateKey('master'); - next(); -}; - -/** - * Register a new application - * @param {String} owner Owner's user ID. - * @param {String} name Name of the application - * @param {Object} options Other options - * @param {Function} callback 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]; - } - } - this.create(props, cb); -}; - -/** - * Reset keys for the application instance - * @callback {Function} callback - * @param {Error} err - */ -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 {Any} appId - * @callback {Function} callback - * @param {Error} err - */ -Application.resetKeys = function (appId, cb) { - this.findById(appId, function (err, app) { - if (err) { - cb && cb(err, app); - return; - } - app.resetKeys(cb); - }); -}; - -/** - * Authenticate the application id and key. - * - * `matched` parameter is one of: - * - clientKey - * - javaScriptKey - * - restApiKey - * - windowsKey - * - masterKey * - * @param {Any} appId - * @param {String} key - * @callback {Function} callback - * @param {Error} err - * @param {String} matched The matching key + * @property {Boolean} authenticationEnabled + * @property {Boolean} anonymousAllowed + * @property {Array} authenticationSchemes List of authentication schemes + * (see below). + * @property {String} authenticationSchemes.scheme Scheme name. + * Supported values: `local`, `facebook`, `google`, + * `twitter`, `linkedin`, `github`. + * @property {Object} authenticationSchemes.credential + * Scheme-specific credentials. + * + * @class Application + * @inherits {PersistedModel} */ -Application.authenticate = function (appId, key, cb) { - this.findById(appId, function (err, app) { - if (err || !app) { - cb && cb(err, null); - return; + +module.exports = function(Application) { + + // Workaround for https://github.com/strongloop/loopback/issues/292 + Application.definition.rawProperties.created.default = + Application.definition.properties.created.default = function() { + return new Date(); + }; + + // Workaround for https://github.com/strongloop/loopback/issues/292 + Application.definition.rawProperties.modified.default = + Application.definition.properties.modified.default = function() { + return new Date(); + }; + + /*! + * A hook to generate keys before creation + * @param next + */ + Application.beforeCreate = function(next) { + var app = this; + app.created = app.modified = new Date(); + app.id = generateKey('id', 'md5'); + app.clientKey = generateKey('client'); + app.javaScriptKey = generateKey('javaScript'); + app.restApiKey = generateKey('restApi'); + app.windowsKey = generateKey('windows'); + app.masterKey = generateKey('master'); + next(); + }; + + /** + * Register a new application + * @param {String} owner Owner's user ID. + * @param {String} name Name of the application + * @param {Object} options Other options + * @param {Function} callback 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 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; + var props = {owner: owner, name: name}; + for (var p in options) { + if (!(p in props)) { + props[p] = options[p]; } } - cb && cb(null, result); - }); + this.create(props, cb); + }; + + /** + * Reset keys for the application instance + * @callback {Function} callback + * @param {Error} err + */ + 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 {Any} appId + * @callback {Function} callback + * @param {Error} err + */ + Application.resetKeys = function(appId, cb) { + this.findById(appId, function(err, app) { + if (err) { + cb && cb(err, app); + return; + } + app.resetKeys(cb); + }); + }; + + /** + * Authenticate the application id and key. + * + * `matched` parameter is one of: + * - clientKey + * - javaScriptKey + * - restApiKey + * - windowsKey + * - masterKey + * + * @param {Any} appId + * @param {String} key + * @callback {Function} callback + * @param {Error} err + * @param {String} matched The matching key + */ + Application.authenticate = function(appId, key, cb) { + this.findById(appId, function(err, app) { + if (err || !app) { + cb && cb(err, null); + return; + } + 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, result); + }); + }; }; - -module.exports = Application; - diff --git a/common/models/application.json b/common/models/application.json new file mode 100644 index 00000000..8b053ced --- /dev/null +++ b/common/models/application.json @@ -0,0 +1,121 @@ +{ + "name": "Application", + "properties": { + "id": { + "type": "string", + "id": true + }, + "name": { + "type": "string", + "required": true + }, + "description": "string", + "icon": { + "type": "string", + "description": "The icon image url" + }, + + "owner": { + "type": "string", + "description": "The user id of the developer who registers the application" + }, + "collaborators": { + "type": ["string"], + "description": "A list of users ids who have permissions to work on this app" + }, + + "email": "string", + "emailVerified": "boolean", + + "url": { + "type": "string", + "description": "The application URL for OAuth 2.0" + }, + "callbackUrls": { + "type": ["string"], + "description": "OAuth 2.0 code/token callback URLs" + }, + "permissions": { + "type": ["string"], + "description": "A list of permissions required by the application" + }, + + "clientKey": "string", + "javaScriptKey": "string", + "restApiKey": "string", + "windowsKey": "string", + "masterKey": "string", + + "pushSettings": { + "apns": { + "production": { + "type": "boolean", + "description": [ + "Production or development mode. It denotes what default APNS", + "servers to be used to send notifications.", + "See API documentation for more details." + ] + }, + + "certData": { + "type": "string", + "description": "The certificate data loaded from the cert.pem file" + }, + "keyData": { + "type": "string", + "description": "The key data loaded from the key.pem file" + }, + + "pushOptions": { + "type": { + "gateway": "string", + "port": "number" + } + }, + + "feedbackOptions": { + "type": { + "gateway": "string", + "port": "number", + "batchFeedback": "boolean", + "interval": "number" + } + } + }, + + "gcm": { + "serverApiKey": "string" + } + }, + + "authenticationEnabled": { + "type": "boolean", + "default": true + }, + "anonymousAllowed": { + "type": "boolean", + "default": true + }, + "authenticationSchemes": [ + { + "scheme": { + "type": "string", + "description": "See the API docs for the list of supported values." + }, + "credential": { + "type": "object", + "description": "Scheme-specific credentials" + } + } + ], + + "status": { + "type": "string", + "default": "sandbox", + "description": "Status of the application, production/sandbox/disabled" + }, + + "created": "date", + "modified": "date" + } +} diff --git a/lib/builtin-models.js b/lib/builtin-models.js index f95433b7..ef847a07 100644 --- a/lib/builtin-models.js +++ b/lib/builtin-models.js @@ -5,7 +5,10 @@ module.exports = function(loopback) { require('../common/models/email.json'), require('../common/models/email.js')); - loopback.Application = require('../common/models/application'); + loopback.Application = createModel( + require('../common/models/application.json'), + require('../common/models/application.js')); + loopback.AccessToken = require('../common/models/access-token'); loopback.Role = require('../common/models/role').Role; loopback.RoleMapping = require('../common/models/role').RoleMapping;