Merge pull request #635 from strongloop/feature/builtin-models-defined-via-json
Define built-in models via JSON
This commit is contained in:
commit
dcedfa03a1
|
@ -4,24 +4,8 @@
|
|||
|
||||
var loopback = require('../../lib/loopback')
|
||||
, assert = require('assert')
|
||||
, crypto = require('crypto')
|
||||
, uid = require('uid2')
|
||||
, DEFAULT_TTL = 1209600 // 2 weeks in seconds
|
||||
, DEFAULT_TOKEN_LEN = 64
|
||||
, Role = require('./role').Role
|
||||
, ACL = require('./acl').ACL;
|
||||
|
||||
/*!
|
||||
* Default AccessToken properties.
|
||||
*/
|
||||
|
||||
var properties = {
|
||||
id: {type: String, id: true},
|
||||
ttl: {type: Number, ttl: true, default: DEFAULT_TTL}, // time to live in seconds
|
||||
created: {type: Date, default: function() {
|
||||
return new Date();
|
||||
}}
|
||||
};
|
||||
, DEFAULT_TOKEN_LEN = 64;
|
||||
|
||||
/**
|
||||
* Token based authentication and access control.
|
||||
|
@ -32,36 +16,20 @@ var properties = {
|
|||
* - ALLOW EVERYONE create
|
||||
*
|
||||
* @property {String} id Generated token ID
|
||||
* @property {Number} ttl Time to live in seconds
|
||||
* @property {Number} ttl Time to live in seconds, 2 weeks by default.
|
||||
* @property {Date} created When the token was created
|
||||
*
|
||||
* @class
|
||||
* @class AccessToken
|
||||
* @inherits {PersistedModel}
|
||||
*/
|
||||
|
||||
var AccessToken = module.exports =
|
||||
loopback.PersistedModel.extend('AccessToken', properties, {
|
||||
acls: [
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.EVERYONE,
|
||||
permission: 'DENY'
|
||||
},
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.EVERYONE,
|
||||
property: 'create',
|
||||
permission: 'ALLOW'
|
||||
}
|
||||
],
|
||||
relations: {
|
||||
user: {
|
||||
type: 'belongsTo',
|
||||
model: 'User',
|
||||
foreignKey: 'userId'
|
||||
}
|
||||
}
|
||||
});
|
||||
module.exports = function(AccessToken) {
|
||||
|
||||
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||
AccessToken.definition.rawProperties.created.default =
|
||||
AccessToken.definition.properties.created.default = function() {
|
||||
return new Date();
|
||||
};
|
||||
|
||||
/**
|
||||
* Anonymous Token
|
||||
|
@ -232,3 +200,4 @@ function tokenIdForRequest(req, options) {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "AccessToken",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"id": true
|
||||
},
|
||||
"ttl": {
|
||||
"type": "number",
|
||||
"ttl": true,
|
||||
"default": 1209600,
|
||||
"description": "time to live in seconds (2 weeks by default)"
|
||||
},
|
||||
"created": {
|
||||
"type": "Date"
|
||||
}
|
||||
},
|
||||
"relations": {
|
||||
"user": {
|
||||
"type": "belongsTo",
|
||||
"model": "User",
|
||||
"foreignKey": "userId"
|
||||
}
|
||||
},
|
||||
"acls": [
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"property": "create",
|
||||
"permission": "ALLOW"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -41,10 +41,12 @@ var AccessContext = ctx.AccessContext;
|
|||
var Principal = ctx.Principal;
|
||||
var AccessRequest = ctx.AccessRequest;
|
||||
|
||||
var role = require('./role');
|
||||
var Role = role.Role;
|
||||
var Role = loopback.Role;
|
||||
assert(Role, 'Role model must be defined before ACL model');
|
||||
|
||||
/**
|
||||
* A Model for access control meta data.
|
||||
*
|
||||
* System grants permissions to principals (users/applications, can be grouped
|
||||
* into roles).
|
||||
*
|
||||
|
@ -54,18 +56,6 @@ var Role = role.Role;
|
|||
* For a given principal, such as client application and/or user, is it allowed
|
||||
* to access (read/write/execute)
|
||||
* the protected resource?
|
||||
*/
|
||||
var ACLSchema = {
|
||||
model: String, // The name of the model
|
||||
property: String, // The name of the property, method, scope, or relation
|
||||
accessType: String,
|
||||
permission: String,
|
||||
principalType: String,
|
||||
principalId: String
|
||||
};
|
||||
|
||||
/**
|
||||
* A Model for access control meta data.
|
||||
*
|
||||
* @header ACL
|
||||
* @property {String} model Name of the model.
|
||||
|
@ -78,11 +68,12 @@ var ACLSchema = {
|
|||
* - DENY: Explicitly denies access to the resource.
|
||||
* @property {String} principalType Type of the principal; one of: Application, Use, Role.
|
||||
* @property {String} principalId ID of the principal - such as appId, userId or roleId
|
||||
* @class
|
||||
* @inherits Model
|
||||
*
|
||||
* @class ACL
|
||||
* @inherits PersistedModel
|
||||
*/
|
||||
|
||||
var ACL = loopback.PersistedModel.extend('ACL', ACLSchema);
|
||||
module.exports = function(ACL) {
|
||||
|
||||
ACL.ALL = AccessContext.ALL;
|
||||
|
||||
|
@ -468,47 +459,4 @@ ACL.checkAccessForToken = function (token, model, modelId, method, callback) {
|
|||
});
|
||||
};
|
||||
|
||||
/*!
|
||||
* Schema for Scope which represents the permissions that are granted to client
|
||||
* applications by the resource owner
|
||||
*/
|
||||
var ScopeSchema = {
|
||||
name: {type: String, required: true},
|
||||
description: String
|
||||
};
|
||||
|
||||
/**
|
||||
* Resource owner grants/delegates permissions to client applications
|
||||
*
|
||||
* For a protected resource, does the client application have the authorization
|
||||
* from the resource owner (user or system)?
|
||||
*
|
||||
* Scope has many resource access entries
|
||||
* @class
|
||||
*/
|
||||
var Scope = loopback.createModel('Scope', ScopeSchema);
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given scope is allowed to access the model/property
|
||||
* @param {String} scope The scope name
|
||||
* @param {String} model The model name
|
||||
* @param {String} property The property/method/relation name
|
||||
* @param {String} accessType The access type
|
||||
* @callback {Function} callback
|
||||
* @param {String|Error} err The error object
|
||||
* @param {AccessRequest} result The access permission
|
||||
*/
|
||||
Scope.checkPermission = function (scope, model, property, accessType, callback) {
|
||||
this.findOne({where: {name: scope}}, function (err, scope) {
|
||||
if (err) {
|
||||
callback && callback(err);
|
||||
} else {
|
||||
var aclModel = loopback.getModelByType(ACL);
|
||||
aclModel.checkPermission(ACL.SCOPE, scope.id, model, property, accessType, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.ACL = ACL;
|
||||
module.exports.Scope = Scope;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "ACL",
|
||||
"properties": {
|
||||
"model": {
|
||||
"type": "string",
|
||||
"description": "The name of the model"
|
||||
},
|
||||
"property": {
|
||||
"type": "string",
|
||||
"description": "The name of the property, method, scope, or relation"
|
||||
},
|
||||
"accessType": "string",
|
||||
"permission": "string",
|
||||
"principalType": "string",
|
||||
"principalId": "string"
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
|
@ -123,6 +33,9 @@ function generateKey(hmacKey, algorithm, encoding) {
|
|||
* @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`
|
||||
|
@ -136,11 +49,33 @@ function generateKey(hmacKey, algorithm, encoding) {
|
|||
* @property {Number} pushSettings.apns.feedbackOptions.interval (APNS).
|
||||
* @property {String} pushSettings.gcm.serverApiKey: Google Cloud Messaging API key.
|
||||
*
|
||||
* @class
|
||||
* @inherits {Model}
|
||||
* @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}
|
||||
*/
|
||||
|
||||
var Application = loopback.PersistedModel.extend('Application', ApplicationSchema);
|
||||
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
|
||||
|
@ -249,6 +184,4 @@ Application.authenticate = function (appId, key, cb) {
|
|||
cb && cb(null, result);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = Application;
|
||||
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -10,26 +10,6 @@ var PersistedModel = require('../../lib/loopback').PersistedModel
|
|||
, assert = require('assert')
|
||||
, debug = require('debug')('loopback:change');
|
||||
|
||||
/*!
|
||||
* Properties
|
||||
*/
|
||||
|
||||
var properties = {
|
||||
id: {type: String, id: true},
|
||||
rev: {type: String},
|
||||
prev: {type: String},
|
||||
checkpoint: {type: Number},
|
||||
modelName: {type: String},
|
||||
modelId: {type: String}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Options
|
||||
*/
|
||||
|
||||
var options = {
|
||||
trackChanges: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Change list entry.
|
||||
|
@ -41,11 +21,11 @@ var options = {
|
|||
* @property {String} modelName Model name
|
||||
* @property {String} modelId Model ID
|
||||
*
|
||||
* @class
|
||||
* @inherits {Model}
|
||||
* @class Change
|
||||
* @inherits {PersistedModel}
|
||||
*/
|
||||
|
||||
var Change = module.exports = PersistedModel.extend('Change', properties, options);
|
||||
module.exports = function(Change) {
|
||||
|
||||
/*!
|
||||
* Constants
|
||||
|
@ -441,7 +421,7 @@ Change.rectifyAll = function(cb) {
|
|||
Change.getCheckpointModel = function() {
|
||||
var checkpointModel = this.Checkpoint;
|
||||
if (checkpointModel) return checkpointModel;
|
||||
this.checkpoint = checkpointModel = require('./checkpoint').extend('checkpoint');
|
||||
this.checkpoint = checkpointModel = loopback.Checkpoint.extend('checkpoint');
|
||||
assert(this.dataSource, 'Cannot getCheckpointModel(): ' + this.modelName
|
||||
+ ' is not attached to a dataSource');
|
||||
checkpointModel.attachTo(this.dataSource);
|
||||
|
@ -501,6 +481,7 @@ Change.prototype.getModel = function(callback) {
|
|||
* @param {PersistedModel} TargetModel
|
||||
* @property {ModelClass} source The source model instance
|
||||
* @property {ModelClass} target The target model instance
|
||||
* @class Change.Conflict
|
||||
*/
|
||||
|
||||
function Conflict(modelId, SourceModel, TargetModel) {
|
||||
|
@ -644,3 +625,4 @@ Conflict.prototype.type = function(cb) {
|
|||
return cb(null, Change.UNKNOWN);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "Change",
|
||||
"trackChanges": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"id": true
|
||||
},
|
||||
"rev": {
|
||||
"type": "string"
|
||||
},
|
||||
"prev": {
|
||||
"type": "string"
|
||||
},
|
||||
"checkpoint": {
|
||||
"type": "number"
|
||||
},
|
||||
"modelName": {
|
||||
"type": "string"
|
||||
},
|
||||
"modelId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,27 +2,7 @@
|
|||
* Module Dependencies.
|
||||
*/
|
||||
|
||||
var PersistedModel = require('../../lib/loopback').PersistedModel
|
||||
, loopback = require('../../lib/loopback')
|
||||
, assert = require('assert');
|
||||
|
||||
/**
|
||||
* Properties
|
||||
*/
|
||||
|
||||
var properties = {
|
||||
seq: {type: Number},
|
||||
time: {type: Date, default: Date},
|
||||
sourceId: {type: String}
|
||||
};
|
||||
|
||||
/**
|
||||
* Options
|
||||
*/
|
||||
|
||||
var options = {
|
||||
|
||||
};
|
||||
var assert = require('assert');
|
||||
|
||||
/**
|
||||
* Checkpoint list entry.
|
||||
|
@ -31,11 +11,17 @@ var options = {
|
|||
* @property time {Number} the time when the checkpoint was created
|
||||
* @property sourceId {String} the source identifier
|
||||
*
|
||||
* @class
|
||||
* @class Checkpoint
|
||||
* @inherits {PersistedModel}
|
||||
*/
|
||||
|
||||
var Checkpoint = module.exports = PersistedModel.extend('Checkpoint', properties, options);
|
||||
module.exports = function(Checkpoint) {
|
||||
|
||||
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||
Checkpoint.definition.rawProperties.time.default =
|
||||
Checkpoint.definition.properties.time.default = function() {
|
||||
return new Date();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current checkpoint id
|
||||
|
@ -74,4 +60,4 @@ Checkpoint.beforeSave = function(next, model) {
|
|||
next();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "Checkpoint",
|
||||
"properties": {
|
||||
"seq": {
|
||||
"type": "number"
|
||||
},
|
||||
"time": {
|
||||
"type": "date"
|
||||
},
|
||||
"sourceId": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,3 @@
|
|||
/*!
|
||||
* Module Dependencies.
|
||||
*/
|
||||
|
||||
var Model = require('../../lib/loopback').Model
|
||||
, loopback = require('../../lib/loopback');
|
||||
|
||||
var properties = {
|
||||
to: {type: String, required: true},
|
||||
from: {type: String, required: true},
|
||||
subject: {type: String, required: true},
|
||||
text: {type: String},
|
||||
html: {type: String}
|
||||
};
|
||||
|
||||
/**
|
||||
* @property {String} to Email addressee. Required.
|
||||
* @property {String} from Email sender address. Required.
|
||||
|
@ -20,11 +5,11 @@ var properties = {
|
|||
* @property {String} text Text body of email.
|
||||
* @property {String} html HTML body of email.
|
||||
*
|
||||
* @class
|
||||
* @class Email
|
||||
* @inherits {Model}
|
||||
*/
|
||||
|
||||
var Email = module.exports = Model.extend('Email', properties);
|
||||
module.exports = function(Email) {
|
||||
|
||||
/**
|
||||
* Send an email with the given `options`.
|
||||
|
@ -52,6 +37,14 @@ var Email = module.exports = Model.extend('Email', properties);
|
|||
* @param {Function} callback Called after the e-mail is sent or the sending failed
|
||||
*/
|
||||
|
||||
Email.send = function() {
|
||||
throw new Error('You must connect the Email Model to a Mail connector');
|
||||
};
|
||||
|
||||
/**
|
||||
* A shortcut for Email.send(this).
|
||||
*/
|
||||
Email.prototype.send = function() {
|
||||
throw new Error('You must connect the Email Model to a Mail connector');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "Email",
|
||||
"base": "Model",
|
||||
"properties": {
|
||||
"to": {"type": "String", "required": true},
|
||||
"from": {"type": "String", "required": true},
|
||||
"subject": {"type": "String", "required": true},
|
||||
"text": {"type": "String"},
|
||||
"html": {"type": "String"}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* The `RoleMapping` model extends from the built in `loopback.Model` type.
|
||||
*
|
||||
* @property {String} id Generated ID.
|
||||
* @property {String} name Name of the role.
|
||||
* @property {String} Description Text description.
|
||||
*
|
||||
* @class RoleMapping
|
||||
* @inherits {PersistedModel}
|
||||
*/
|
||||
|
||||
module.exports = function(RoleMapping) {
|
||||
// Principal types
|
||||
RoleMapping.USER = 'USER';
|
||||
RoleMapping.APP = RoleMapping.APPLICATION = 'APP';
|
||||
RoleMapping.ROLE = 'ROLE';
|
||||
|
||||
/**
|
||||
* Get the application principal
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err
|
||||
* @param {Application} application
|
||||
*/
|
||||
RoleMapping.prototype.application = function (callback) {
|
||||
if (this.principalType === RoleMapping.APPLICATION) {
|
||||
var applicationModel = this.constructor.Application
|
||||
|| loopback.getModelByType(loopback.Application);
|
||||
applicationModel.findById(this.principalId, callback);
|
||||
} else {
|
||||
process.nextTick(function () {
|
||||
callback && callback(null, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the user principal
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err
|
||||
* @param {User} user
|
||||
*/
|
||||
RoleMapping.prototype.user = function (callback) {
|
||||
if (this.principalType === RoleMapping.USER) {
|
||||
var userModel = this.constructor.User
|
||||
|| loopback.getModelByType(loopback.User);
|
||||
userModel.findById(this.principalId, callback);
|
||||
} else {
|
||||
process.nextTick(function () {
|
||||
callback && callback(null, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the child role principal
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err
|
||||
* @param {User} childUser
|
||||
*/
|
||||
RoleMapping.prototype.childRole = function (callback) {
|
||||
if (this.principalType === RoleMapping.ROLE) {
|
||||
var roleModel = this.constructor.Role || loopback.getModelByType(Role);
|
||||
roleModel.findById(this.principalId, callback);
|
||||
} else {
|
||||
process.nextTick(function () {
|
||||
callback && callback(null, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "RoleMapping",
|
||||
"description": "Map principals to roles",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"id": true,
|
||||
"generated": true
|
||||
},
|
||||
"principalType": {
|
||||
"type": "string",
|
||||
"description": "The principal type, such as user, application, or role"
|
||||
},
|
||||
"principalId": "string"
|
||||
},
|
||||
"relations": {
|
||||
"role": {
|
||||
"type": "belongsTo",
|
||||
"model": "Role",
|
||||
"foreignKey": "roleId"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,118 +5,26 @@ var async = require('async');
|
|||
|
||||
var AccessContext = require('../../lib/access-context').AccessContext;
|
||||
|
||||
// Role model
|
||||
var RoleSchema = {
|
||||
id: {type: String, id: true, generated: true}, // Id
|
||||
name: {type: String, required: true}, // The name of a role
|
||||
description: String, // Description
|
||||
|
||||
// Timestamps
|
||||
created: {type: Date, default: Date},
|
||||
modified: {type: Date, default: Date}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Map principals to roles
|
||||
*/
|
||||
var RoleMappingSchema = {
|
||||
id: {type: String, id: true, generated: true}, // Id
|
||||
// roleId: String, // The role id, to be injected by the belongsTo relation
|
||||
principalType: String, // The principal type, such as user, application, or role
|
||||
principalId: String // The principal id
|
||||
};
|
||||
|
||||
/**
|
||||
* The `RoleMapping` model extends from the built in `loopback.Model` type.
|
||||
*
|
||||
* @class
|
||||
* @property {String} id Generated ID.
|
||||
* @property {String} name Name of the role.
|
||||
* @property {String} Description Text description.
|
||||
* @inherits {Model}
|
||||
*/
|
||||
|
||||
var RoleMapping = loopback.createModel('RoleMapping', RoleMappingSchema, {
|
||||
relations: {
|
||||
role: {
|
||||
type: 'belongsTo',
|
||||
model: 'Role',
|
||||
foreignKey: 'roleId'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Principal types
|
||||
RoleMapping.USER = 'USER';
|
||||
RoleMapping.APP = RoleMapping.APPLICATION = 'APP';
|
||||
RoleMapping.ROLE = 'ROLE';
|
||||
|
||||
/**
|
||||
* Get the application principal
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err
|
||||
* @param {Application} application
|
||||
*/
|
||||
RoleMapping.prototype.application = function (callback) {
|
||||
if (this.principalType === RoleMapping.APPLICATION) {
|
||||
var applicationModel = this.constructor.Application
|
||||
|| loopback.getModelByType(loopback.Application);
|
||||
applicationModel.findById(this.principalId, callback);
|
||||
} else {
|
||||
process.nextTick(function () {
|
||||
callback && callback(null, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the user principal
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err
|
||||
* @param {User} user
|
||||
*/
|
||||
RoleMapping.prototype.user = function (callback) {
|
||||
if (this.principalType === RoleMapping.USER) {
|
||||
var userModel = this.constructor.User
|
||||
|| loopback.getModelByType(loopback.User);
|
||||
userModel.findById(this.principalId, callback);
|
||||
} else {
|
||||
process.nextTick(function () {
|
||||
callback && callback(null, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the child role principal
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err
|
||||
* @param {User} childUser
|
||||
*/
|
||||
RoleMapping.prototype.childRole = function (callback) {
|
||||
if (this.principalType === RoleMapping.ROLE) {
|
||||
var roleModel = this.constructor.Role || loopback.getModelByType(Role);
|
||||
roleModel.findById(this.principalId, callback);
|
||||
} else {
|
||||
process.nextTick(function () {
|
||||
callback && callback(null, null);
|
||||
});
|
||||
}
|
||||
};
|
||||
var RoleMapping = loopback.RoleMapping;
|
||||
assert(RoleMapping, 'RoleMapping model must be defined before Role model');
|
||||
|
||||
/**
|
||||
* The Role Model
|
||||
* @class
|
||||
* @class Role
|
||||
*/
|
||||
var Role = loopback.createModel('Role', RoleSchema, {
|
||||
relations: {
|
||||
principals: {
|
||||
type: 'hasMany',
|
||||
model: 'RoleMapping',
|
||||
foreignKey: 'roleId'
|
||||
}
|
||||
}
|
||||
});
|
||||
module.exports = function(Role) {
|
||||
|
||||
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||
Role.definition.rawProperties.created.default =
|
||||
Role.definition.properties.created.default = function() {
|
||||
return new Date();
|
||||
};
|
||||
|
||||
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||
Role.definition.rawProperties.modified.default =
|
||||
Role.definition.properties.modified.default = function() {
|
||||
return new Date();
|
||||
};
|
||||
|
||||
// Set up the connection to users/applications/roles once the model
|
||||
Role.once('dataSourceAttached', function() {
|
||||
|
@ -478,11 +386,4 @@ Role.getRoles = function (context, callback) {
|
|||
callback && callback(err, roles);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
Role: Role,
|
||||
RoleMapping: RoleMapping
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "Role",
|
||||
"properties": {
|
||||
|
||||
"id": {
|
||||
"type": "string",
|
||||
"id": true,
|
||||
"generated": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"description": "string",
|
||||
|
||||
"created": "date",
|
||||
"modified": "date"
|
||||
},
|
||||
"relations": {
|
||||
"principals": {
|
||||
"type": "hasMany",
|
||||
"model": "RoleMapping",
|
||||
"foreignKey": "roleId"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
var assert = require('assert');
|
||||
|
||||
/**
|
||||
* Resource owner grants/delegates permissions to client applications
|
||||
*
|
||||
* For a protected resource, does the client application have the authorization
|
||||
* from the resource owner (user or system)?
|
||||
*
|
||||
* Scope has many resource access entries
|
||||
*
|
||||
* @class Scope
|
||||
*/
|
||||
|
||||
module.exports = function(Scope) {
|
||||
/**
|
||||
* Check if the given scope is allowed to access the model/property
|
||||
* @param {String} scope The scope name
|
||||
* @param {String} model The model name
|
||||
* @param {String} property The property/method/relation name
|
||||
* @param {String} accessType The access type
|
||||
* @callback {Function} callback
|
||||
* @param {String|Error} err The error object
|
||||
* @param {AccessRequest} result The access permission
|
||||
*/
|
||||
Scope.checkPermission = function (scope, model, property, accessType, callback) {
|
||||
var ACL = loopback.ACL;
|
||||
assert(ACL,
|
||||
'ACL model must be defined before Scope.checkPermission is called');
|
||||
|
||||
this.findOne({where: {name: scope}}, function (err, scope) {
|
||||
if (err) {
|
||||
callback && callback(err);
|
||||
} else {
|
||||
var aclModel = loopback.getModelByType(ACL);
|
||||
aclModel.checkPermission(ACL.SCOPE, scope.id, model, property, accessType, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "Scope",
|
||||
"description": [
|
||||
"Schema for Scope which represents the permissions that are granted",
|
||||
"to client applications by the resource owner"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"description": "string"
|
||||
}
|
||||
}
|
|
@ -2,107 +2,18 @@
|
|||
* Module Dependencies.
|
||||
*/
|
||||
|
||||
var PersistedModel = require('../../lib/loopback').PersistedModel
|
||||
, loopback = require('../../lib/loopback')
|
||||
var loopback = require('../../lib/loopback')
|
||||
, path = require('path')
|
||||
, SALT_WORK_FACTOR = 10
|
||||
, crypto = require('crypto')
|
||||
, bcrypt = require('bcryptjs')
|
||||
, BaseAccessToken = require('./access-token')
|
||||
, DEFAULT_TTL = 1209600 // 2 weeks in seconds
|
||||
, DEFAULT_RESET_PW_TTL = 15 * 60 // 15 mins in seconds
|
||||
, DEFAULT_MAX_TTL = 31556926 // 1 year in seconds
|
||||
, Role = require('./role').Role
|
||||
, ACL = require('./acl').ACL
|
||||
, assert = require('assert');
|
||||
|
||||
var debug = require('debug')('loopback:user');
|
||||
|
||||
/*!
|
||||
* Default User properties.
|
||||
*/
|
||||
|
||||
var properties = {
|
||||
realm: {type: String},
|
||||
username: {type: String},
|
||||
password: {type: String, required: true},
|
||||
credentials: Object, // deprecated, to be removed in 2.x
|
||||
challenges: Object, // deprecated, to be removed in 2.x
|
||||
email: {type: String, required: true},
|
||||
emailVerified: Boolean,
|
||||
verificationToken: String,
|
||||
status: String,
|
||||
created: Date,
|
||||
lastUpdated: Date
|
||||
};
|
||||
|
||||
var options = {
|
||||
hidden: ['password'],
|
||||
acls: [
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.EVERYONE,
|
||||
permission: ACL.DENY
|
||||
},
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.EVERYONE,
|
||||
permission: ACL.ALLOW,
|
||||
property: 'create'
|
||||
},
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.OWNER,
|
||||
permission: ACL.ALLOW,
|
||||
property: 'deleteById'
|
||||
},
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.EVERYONE,
|
||||
permission: ACL.ALLOW,
|
||||
property: "login"
|
||||
},
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.EVERYONE,
|
||||
permission: ACL.ALLOW,
|
||||
property: "logout"
|
||||
},
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.OWNER,
|
||||
permission: ACL.ALLOW,
|
||||
property: "findById"
|
||||
},
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.OWNER,
|
||||
permission: ACL.ALLOW,
|
||||
property: "updateAttributes"
|
||||
},
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.EVERYONE,
|
||||
permission: ACL.ALLOW,
|
||||
property: "confirm"
|
||||
},
|
||||
{
|
||||
principalType: ACL.ROLE,
|
||||
principalId: Role.EVERYONE,
|
||||
permission: ACL.ALLOW,
|
||||
property: "resetPassword",
|
||||
accessType: ACL.EXECUTE
|
||||
}
|
||||
],
|
||||
relations: {
|
||||
accessTokens: {
|
||||
type: 'hasMany',
|
||||
model: 'AccessToken',
|
||||
foreignKey: 'userId'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends from the built in `loopback.Model` type.
|
||||
*
|
||||
|
@ -122,11 +33,11 @@ var options = {
|
|||
* @property {Boolean} emailVerified Set when a user's email has been verified via `confirm()`
|
||||
* @property {String} verificationToken Set when `verify()` is called
|
||||
*
|
||||
* @class
|
||||
* @inherits {Model}
|
||||
* @class User
|
||||
* @inherits {PersistedModel}
|
||||
*/
|
||||
|
||||
var User = module.exports = PersistedModel.extend('User', properties, options);
|
||||
module.exports = function(User) {
|
||||
|
||||
/**
|
||||
* Create access token for the logged in user. This method can be overridden to
|
||||
|
@ -177,7 +88,6 @@ User.login = function (credentials, include, fn) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
var query = {};
|
||||
if (credentials.email) {
|
||||
query.email = credentials.email;
|
||||
|
@ -464,7 +374,7 @@ User.resetPassword = function(options, cb) {
|
|||
|
||||
User.setup = function() {
|
||||
// We need to call the base class's setup method
|
||||
PersistedModel.setup.call(this);
|
||||
User.base.setup.call(this);
|
||||
var UserModel = this;
|
||||
|
||||
// max ttl
|
||||
|
@ -491,13 +401,11 @@ User.setup = function () {
|
|||
description: 'Login a user with username/email and password',
|
||||
accepts: [
|
||||
{arg: 'credentials', type: 'object', required: true, http: {source: 'body'}},
|
||||
{arg: 'include', type: 'string', http: {source: 'query' }, description:
|
||||
'Related objects to include in the response. ' +
|
||||
{arg: 'include', type: 'string', http: {source: 'query' }, description: 'Related objects to include in the response. ' +
|
||||
'See the description of return value for more details.'}
|
||||
],
|
||||
returns: {
|
||||
arg: 'accessToken', type: 'object', root: true, description:
|
||||
'The response body contains properties of the AccessToken created on login.\n' +
|
||||
arg: 'accessToken', type: 'object', root: true, description: 'The response body contains properties of the AccessToken created on login.\n' +
|
||||
'Depending on the value of `include` parameter, the body may contain ' +
|
||||
'additional properties:\n\n' +
|
||||
' - `user` - `{User}` - Data of the currently logged in user. (`include=user`)\n\n'
|
||||
|
@ -517,8 +425,7 @@ User.setup = function () {
|
|||
var tokenID = accessToken && accessToken.id;
|
||||
|
||||
return tokenID;
|
||||
}, description:
|
||||
'Do not supply this argument, it is automatically extracted ' +
|
||||
}, description: 'Do not supply this argument, it is automatically extracted ' +
|
||||
'from request headers.'
|
||||
}
|
||||
],
|
||||
|
@ -561,8 +468,11 @@ User.setup = function () {
|
|||
});
|
||||
|
||||
// default models
|
||||
UserModel.email = require('./email');
|
||||
UserModel.accessToken = require('./access-token');
|
||||
assert(loopback.Email, 'Email model must be defined before User model');
|
||||
UserModel.email = loopback.Email;
|
||||
|
||||
assert(loopback.AccessToken, 'AccessToken model must be defined before User model');
|
||||
UserModel.accessToken = loopback.AccessToken;
|
||||
|
||||
// email validation regex
|
||||
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
|
@ -579,3 +489,5 @@ User.setup = function () {
|
|||
*/
|
||||
|
||||
User.setup();
|
||||
|
||||
};
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
{
|
||||
"name": "User",
|
||||
"properties": {
|
||||
"realm": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"credentials": {
|
||||
"type": "object",
|
||||
"deprecated": true
|
||||
},
|
||||
"challenges": {
|
||||
"type": "object",
|
||||
"deprecated": true
|
||||
},
|
||||
"email": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"emailVerified": "boolean",
|
||||
"verificationToken": "string",
|
||||
"status": "string",
|
||||
"created": "date",
|
||||
"lastUpdated": "date"
|
||||
},
|
||||
"hidden": ["password"],
|
||||
"acls": [
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "DENY"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "create"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$owner",
|
||||
"permission": "ALLOW",
|
||||
"property": "deleteById"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "login"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "logout"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$owner",
|
||||
"permission": "ALLOW",
|
||||
"property": "findById"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$owner",
|
||||
"permission": "ALLOW",
|
||||
"property": "updateAttributes"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ACL.ALLOW",
|
||||
"property": "confirm"
|
||||
},
|
||||
{
|
||||
"principalType": "ROLE",
|
||||
"principalId": "$everyone",
|
||||
"permission": "ALLOW",
|
||||
"property": "resetPassword",
|
||||
"accessType": "EXECUTE"
|
||||
}
|
||||
],
|
||||
"relations": {
|
||||
"accessTokens": {
|
||||
"type": "hasMany",
|
||||
"model": "AccessToken",
|
||||
"foreignKey": "userId"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,8 +16,10 @@
|
|||
{ "title": "Built-in models", "depth": 2 },
|
||||
"common/models/access-token.js",
|
||||
"common/models/acl.js",
|
||||
"common/models/scope.js",
|
||||
"common/models/application.js",
|
||||
"common/models/email.js",
|
||||
"common/models/role-mapping.js",
|
||||
"common/models/role.js",
|
||||
"common/models/user.js",
|
||||
"common/models/change.js"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
var loopback = require('./loopback');
|
||||
var AccessToken = require('../common/models/access-token');
|
||||
var debug = require('debug')('loopback:security:access-context');
|
||||
|
||||
/**
|
||||
|
@ -49,7 +48,9 @@ function AccessContext(context) {
|
|||
}
|
||||
|
||||
this.accessType = context.accessType || AccessContext.ALL;
|
||||
this.accessToken = context.accessToken || AccessToken.ANONYMOUS;
|
||||
assert(loopback.AccessToken,
|
||||
'AccessToken model must be defined before AccessContext model');
|
||||
this.accessToken = context.accessToken || loopback.AccessToken.ANONYMOUS;
|
||||
|
||||
var principalType = context.principalType || Principal.USER;
|
||||
var principalId = context.principalId || undefined;
|
||||
|
@ -280,7 +281,7 @@ AccessRequest.prototype.exactlyMatches = function(acl) {
|
|||
*/
|
||||
|
||||
AccessRequest.prototype.isAllowed = function() {
|
||||
return this.permission !== require('../common/models/acl').ACL.DENY;
|
||||
return this.permission !== loopback.ACL.DENY;
|
||||
}
|
||||
|
||||
AccessRequest.prototype.debug = function() {
|
||||
|
|
|
@ -1,13 +1,45 @@
|
|||
module.exports = function(loopback) {
|
||||
loopback.Email = require('../common/models/email');
|
||||
loopback.User = require('../common/models/user');
|
||||
loopback.Application = require('../common/models/application');
|
||||
loopback.AccessToken = require('../common/models/access-token');
|
||||
loopback.Role = require('../common/models/role').Role;
|
||||
loopback.RoleMapping = require('../common/models/role').RoleMapping;
|
||||
loopback.ACL = require('../common/models/acl').ACL;
|
||||
loopback.Scope = require('../common/models/acl').Scope;
|
||||
loopback.Change = require('../common/models/change');
|
||||
// NOTE(bajtos) we must use static require() due to browserify limitations
|
||||
|
||||
loopback.Email = createModel(
|
||||
require('../common/models/email.json'),
|
||||
require('../common/models/email.js'));
|
||||
|
||||
loopback.Application = createModel(
|
||||
require('../common/models/application.json'),
|
||||
require('../common/models/application.js'));
|
||||
|
||||
loopback.AccessToken = createModel(
|
||||
require('../common/models/access-token.json'),
|
||||
require('../common/models/access-token.js'));
|
||||
|
||||
loopback.RoleMapping = createModel(
|
||||
require('../common/models/role-mapping.json'),
|
||||
require('../common/models/role-mapping.js'));
|
||||
|
||||
loopback.Role = createModel(
|
||||
require('../common/models/role.json'),
|
||||
require('../common/models/role.js'));
|
||||
|
||||
loopback.ACL = createModel(
|
||||
require('../common/models/acl.json'),
|
||||
require('../common/models/acl.js'));
|
||||
|
||||
loopback.Scope = createModel(
|
||||
require('../common/models/scope.json'),
|
||||
require('../common/models/scope.js'));
|
||||
|
||||
loopback.User = createModel(
|
||||
require('../common/models/user.json'),
|
||||
require('../common/models/user.js'));
|
||||
|
||||
loopback.Change = createModel(
|
||||
require('../common/models/change.json'),
|
||||
require('../common/models/change.js'));
|
||||
|
||||
loopback.Checkpoint = createModel(
|
||||
require('../common/models/checkpoint.json'),
|
||||
require('../common/models/checkpoint.js'));
|
||||
|
||||
/*!
|
||||
* Automatically attach these models to dataSources
|
||||
|
@ -27,4 +59,10 @@ module.exports = function(loopback) {
|
|||
loopback.ACL.autoAttach = dataSourceTypes.DB;
|
||||
loopback.Scope.autoAttach = dataSourceTypes.DB;
|
||||
loopback.Application.autoAttach = dataSourceTypes.DB;
|
||||
|
||||
function createModel(definitionJson, customizeFn) {
|
||||
var Model = loopback.createModel(definitionJson);
|
||||
customizeFn(Model);
|
||||
return Model;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -960,7 +960,10 @@ PersistedModel.enableChangeTracking = function() {
|
|||
}
|
||||
|
||||
PersistedModel._defineChangeModel = function() {
|
||||
var BaseChangeModel = require('./../common/models/change');
|
||||
var BaseChangeModel = loopback.Change;
|
||||
assert(BaseChangeModel,
|
||||
'Change model must be defined before enabling change replication');
|
||||
|
||||
return this.Change = BaseChangeModel.extend(this.modelName + '-change',
|
||||
{},
|
||||
{
|
||||
|
|
|
@ -109,8 +109,6 @@ describe('AccessToken', function () {
|
|||
});
|
||||
|
||||
describe('app.enableAuth()', function() {
|
||||
this.timeout(0);
|
||||
|
||||
beforeEach(createTestingToken);
|
||||
|
||||
it('prevents remote call with 401 status on denied ACL', function (done) {
|
||||
|
|
|
@ -2,7 +2,7 @@ var async = require('async');
|
|||
var loopback = require('../');
|
||||
|
||||
// create a unique Checkpoint model
|
||||
var Checkpoint = require('../common/models/checkpoint').extend('TestCheckpoint');
|
||||
var Checkpoint = loopback.Checkpoint.extend('TestCheckpoint');
|
||||
Checkpoint.attachTo(loopback.memory());
|
||||
|
||||
describe('Checkpoint', function() {
|
||||
|
|
|
@ -16,7 +16,9 @@ module.exports = function(config) {
|
|||
files: [
|
||||
'node_modules/es5-shim/es5-shim.js',
|
||||
'test/support.js',
|
||||
'test/loopback.test.js',
|
||||
'test/model.test.js',
|
||||
'test/model.application.test.js',
|
||||
'test/geo-point.test.js',
|
||||
'test/app.test.js'
|
||||
],
|
||||
|
|
|
@ -241,4 +241,28 @@ describe('loopback', function() {
|
|||
expect(owner._targetClass).to.equal('User');
|
||||
});
|
||||
});
|
||||
|
||||
describe('loopback object', function() {
|
||||
it('exports all built-in models', function() {
|
||||
var expectedModelNames = [
|
||||
'Email',
|
||||
'User',
|
||||
'Application',
|
||||
'AccessToken',
|
||||
'Role',
|
||||
'RoleMapping',
|
||||
'ACL',
|
||||
'Scope',
|
||||
'Change',
|
||||
'Checkpoint'
|
||||
];
|
||||
|
||||
expect(Object.keys(loopback)).to.include.members(expectedModelNames);
|
||||
|
||||
expectedModelNames.forEach(function(name) {
|
||||
expect(loopback[name], name).to.be.a('function');
|
||||
expect(loopback[name].modelName, name + '.modelName').to.eql(name);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -600,4 +600,16 @@ describe('User', function(){
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ctor', function() {
|
||||
it('exports default Email model', function() {
|
||||
expect(User.email, 'User.email').to.be.a('function');
|
||||
expect(User.email.modelName, 'modelName').to.eql('email');
|
||||
});
|
||||
|
||||
it('exports default AccessToken model', function() {
|
||||
expect(User.accessToken, 'User.accessToken').to.be.a('function');
|
||||
expect(User.accessToken.modelName, 'modelName').to.eql('AccessToken');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue