diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..a43e8510 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,194 @@ + +### Contributing ### + +Thank you for your interest in `loopback`, an open source project +administered by StrongLoop. + +Contributing to loopback is easy. In a few simple steps: + + * Ensure that your effort is aligned with the project’s roadmap by + talking to the maintainers, especially if you are going to spend a + lot of time on it. This project is currently maintained by + [@ritch](https://github.com/ritch), [@raymondfeng](https://github.com/raymondfeng), + and [@bajtos](https://github.com/bajtos). The preferred channel of communication + is [LoopBack Forum](https://groups.google.com/forum/#!forum/loopbackjs) or + [Github Issues](https://github.com/strongloop/loopback/issues). + + * Make something better or fix a bug. + + * Adhere to code style outlined in the + [Google Javascript Style Guide][]. + + * [Sign your patches](#signing-patches) to indicate that your are + making your contribution available under the terms of the + [Contributor License Agreement](#contributor-license-agreement). + + * Submit a pull request through Github. + + +### Signing patches ### + +Like many open source projects, we need a contributor license agreement +from you before we can merge in your changes. + +In summary, by submitting your code, you are granting us a right to use +that code under the terms of this Agreement, including providing it to +others. You are also certifying that you wrote it, and that you are +allowed to license it to us. You are not giving up your copyright in +your work. The license does not change your rights to use your own +contributions for any other purpose. + +Contributor License Agreements are important because they define the +chain of ownership of a piece of software. Some companies won't allow +the use of free software without clear agreements around code ownership. +That's why many open source projects collect similar agreements from +contributors. The CLA here is based on the Apache CLA. + +To signify your agreement to these terms, add the following line to the +bottom of your commit message. Use your real name and an actual e-mail +address. + +``` +Signed-off-by: Random J Developer +``` + +Alternatively you can use the git command line to automatically add this +line, as follows: + +``` +$ git commit -sm "Replace rainbows by unicorns" +``` + + +### Contributor License Agreement ### + +``` + Individual Contributor License Agreement + + By signing this Individual Contributor License Agreement + ("Agreement"), and making a Contribution (as defined below) to + StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and + agree to the following terms and conditions for Your present and + future Contributions submitted to StrongLoop. Except for the license + granted in this Agreement to StrongLoop and recipients of software + distributed by StrongLoop, You reserve all right, title, and interest + in and to Your Contributions. + + 1. Definitions + + "You" or "Your" shall mean the copyright owner or the individual + authorized by the copyright owner that is entering into this + Agreement with StrongLoop. + + "Contribution" shall mean any original work of authorship, + including any modifications or additions to an existing work, that + is intentionally submitted by You to StrongLoop for inclusion in, + or documentation of, any of the products owned or managed by + StrongLoop ("Work"). For purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication + sent to StrongLoop or its representatives, including but not + limited to communication or electronic mailing lists, source code + control systems, and issue tracking systems that are managed by, + or on behalf of, StrongLoop for the purpose of discussing and + improving the Work, but excluding communication that is + conspicuously marked or otherwise designated in writing by You as + "Not a Contribution." + + 2. You Grant a Copyright License to StrongLoop + + Subject to the terms and conditions of this Agreement, You hereby + grant to StrongLoop and recipients of software distributed by + StrongLoop, a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable copyright license to reproduce, prepare + derivative works of, publicly display, publicly perform, + sublicense, and distribute Your Contributions and such derivative + works under any license and without any restrictions. + + 3. You Grant a Patent License to StrongLoop + + Subject to the terms and conditions of this Agreement, You hereby + grant to StrongLoop and to recipients of software distributed by + StrongLoop a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable (except as stated in this Section) + patent license to make, have made, use, offer to sell, sell, + import, and otherwise transfer the Work under any license and + without any restrictions. The patent license You grant to + StrongLoop under this Section applies only to those patent claims + licensable by You that are necessarily infringed by Your + Contributions(s) alone or by combination of Your Contributions(s) + with the Work to which such Contribution(s) was submitted. If any + entity institutes a patent litigation against You or any other + entity (including a cross-claim or counterclaim in a lawsuit) + alleging that Your Contribution, or the Work to which You have + contributed, constitutes direct or contributory patent + infringement, any patent licenses granted to that entity under + this Agreement for that Contribution or Work shall terminate as + of the date such litigation is filed. + + 4. You Have the Right to Grant Licenses to StrongLoop + + You represent that You are legally entitled to grant the licenses + in this Agreement. + + If Your employer(s) has rights to intellectual property that You + create, You represent that You have received permission to make + the Contributions on behalf of that employer, that Your employer + has waived such rights for Your Contributions, or that Your + employer has executed a separate Corporate Contributor License + Agreement with StrongLoop. + + 5. The Contributions Are Your Original Work + + You represent that each of Your Contributions are Your original + works of authorship (see Section 8 (Submissions on Behalf of + Others) for submission on behalf of others). You represent that to + Your knowledge, no other person claims, or has the right to claim, + any right in any intellectual property right related to Your + Contributions. + + You also represent that You are not legally obligated, whether by + entering into an agreement or otherwise, in any way that conflicts + with the terms of this Agreement. + + You represent that Your Contribution submissions include complete + details of any third-party license or other restriction (including, + but not limited to, related patents and trademarks) of which You + are personally aware and which are associated with any part of + Your Contributions. + + 6. You Don't Have an Obligation to Provide Support for Your Contributions + + You are not expected to provide support for Your Contributions, + except to the extent You desire to provide support. You may provide + support for free, for a fee, or not at all. + + 6. No Warranties or Conditions + + StrongLoop acknowledges that unless required by applicable law or + agreed to in writing, You provide Your Contributions on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER + EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES + OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR + FITNESS FOR A PARTICULAR PURPOSE. + + 7. Submission on Behalf of Others + + If You wish to submit work that is not Your original creation, You + may submit it to StrongLoop separately from any Contribution, + identifying the complete details of its source and of any license + or other restriction (including, but not limited to, related + patents, trademarks, and license agreements) of which You are + personally aware, and conspicuously marking the work as + "Submitted on Behalf of a Third-Party: [named here]". + + 8. Agree to Notify of Change of Circumstances + + You agree to notify StrongLoop of any facts or circumstances of + which You become aware that would make these representations + inaccurate in any respect. Email us at callback@strongloop.com. +``` + + +[Google Javascript Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml +[license]: LICENSE + diff --git a/LICENSE b/LICENSE index 8de4196b..16bb096f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2014 StrongLoop, Inc. +Copyright (c) 2013-2014 StrongLoop, Inc and other contributors. loopback uses a 'dual license' model. Users may use loopback under the terms of the MIT license, or under the StrongLoop License. The text of both is included @@ -26,7 +26,12 @@ THE SOFTWARE. StrongLoop License +You may obtain a copy of the License at + + http://www.strongloop.com/license/ + STRONGLOOP SUBSCRIPTION AGREEMENT + PLEASE READ THIS AGREEMENT CAREFULLY BEFORE YOU AGREE TO THESE TERMS. IF YOU ARE ACTING ON BEHALF OF AN ENTITY, THEN YOU REPRESENT THAT YOU HAVE THE AUTHORITY TO ENTER INTO THIS AGREEMENT ON BEHALF OF THAT ENTITY. IF YOU DO NOT diff --git a/README.md b/README.md index 46464037..ef5b6371 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,78 @@ -# LoopBack +LoopBack is a highly extensible, open source Node.js framework that enables you to: -For a quick introduction and overview, see http://loopback.io/. + * Create dynamic end-to-end REST APIs with little or no coding + * Easily access data from Oracle, MySQL, PostgreSQL, MS SQL Server, MongoDB, SOAP and other REST APIs + * Incorporate model relationships and access controls for complex APIs + * Run your application on-premises or in the cloud + * Use built-in push, geolocation, and file services for mobile use cases + * Easily create client apps using Android, iOS, and JavaScript SDKs -## Documentation +LoopBack consists of: + * A library of Node.js modules. + * A command line tool, `slc`, for creating and working with LoopBack applications. + * Client SDKs for native and web-based mobile clients. -[See the full documentation](http://docs.strongloop.com/display/DOC/LoopBack). +For more details, see http://loopback.io/. -## API +## LoopBack modules -[Browse the API documentation](http://apidocs.strongloop.com/loopback). +In addition to the [main LoopBack module](https://github.com/strongloop/loopback), LoopBack consists of numerous other modules that implement specific functionality, +as illustrated below: -## Mailing List +![LoopBack modules](./docs/assets/lb-modules.png "LoopBack modules") -Discuss features and ask questions on [LoopBack Forum](https://groups.google.com/forum/#!forum/loopbackjs). +* Frameworks + * [loopback](https://github.com/strongloop/loopback) + * [loopback-datasource-juggler](https://github.com/strongloop/loopback-datasource-juggler) + * [strong-remoting](https://github.com/strongloop/strong-remoting) -## Issues +* Enterprise Connectors + * [loopback-connector-mongodb](https://github.com/strongloop/loopback-connector-mongodb) + * [loopback-connector-mysql](https://github.com/strongloop/loopback-connector-mysql) + * [loopback-connector-oracle](https://github.com/strongloop/loopback-connector-oracle) + * [loopback-connector-postgresql](https://github.com/strongloop/loopback-connector-postgresql) + * [loopback-connector-rest](https://github.com/strongloop/loopback-connector-rest) + * [loopback-connector-soap](https://github.com/strongloop/loopback-connector-soap) -File any issues [here on GitHub](https://github.com/strongloop/loopback/issues). +* Mobile services + * [loopback-push-notification](https://github.com/strongloop/loopback-push-notification) + * [loopback-storage-service](https://github.com/strongloop/loopback-storage-service) -## Features +* Clients + * [loopback-ios](https://github.com/strongloop/loopback-ios) + * [loopback-remoting-ios](https://github.com/strongloop/loopback-remoting-ios) + * [loopback-android](https://github.com/strongloop/loopback-android) + * [loopback-remoting-android](https://github.com/strongloop/loopback-remoting-android) + * [loopback-angular](https://github.com/strongloop/loopback-angular) -Read about the [features of LoopBack](https://github.com/strongloop/loopback/wiki/Features). +* Tools + * [loopback-explorer](https://github.com/strongloop/loopback-explorer) + * [loopback-workspace](https://github.com/strongloop/loopback-workspace) + * [strong-cli](https://github.com/strongloop/strong-cli) + +* Examples + * [loopback-example-full-stack](https://github.com/strongloop/loopback-example-full-stack) + * [loopback-example-office-supplies](https://github.com/strongloop/loopback-example-office-supplies) + * [loopback-example-todo](https://github.com/strongloop/loopback-example-todo) + * [loopback-example-access-control](https://github.com/strongloop/loopback-example-access-control) + * [loopback-example-proxy](https://github.com/strongloop/loopback-example-proxy) + * [strongloop-community/loopback-example-datagraph](https://github.com/strongloop-community/loopback-example-datagraph) + * [strongloop-community/loopback-mysql-example](https://github.com/strongloop-community/loopback-mysql-example) + * [strongloop-community/loopback-examples-ios](https://github.com/strongloop-community/loopback-examples-ios) + * [strongloop-community/loopback-example-ssl](https://github.com/strongloop-community/loopback-example-ssl) + +## Resources + + * [Documentation](http://docs.strongloop.com/display/DOC/LoopBack). + * [API documentation](http://apidocs.strongloop.com/loopback). + * [LoopBack Google Group](https://groups.google.com/forum/#!forum/loopbackjs). + * [GitHub issues](https://github.com/strongloop/loopback/issues). + * Read more about the [LoopBack's features](https://github.com/strongloop/loopback/wiki/Features). ## Contributing +Please read [CONTRIBUTING.md](CONTRIBUTING.md) on how to contribute. + A brief guide for [contributing to LoopBack projects](https://github.com/strongloop/loopback/wiki/How-To-Contribute). -[![githalytics.com alpha](https://cruel-carlota.pagodabox.com/8caf6b5cce4be4d13c01ea9aafc9f407 "githalytics.com")](http://githalytics.com/strongloop/loopback) +[![Analytics](https://sl-beacon.appspot.com/UA-37775386-1/github/loopback/readme?pixel)](https://github.com/strongloop/loopback) diff --git a/docs.json b/docs.json index 8d60a579..6df5005f 100644 --- a/docs.json +++ b/docs.json @@ -2,9 +2,7 @@ "title": "LoopBack Documentation", "content": [ "lib/application.js", - {"title": "LoopBack", "depth": 3}, "lib/loopback.js", - {"title": "Middleware", "depth": 3}, "lib/middleware/token.js", "lib/models/access-token.js", "lib/models/access-context.js", diff --git a/docs/api-model.md b/docs/api-model.md index 5ec0beb2..7aaf3687 100644 --- a/docs/api-model.md +++ b/docs/api-model.md @@ -442,145 +442,6 @@ appearing in many groups, you could declare the models this way, user.groups.add(group, callback); // connect an existing group with the user user.groups.remove(group, callback); // remove the user from the group ``` - -### Validations - -#### Model.validatesFormatOf(property, options) - -Require a model to include a property that matches the given format. - -```js -User.validatesFormat('name', {with: /\w+/}); -``` - -#### Model.validatesPresenceOf(properties...) - -Require a model to include a property to be considered valid. - -```js -User.validatesPresenceOf('first', 'last', 'age'); -``` - -#### Model.validatesLengthOf(property, options) - -Require a property length to be within a specified range. - -```js -User.validatesLengthOf('password', {min: 5, message: {min: 'Password is too short'}}); -``` - -#### Model.validatesInclusionOf(property, options) - -Require a value for `property` to be in the specified array. - -```js -User.validatesInclusionOf('gender', {in: ['male', 'female']}); -``` - -#### Model.validatesExclusionOf(property, options) - -Require a value for `property` to not exist in the specified array. - -```js -User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']}); -``` - -#### Model.validatesNumericalityOf(property, options) - -Require a value for `property` to be a specific type of `Number`. - -```js -User.validatesNumericalityOf('age', {int: true}); -``` - -#### Model.validatesUniquenessOf(property, options) - -Ensure the value for `property` is unique in the collection of models. - -```js -User.validatesUniquenessOf('email', {message: 'email is not unique'}); -``` - -**Note:** not available for all [connectors](#connectors). - -Currently supported in these connectors: - - - [In Memory](#memory-connector) - - [Oracle](http://github.com/strongloop/loopback-connector-oracle) - - [MongoDB](http://github.com/strongloop/loopback-connector-mongodb) - -#### myModel.isValid() - -Validate the model instance. - -```js -user.isValid(function (valid) { - if (!valid) { - console.log(user.errors); - // => hash of errors - // => { - // => username: [errmessage, errmessage, ...], - // => email: ... - // => } - } -}); -``` - -#### loopback.ValidationError - -`ValidationError` is raised when the application attempts to save an invalid -model instance. - -Example: - -```js -{ - "name": "ValidationError", - "status": 422, - "message": "The Model instance is not valid. \ - See `details` property of the error object for more info.", - "statusCode": 422, - "details": { - "context": "user", - "codes": { - "password": [ - "presence" - ], - "email": [ - "uniqueness" - ] - }, - "messages": { - "password": [ - "can't be blank" - ], - "email": [ - "Email already exists" - ] - } - }, -} -``` - -You might run into situations where you need to raise a validation error -yourself, for example in a "before" hook or a custom model method. - -```js -MyModel.prototype.preflight = function(changes, callback) { - // Update properties, do not save to db - for (var key in changes) { - model[key] = changes[key]; - } - - if (model.isValid()) { - return callback(null, { success: true }); - } - - // This line shows how to create a ValidationError - err = new ValidationError(model); - callback(err); -} -``` ### Shared methods diff --git a/docs/assets/lb-modules.png b/docs/assets/lb-modules.png new file mode 100644 index 00000000..24c7bcee Binary files /dev/null and b/docs/assets/lb-modules.png differ diff --git a/example/client-server/client.js b/example/client-server/client.js index 4e1e423c..436e266b 100644 --- a/example/client-server/client.js +++ b/example/client-server/client.js @@ -3,7 +3,7 @@ var client = loopback(); var CartItem = require('./models').CartItem; var remote = loopback.createDataSource({ connector: loopback.Remote, - root: 'http://localhost:3000' + url: 'http://localhost:3000' }); client.model(CartItem); diff --git a/lib/models/access-context.js b/lib/models/access-context.js index 72b24810..b748db1b 100644 --- a/lib/models/access-context.js +++ b/lib/models/access-context.js @@ -7,6 +7,7 @@ var debug = require('debug')('loopback:security:access-context'); * resources * * @class + * @options {Object} context The context object * @property {Principal[]} principals An array of principals * @property {Function} model The model class * @property {String} modelName The model name @@ -16,7 +17,6 @@ var debug = require('debug')('loopback:security:access-context'); * @property {String} accessType The access type * @property {AccessToken} accessToken The access token * - * @param {Object} context The context object * @returns {AccessContext} * @constructor */ diff --git a/lib/models/acl.js b/lib/models/acl.js index 27b53fcc..6c2456bb 100644 --- a/lib/models/acl.js +++ b/lib/models/acl.js @@ -296,6 +296,9 @@ ACL.getStaticACLs = function getStaticACLs(model, property) { ACL.checkPermission = function checkPermission(principalType, principalId, model, property, accessType, callback) { + if(principalId !== null && principalId !== undefined && (typeof principalId !== 'string') ) { + principalId = principalId.toString(); + } property = property || ACL.ALL; var propertyQuery = (property === ACL.ALL) ? undefined : {inq: [property, ACL.ALL]}; accessType = accessType || ACL.ALL; @@ -356,7 +359,7 @@ ACL.prototype.debug = function() { * @property {String} accessType The access type * @param {Function} callback */ -ACL.checkAccess = function (context, callback) { +ACL.checkAccessForContext = function (context, callback) { if(!(context instanceof AccessContext)) { context = new AccessContext(context); } @@ -422,7 +425,7 @@ ACL.checkAccess = function (context, callback) { if(resolved && resolved.permission === ACL.DEFAULT) { resolved.permission = (model && model.settings.defaultPermission) || ACL.ALLOW; } - debug('checkAccess() returns: %j', resolved); + debug('checkAccessForContext() returns: %j', resolved); callback && callback(null, resolved); }); }); @@ -455,7 +458,7 @@ ACL.checkAccessForToken = function (token, model, modelId, method, callback) { context.debug(); - this.checkAccess(context, function (err, access) { + this.checkAccessForContext(context, function (err, access) { if (err) { callback && callback(err); return; diff --git a/lib/models/data-model.js b/lib/models/data-model.js index 91bbc4a8..048a563d 100644 --- a/lib/models/data-model.js +++ b/lib/models/data-model.js @@ -78,7 +78,7 @@ function convertNullToNotFoundError(ctx, cb) { var modelName = ctx.method.sharedClass.name; var id = ctx.getArgByName('id'); - var msg = 'Unkown "' + modelName + '" id "' + id + '".'; + var msg = 'Unknown "' + modelName + '" id "' + id + '".'; var error = new Error(msg); error.statusCode = error.status = 404; cb(error); diff --git a/lib/models/role.js b/lib/models/role.js index 86ff5cc3..8b64796f 100644 --- a/lib/models/role.js +++ b/lib/models/role.js @@ -373,8 +373,14 @@ Role.isInRole = function (role, context, callback) { async.some(context.principals, function (p, done) { var principalType = p.type || undefined; var principalId = p.id || undefined; + var roleId = result.id.toString(); + + if(principalId !== null && principalId !== undefined && (typeof principalId !== 'string') ) { + principalId = principalId.toString(); + } + if (principalType && principalId) { - roleMappingModel.findOne({where: {roleId: result.id, + roleMappingModel.findOne({where: {roleId: roleId, principalType: principalType, principalId: principalId}}, function (err, result) { debug('Role mapping found: %j', result); diff --git a/lib/models/user.js b/lib/models/user.js index 605b4287..ef612403 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -379,7 +379,7 @@ User.resetPassword = function(options, cb) { options = options || {}; if(typeof options.email === 'string') { - UserModel.findOne({email: options.email}, function(err, user) { + UserModel.findOne({ where: {email: options.email} }, function(err, user) { if(err) { cb(err); } else if(user) { @@ -392,7 +392,8 @@ User.resetPassword = function(options, cb) { cb(); UserModel.emit('resetPasswordRequest', { email: options.email, - accessToken: accessToken + accessToken: accessToken, + user: user }); } }) diff --git a/package.json b/package.json index e9862131..9eb54cf9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "loopback", "description": "LoopBack: Open Mobile Platform for Node.js", + "homepage": "http://loopback.io", "keywords": [ "StrongLoop", "LoopBack", @@ -9,7 +10,7 @@ "Platform", "mBaaS" ], - "version": "1.8.0", + "version": "1.8.2", "scripts": { "test": "mocha -R spec" }, diff --git a/test/acl.test.js b/test/acl.test.js index f5958187..0f4f8597 100644 --- a/test/acl.test.js +++ b/test/acl.test.js @@ -268,7 +268,7 @@ describe('security ACLs', function () { log('ACL 2: ', acl.toObject()); - ACL.checkAccess({ + ACL.checkAccessForContext({ principals: [ {type: ACL.USER, id: userId} ], @@ -279,7 +279,7 @@ describe('security ACLs', function () { assert(!err && access.permission === ACL.ALLOW); }); - ACL.checkAccess({ + ACL.checkAccessForContext({ principals: [ {type: ACL.ROLE, id: Role.EVERYONE} ], diff --git a/test/geo-point.test.js b/test/geo-point.test.js index 2f3da597..bc84f717 100644 --- a/test/geo-point.test.js +++ b/test/geo-point.test.js @@ -3,8 +3,9 @@ describe('GeoPoint', function() { it("Get the distance to another `GeoPoint`", function() { var here = new GeoPoint({lat: 10, lng: 10}); var there = new GeoPoint({lat: 5, lng: 5}); - - assert.equal(here.distanceTo(there, {type: 'meters'}), 782777.923052584); + var distance = here.distanceTo(there, {type: 'meters'}); + + assert.equal(Math.floor(distance), 782777); }); }); @@ -12,8 +13,9 @@ describe('GeoPoint', function() { it("Get the distance between two points", function() { var here = new GeoPoint({lat: 10, lng: 10}); var there = new GeoPoint({lat: 5, lng: 5}); + var distance = GeoPoint.distanceBetween(here, there, {type: 'feet'}); - assert.equal(GeoPoint.distanceBetween(here, there, {type: 'feet'}), 2568169.038886431); + assert.equal(Math.floor(distance), 2568169); }); });