Merge pull request #105 from strongloop/feature/push-settings
Allow cert/key data to be shared by push/feedback
This commit is contained in:
commit
3652c1584a
|
@ -3,81 +3,93 @@ var assert = require('assert');
|
||||||
|
|
||||||
// Authentication schemes
|
// Authentication schemes
|
||||||
var AuthenticationSchemeSchema = {
|
var AuthenticationSchemeSchema = {
|
||||||
scheme: String, // local, facebook, google, twitter, linkedin, github
|
scheme: String, // local, facebook, google, twitter, linkedin, github
|
||||||
credential: Object // Scheme-specific credentials
|
credential: Object // Scheme-specific credentials
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// See https://github.com/argon/node-apn/blob/master/doc/apn.markdown
|
||||||
var APNSSettingSchema = {
|
var APNSSettingSchema = {
|
||||||
pushOptions: {type: {
|
/**
|
||||||
gateway: String,
|
* production or development mode. It denotes what default APNS servers to be
|
||||||
cert: String,
|
* used to send notifications
|
||||||
key: String
|
* - 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
|
||||||
|
|
||||||
feedbackOptions: {type: {
|
pushOptions: {type: {
|
||||||
gateway: String,
|
gateway: String,
|
||||||
cert: String,
|
port: Number
|
||||||
key: String,
|
}},
|
||||||
batchFeedback: Boolean,
|
|
||||||
interval: Number
|
feedbackOptions: {type: {
|
||||||
}}
|
gateway: String,
|
||||||
|
port: Number,
|
||||||
|
batchFeedback: Boolean,
|
||||||
|
interval: Number
|
||||||
|
}}
|
||||||
};
|
};
|
||||||
|
|
||||||
var GcmSettingsSchema = {
|
var GcmSettingsSchema = {
|
||||||
serverApiKey: String
|
serverApiKey: String
|
||||||
}
|
};
|
||||||
|
|
||||||
// Push notification settings
|
// Push notification settings
|
||||||
var PushNotificationSettingSchema = {
|
var PushNotificationSettingSchema = {
|
||||||
apns: APNSSettingSchema,
|
apns: APNSSettingSchema,
|
||||||
gcm: GcmSettingsSchema
|
gcm: GcmSettingsSchema
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data model for Application
|
* Data model for Application
|
||||||
*/
|
*/
|
||||||
var ApplicationSchema = {
|
var ApplicationSchema = {
|
||||||
id: {type: String, id: true, generated: true},
|
id: {type: String, id: true, generated: true},
|
||||||
// Basic information
|
// Basic information
|
||||||
name: {type: String, required: true}, // The name
|
name: {type: String, required: true}, // The name
|
||||||
description: String, // The description
|
description: String, // The description
|
||||||
icon: String, // The icon image url
|
icon: String, // The icon image url
|
||||||
|
|
||||||
owner: String, // The user id of the developer who registers the application
|
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
|
collaborators: [String], // A list of users ids who have permissions to work on this app
|
||||||
|
|
||||||
// EMail
|
// EMail
|
||||||
email: String, // e-mail address
|
email: String, // e-mail address
|
||||||
emailVerified: Boolean, // Is the e-mail verified
|
emailVerified: Boolean, // Is the e-mail verified
|
||||||
|
|
||||||
// oAuth 2.0 settings
|
// oAuth 2.0 settings
|
||||||
url: String, // The application url
|
url: String, // The application url
|
||||||
callbackUrls: [String], // oAuth 2.0 code/token callback url
|
callbackUrls: [String], // oAuth 2.0 code/token callback url
|
||||||
permissions: [String], // A list of permissions required by the application
|
permissions: [String], // A list of permissions required by the application
|
||||||
|
|
||||||
// Keys
|
// Keys
|
||||||
clientKey: String,
|
clientKey: String,
|
||||||
javaScriptKey: String,
|
javaScriptKey: String,
|
||||||
restApiKey: String,
|
restApiKey: String,
|
||||||
windowsKey: String,
|
windowsKey: String,
|
||||||
masterKey: String,
|
masterKey: String,
|
||||||
|
|
||||||
// Push notification
|
// Push notification
|
||||||
pushSettings: PushNotificationSettingSchema,
|
pushSettings: PushNotificationSettingSchema,
|
||||||
|
|
||||||
// User Authentication
|
// User Authentication
|
||||||
authenticationEnabled: {type: Boolean, default: true},
|
authenticationEnabled: {type: Boolean, default: true},
|
||||||
anonymousAllowed: {type: Boolean, default: true},
|
anonymousAllowed: {type: Boolean, default: true},
|
||||||
authenticationSchemes: [AuthenticationSchemeSchema],
|
authenticationSchemes: [AuthenticationSchemeSchema],
|
||||||
|
|
||||||
status: {type: String, default: 'sandbox'}, // Status of the application, production/sandbox/disabled
|
status: {type: String, default: 'sandbox'}, // Status of the application, production/sandbox/disabled
|
||||||
|
|
||||||
// Timestamps
|
// Timestamps
|
||||||
created: {type: Date, default: Date},
|
created: {type: Date, default: Date},
|
||||||
modified: {type: Date, default: Date}
|
modified: {type: Date, default: Date}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application management functions
|
* Application management functions
|
||||||
*/
|
*/
|
||||||
|
@ -85,13 +97,13 @@ var ApplicationSchema = {
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
|
||||||
function generateKey(hmacKey, algorithm, encoding) {
|
function generateKey(hmacKey, algorithm, encoding) {
|
||||||
hmacKey = hmacKey || 'loopback';
|
hmacKey = hmacKey || 'loopback';
|
||||||
algorithm = algorithm || 'sha256';
|
algorithm = algorithm || 'sha256';
|
||||||
encoding = encoding || 'base64';
|
encoding = encoding || 'base64';
|
||||||
var hmac = crypto.createHmac(algorithm, hmacKey);
|
var hmac = crypto.createHmac(algorithm, hmacKey);
|
||||||
var buf = crypto.randomBytes(64);
|
var buf = crypto.randomBytes(64);
|
||||||
hmac.update(buf);
|
hmac.update(buf);
|
||||||
return hmac.digest('base64');
|
return hmac.digest('base64');
|
||||||
}
|
}
|
||||||
|
|
||||||
var Application = loopback.createModel('Application', ApplicationSchema);
|
var Application = loopback.createModel('Application', ApplicationSchema);
|
||||||
|
@ -101,15 +113,15 @@ var Application = loopback.createModel('Application', ApplicationSchema);
|
||||||
* @param next
|
* @param next
|
||||||
*/
|
*/
|
||||||
Application.beforeCreate = function (next) {
|
Application.beforeCreate = function (next) {
|
||||||
var app = this;
|
var app = this;
|
||||||
app.created = app.modified = new Date();
|
app.created = app.modified = new Date();
|
||||||
app.id = generateKey('id', 'sha1');
|
app.id = generateKey('id', 'sha1');
|
||||||
app.clientKey = generateKey('client');
|
app.clientKey = generateKey('client');
|
||||||
app.javaScriptKey = generateKey('javaScript');
|
app.javaScriptKey = generateKey('javaScript');
|
||||||
app.restApiKey = generateKey('restApi');
|
app.restApiKey = generateKey('restApi');
|
||||||
app.windowsKey = generateKey('windows');
|
app.windowsKey = generateKey('windows');
|
||||||
app.masterKey = generateKey('master');
|
app.masterKey = generateKey('master');
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,34 +132,34 @@ Application.beforeCreate = function (next) {
|
||||||
* @param cb Callback function
|
* @param cb Callback function
|
||||||
*/
|
*/
|
||||||
Application.register = function (owner, name, options, cb) {
|
Application.register = function (owner, name, options, cb) {
|
||||||
assert(owner, 'owner is required');
|
assert(owner, 'owner is required');
|
||||||
assert(name, 'name is required');
|
assert(name, 'name is required');
|
||||||
|
|
||||||
if(typeof options === 'function' && !cb) {
|
if (typeof options === 'function' && !cb) {
|
||||||
cb = options;
|
cb = options;
|
||||||
options = {};
|
options = {};
|
||||||
|
}
|
||||||
|
var props = {owner: owner, name: name};
|
||||||
|
for (var p in options) {
|
||||||
|
if (!(p in props)) {
|
||||||
|
props[p] = options[p];
|
||||||
}
|
}
|
||||||
var props = {owner: owner, name: name};
|
}
|
||||||
for(var p in options) {
|
Application.create(props, cb);
|
||||||
if(!(p in props)) {
|
|
||||||
props[p] = options[p];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Application.create(props, cb);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset keys for the application instance
|
* Reset keys for the application instance
|
||||||
* @param cb
|
* @param cb
|
||||||
*/
|
*/
|
||||||
Application.prototype.resetKeys = function(cb) {
|
Application.prototype.resetKeys = function (cb) {
|
||||||
this.clientKey = generateKey('client');
|
this.clientKey = generateKey('client');
|
||||||
this.javaScriptKey = generateKey('javaScript');
|
this.javaScriptKey = generateKey('javaScript');
|
||||||
this.restApiKey = generateKey('restApi');
|
this.restApiKey = generateKey('restApi');
|
||||||
this.windowsKey = generateKey('windows');
|
this.windowsKey = generateKey('windows');
|
||||||
this.masterKey = generateKey('master');
|
this.masterKey = generateKey('master');
|
||||||
this.modified = new Date();
|
this.modified = new Date();
|
||||||
this.save(cb);
|
this.save(cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -155,14 +167,14 @@ Application.prototype.resetKeys = function(cb) {
|
||||||
* @param appId
|
* @param appId
|
||||||
* @param cb
|
* @param cb
|
||||||
*/
|
*/
|
||||||
Application.resetKeys = function(appId, cb) {
|
Application.resetKeys = function (appId, cb) {
|
||||||
Application.findById(appId, function(err, app) {
|
Application.findById(appId, function (err, app) {
|
||||||
if(err) {
|
if (err) {
|
||||||
cb && cb(err, app);
|
cb && cb(err, app);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app.resetKeys(cb);
|
app.resetKeys(cb);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -171,20 +183,20 @@ Application.resetKeys = function(appId, cb) {
|
||||||
* @param key
|
* @param key
|
||||||
* @param cb
|
* @param cb
|
||||||
*/
|
*/
|
||||||
Application.authenticate = function(appId, key, cb) {
|
Application.authenticate = function (appId, key, cb) {
|
||||||
Application.findById(appId, function(err, app) {
|
Application.findById(appId, function (err, app) {
|
||||||
if(err || !app) {
|
if (err || !app) {
|
||||||
cb && cb(err, null);
|
cb && cb(err, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var matched = null;
|
var matched = null;
|
||||||
['clientKey', 'javaScriptKey', 'restApiKey', 'windowsKey', 'masterKey'].forEach(function(k) {
|
['clientKey', 'javaScriptKey', 'restApiKey', 'windowsKey', 'masterKey'].forEach(function (k) {
|
||||||
if(app[k] === key) {
|
if (app[k] === key) {
|
||||||
matched = k;
|
matched = k;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
cb && cb(null, matched);
|
|
||||||
});
|
});
|
||||||
|
cb && cb(null, matched);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Application;
|
module.exports = Application;
|
||||||
|
|
|
@ -3,109 +3,167 @@ var assert = require('assert');
|
||||||
var Application = loopback.Application;
|
var Application = loopback.Application;
|
||||||
|
|
||||||
describe('Application', function () {
|
describe('Application', function () {
|
||||||
var registeredApp = null;
|
var registeredApp = null;
|
||||||
|
|
||||||
it('Create a new application', function (done) {
|
it('Create a new application', function (done) {
|
||||||
Application.create({owner: 'rfeng', name: 'MyApp1', description: 'My first mobile application'}, function (err, result) {
|
Application.create({owner: 'rfeng',
|
||||||
var app = result;
|
name: 'MyApp1',
|
||||||
assert.equal(app.owner, 'rfeng');
|
description: 'My first mobile application'}, function (err, result) {
|
||||||
assert.equal(app.name, 'MyApp1');
|
var app = result;
|
||||||
assert.equal(app.description, 'My first mobile application');
|
assert.equal(app.owner, 'rfeng');
|
||||||
assert(app.clientKey);
|
assert.equal(app.name, 'MyApp1');
|
||||||
assert(app.javaScriptKey);
|
assert.equal(app.description, 'My first mobile application');
|
||||||
assert(app.restApiKey);
|
assert(app.clientKey);
|
||||||
assert(app.windowsKey);
|
assert(app.javaScriptKey);
|
||||||
assert(app.masterKey);
|
assert(app.restApiKey);
|
||||||
assert(app.created);
|
assert(app.windowsKey);
|
||||||
assert(app.modified);
|
assert(app.masterKey);
|
||||||
done(err, result);
|
assert(app.created);
|
||||||
});
|
assert(app.modified);
|
||||||
|
done(err, result);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(function (done) {
|
it('Create a new application with push settings', function (done) {
|
||||||
Application.register('rfeng', 'MyApp2', {description: 'My second mobile application'}, function (err, result) {
|
Application.create({owner: 'rfeng',
|
||||||
var app = result;
|
name: 'MyAppWithPush',
|
||||||
assert.equal(app.owner, 'rfeng');
|
description: 'My push mobile application',
|
||||||
assert.equal(app.name, 'MyApp2');
|
pushSettings: {
|
||||||
assert.equal(app.description, 'My second mobile application');
|
apns: {
|
||||||
assert(app.clientKey);
|
production: false,
|
||||||
assert(app.javaScriptKey);
|
certData: 'cert',
|
||||||
assert(app.restApiKey);
|
keyData: 'key',
|
||||||
assert(app.windowsKey);
|
pushOptions: {
|
||||||
assert(app.masterKey);
|
gateway: 'gateway.sandbox.push.apple.com',
|
||||||
assert(app.created);
|
port: 2195
|
||||||
assert(app.modified);
|
},
|
||||||
registeredApp = app;
|
feedbackOptions: {
|
||||||
done(err, result);
|
gateway: 'feedback.sandbox.push.apple.com',
|
||||||
|
port: 2196,
|
||||||
|
interval: 300,
|
||||||
|
batchFeedback: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gcm: {
|
||||||
|
serverApiKey: 'serverKey'
|
||||||
|
}
|
||||||
|
}},
|
||||||
|
function (err, result) {
|
||||||
|
var app = result;
|
||||||
|
assert.deepEqual(app.pushSettings.toObject(), {
|
||||||
|
apns: {
|
||||||
|
production: false,
|
||||||
|
certData: 'cert',
|
||||||
|
keyData: 'key',
|
||||||
|
pushOptions: {
|
||||||
|
gateway: 'gateway.sandbox.push.apple.com',
|
||||||
|
port: 2195
|
||||||
|
},
|
||||||
|
feedbackOptions: {
|
||||||
|
gateway: 'feedback.sandbox.push.apple.com',
|
||||||
|
port: 2196,
|
||||||
|
interval: 300,
|
||||||
|
batchFeedback: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
gcm: {
|
||||||
|
serverApiKey: 'serverKey'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
done(err, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(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);
|
||||||
|
registeredApp = app;
|
||||||
|
done(err, result);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Reset keys', function (done) {
|
it('Authenticate with application id & clientKey', function (done) {
|
||||||
Application.resetKeys(registeredApp.id, function (err, result) {
|
Application.authenticate(registeredApp.id, registeredApp.clientKey,
|
||||||
var app = result;
|
function (err, result) {
|
||||||
assert.equal(app.owner, 'rfeng');
|
assert.equal(result, 'clientKey');
|
||||||
assert.equal(app.name, 'MyApp2');
|
done(err, result);
|
||||||
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);
|
it('Authenticate with application id & javaScriptKey', function (done) {
|
||||||
assert(app.javaScriptKey !== registeredApp.javaScriptKey);
|
Application.authenticate(registeredApp.id, registeredApp.javaScriptKey,
|
||||||
assert(app.restApiKey !== registeredApp.restApiKey);
|
function (err, result) {
|
||||||
assert(app.windowsKey !== registeredApp.windowsKey);
|
assert.equal(result, 'javaScriptKey');
|
||||||
assert(app.masterKey !== registeredApp.masterKey);
|
done(err, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
assert(app.created);
|
it('Authenticate with application id & restApiKey', function (done) {
|
||||||
assert(app.modified);
|
Application.authenticate(registeredApp.id, registeredApp.restApiKey,
|
||||||
registeredApp = app;
|
function (err, result) {
|
||||||
done(err, result);
|
assert.equal(result, 'restApiKey');
|
||||||
});
|
done(err, result);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Authenticate with application id & clientKey', function (done) {
|
it('Authenticate with application id & masterKey', function (done) {
|
||||||
Application.authenticate(registeredApp.id, registeredApp.clientKey, function (err, result) {
|
Application.authenticate(registeredApp.id, registeredApp.masterKey,
|
||||||
assert.equal(result, 'clientKey');
|
function (err, result) {
|
||||||
done(err, result);
|
assert.equal(result, 'masterKey');
|
||||||
});
|
done(err, result);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Authenticate with application id & javaScriptKey', function (done) {
|
it('Authenticate with application id & windowsKey', function (done) {
|
||||||
Application.authenticate(registeredApp.id, registeredApp.javaScriptKey, function (err, result) {
|
Application.authenticate(registeredApp.id, registeredApp.windowsKey,
|
||||||
assert.equal(result, 'javaScriptKey');
|
function (err, result) {
|
||||||
done(err, result);
|
assert.equal(result, 'windowsKey');
|
||||||
});
|
done(err, result);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Authenticate with application id & restApiKey', function (done) {
|
it('Fail to authenticate with application id & invalid key', function (done) {
|
||||||
Application.authenticate(registeredApp.id, registeredApp.restApiKey, function (err, result) {
|
Application.authenticate(registeredApp.id, 'invalid-key',
|
||||||
assert.equal(result, 'restApiKey');
|
function (err, result) {
|
||||||
done(err, result);
|
assert(!result);
|
||||||
});
|
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue