Compare commits

...
55 Commits
Author SHA1 Message Date
Fosco Marotto 01f4bcc3e3 Updating to 2.1.2 2016-02-19 13:04:49 -08:00
Drew 8441dd2956 Merge pull request #518 from ParsePlatform/fosco.212
Updating to 2.1.2
2016-02-19 12:50:44 -08:00
Fosco Marotto 7db69ac8f2 Updating to 2.1.2 2016-02-19 12:10:00 -08:00
Fosco Marotto 3087b2121e Merge pull request #464 from simonbengtsson/master
Don't use https://api.parse.com/1 as default serverURL
2016-02-19 12:01:48 -08:00
Fosco Marotto 2afb28564e Merge pull request #374 from flessard/user-roles
Fix : User Roles not added to create, update or delete calls
2016-02-19 11:59:33 -08:00
Fosco Marotto 39f8143326 Merge pull request #410 from Marco129/query-constraints
Throw when query is encoded incorrectly
2016-02-19 11:56:01 -08:00
Drew 5ed037f8a5 Merge pull request #512 from Joseph-LeGrice/_noBody_fix
Unity _noBody fix
2016-02-19 09:26:48 -08:00
Joseph Le Grice 3f6445d035 remove _noBody when req.body._noBody == false 2016-02-19 16:41:31 +00:00
Drew 02566f0906 Merge pull request #494 from ParsePlatform/fosco.qs
Updated readme with deploy buttons
2016-02-18 19:06:16 -08:00
Fosco Marotto 4bdff73933 Merge pull request #492 from skinp/patp.s3-bucket
Remove default bucket in S3Adapter
2016-02-18 18:09:18 -08:00
Fosco Marotto 605f83c36a Updated readme with deploy buttons 2016-02-18 18:03:45 -08:00
Patrick Pelletier 5f757b0741 Remove default bucket in S3Adapter 2016-02-18 17:26:32 -08:00
Fosco Marotto 9678b7afee Merge pull request #486 from ParsePlatform/fosco.211
Updating to 2.1.1
2016-02-18 12:43:51 -08:00
Fosco Marotto a6dc76d52f Updating to 2.1.1 2016-02-18 11:28:10 -08:00
Francis Lessard 5b8aa22730 Merge remote-tracking branch 'ParsePlatform/master' into user-roles 2016-02-18 14:00:11 -05:00
Fosco Marotto c9431a8ff3 Merge pull request #485 from flovilmart/fix-README
Minor readme change
2016-02-18 10:58:07 -08:00
Florent Vilmart 4e453925f7 Minor readme change 2016-02-18 13:49:13 -05:00
Fosco Marotto ba68acfb0d Merge pull request #476 from sdf611097/master
ExportAdapter destroy nothing will cause change password failed
2016-02-18 10:44:36 -08:00
Drew 2e6173fb99 Merge pull request #474 from drew-gross/schemas-delete
Implement DELETE /schemas/:className
2016-02-18 10:41:43 -08:00
Fosco Marotto d1476ecabd Merge pull request #468 from codegefluester/ccv_pass_in_request
[Cloud Code][Validation] Pass in request object
2016-02-18 10:39:30 -08:00
Francis Lessard 5de33ac226 Fix : save whit masterKey cause object not found. 2016-02-18 13:29:26 -05:00
Fosco Marotto d087ae9b35 Merge pull request #471 from ParsePlatform/readme
Use new Parse Server logo.
2016-02-18 10:28:53 -08:00
Drew 19d17d8610 Merge pull request #478 from flovilmart/flovilmart.hotfix.facebook.appId.Validation
Moved the proper facebook auth data validation
2016-02-18 08:30:36 -08:00
Florent Vilmart d42b08055b Moved the proper facebook auth data validation 2016-02-18 11:23:02 -05:00
Drew b1ab388c96 Merge pull request #480 from flovilmart/flovilmart.fix.missingSessionToken
Fix missing session token when fetching a _User
2016-02-18 08:20:44 -08:00
Florent Vilmart cc55bfb7ba Fix missing session token when fetching a _User 2016-02-18 10:59:36 -05:00
Francis Lessard 67c7670437 Merge remote-tracking branch 'ParsePlatform/master' into user-roles 2016-02-18 10:06:12 -05:00
jim1_lin f271ac9ac1 ExportAdapter destroy nothing will cause change password failed
Changing password without any session will failed,
due to clear no session with response ObjectNotFound.
2016-02-18 15:36:21 +08:00
Drew Gross 61b4468dac Implement DELETE /schemas/:className 2016-02-17 19:00:17 -08:00
Héctor Ramos d668bd984c Remove padding around logo 2016-02-17 17:25:24 -08:00
Héctor Ramos 7cc4ef95c0 Use new Parse Server logo. 2016-02-17 15:07:00 -08:00
Fosco Marotto 57e94dc82c Merge pull request #470 from ParsePlatform/readme
New introduction and links to the documentation
2016-02-17 14:08:19 -08:00
Héctor Ramos a254759da8 New introduction and links to the documentation 2016-02-17 14:00:35 -08:00
Fosco Marotto b380cd2bd6 Merge pull request #467 from ParsePlatform/hramos-contributing
Fix broken link to CoC
2016-02-17 13:04:24 -08:00
Héctor Ramos 86a5ae40e3 Fix broken link to CoC 2016-02-17 13:02:14 -08:00
Björn Kaiser ce1de0a5ef Cloud Function validation now uses the complete request instead of just the request parameters 2016-02-17 20:43:09 +00:00
Björn Kaiser bd0d21af29 Merge remote-tracking branch 'upstream/master' 2016-02-17 20:35:17 +00:00
Simon Bengtsson 4ecf0ac10c Don't use https://api.parse.com/1 as default serverURL 2016-02-17 20:30:43 +01:00
Francis Lessard 176f1b9085 Merge remote-tracking branch 'ParsePlatform/master' into user-roles 2016-02-16 16:34:55 -05:00
Marco129 06b8157eea Throw error when query with wrongly encoded parameter 2016-02-17 02:52:32 +08:00
Francis Lessard 5a167a9a46 Merge remote-tracking branch 'ParsePlatform/master' into user-roles 2016-02-16 10:46:05 -05:00
Francis Lessard 5b40a589c0 Remove public ACL set by default. 2016-02-16 10:45:43 -05:00
Francis Lessard 2c5b77f284 Merge remote-tracking branch 'ParsePlatform/master' into user-roles 2016-02-16 09:58:38 -05:00
Francis Lessard 173c5d46e3 Merge remote-tracking branch 'ParsePlatform/master' into user-roles 2016-02-13 18:42:30 -05:00
Francis Lessard 2234a104d4 Merge remote-tracking branch 'ParsePlatform/master' into user-roles 2016-02-13 08:19:24 -05:00
Francis Lessard 83c0f92553 no message 2016-02-13 08:18:43 -05:00
Francis Lessard 220a13392d no message 2016-02-12 16:32:45 -05:00
Francis Lessard d7d87a31f0 Merge remote-tracking branch 'ParsePlatform/master' into user-roles 2016-02-12 16:16:20 -05:00
Björn Kaiser ec1bbc8d74 Merge remote-tracking branch 'upstream/master' 2016-02-12 20:37:10 +00:00
Francis Lessard 42aacdf62b FIX : User Roles not added to create, update or delete calls 2016-02-11 22:16:07 -05:00
Björn Kaiser 2ccd039497 Merge remote-tracking branch 'upstream/master' 2016-02-06 22:57:52 +00:00
Björn Kaiser 5420c38adf Merge pull request #3 from codegefluester/test_for_calling_nonexisting_cf
Test for calling non-existing Cloud Function
2016-02-03 22:12:58 +00:00
Björn Kaiser 4822d2b668 Merge branch 'master' into test_for_calling_nonexisting_cf 2016-02-03 22:08:30 +00:00
Björn Kaiser 6adffb09d0 Merge pull request #2 from codegefluester/cloud_code_validation
Added Cloud Function validation
2016-02-03 22:01:29 +00:00
Björn Kaiser 035fcd2615 Added test for proper handling of trying to call a non-existing Cloud Function 2016-02-03 21:21:30 +00:00
22 changed files with 389 additions and 149 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+14
View File
@@ -1,5 +1,19 @@
## Parse Server Changelog
### 2.1.2 (2/19/2016)
* Change: The S3 file adapter constructor requires a bucket name
* Fix: Parse Query should throw if improperly encoded
* Fix: Issue where roles were not used in some requests
* Fix: serverURL will no longer default to api.parse.com/1
### 2.1.1 (2/18/2016)
* Experimental: Schemas API support for DELETE operations
* Fix: Session token issue fetching Users
* Fix: Facebook auth validation
* Fix: Invalid error when deleting missing session
### 2.1.0 (2/17/2016)
* Feature: Support for additional OAuth providers
+1 -4
View File
@@ -12,7 +12,4 @@ We really want Parse to be yours, to see it grow and thrive in the open source c
##### Code of Conduct
This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code.
[code-of-conduct]: http://todogroup.org/opencodeofconduct/#Parse Server/fjm@fb.com
This project adheres to the [Open Code of Conduct](http://todogroup.org/opencodeofconduct/#Parse Server/fjm@fb.com). By participating, you are expected to honor this code.
+52 -48
View File
@@ -1,18 +1,56 @@
## parse-server
![Parse Server logo](.github/parse-server-logo.png?raw=true)
[![Build Status](https://img.shields.io/travis/ParsePlatform/parse-server/master.svg?style=flat)](https://travis-ci.org/ParsePlatform/parse-server)
[![Coverage Status](https://img.shields.io/codecov/c/github/ParsePlatform/parse-server/master.svg)](https://codecov.io/github/ParsePlatform/parse-server?branch=master)
[![npm version](https://img.shields.io/npm/v/parse-server.svg?style=flat)](https://www.npmjs.com/package/parse-server)
A Parse.com API compatible router package for Express
Parse Server is an open source version of the Parse backend that can be deployed to any infrastructure that can run Node.js.
Parse Server works with the Express web application framework. It can be added to existing web applications, or run by itself.
Read the announcement blog post here: http://blog.parse.com/announcements/introducing-parse-server-and-the-database-migration-tool/
Read the migration guide here: https://parse.com/docs/server/guide#migrating
## Getting Started
There is a development wiki here on GitHub: https://github.com/ParsePlatform/parse-server/wiki
[![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy?template=https://github.com/parseplatform/parse-server-example)
[![Deploy to Azure](http://azuredeploy.net/deploybutton.png)](https://azuredeploy.net/?repository=https://github.com/parseplatform/parse-server-example)
<a title="Deploy to AWS" href="https://console.aws.amazon.com/elasticbeanstalk/home?region=us-west-2#/newApplication?applicationName=ParseServer&solutionStackName=Node.js&tierName=WebServer&sourceBundleUrl=https://s3.amazonaws.com/elasticbeanstalk-samples-us-east-1/eb-parse-server-sample/parse-server-example.zip" target="_blank"><img src="http://d0.awsstatic.com/product-marketing/Elastic%20Beanstalk/deploy-to-aws.png" height="40"></a>
We also have an [example project](https://github.com/ParsePlatform/parse-server-example) using the parse-server module on Express.
You can create an instance of ParseServer, and mount it on a new or existing Express website:
```js
var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var app = express();
// Specify the connection string for your mongodb database
// and the location to your Parse cloud code
var api = new ParseServer({
databaseURI: 'mongodb://localhost:27017/dev',
cloud: '/home/myApp/cloud/main.js', // Provide an absolute path
appId: 'myAppId',
masterKey: '', //Add your master key here. Keep it secret!
fileKey: 'optionalFileKey',
serverURL: 'http://localhost:1337/parse' // Don't forget to change to https if needed
});
// Serve the Parse API on the /parse URL prefix
app.use('/parse', api);
app.listen(1337, function() {
console.log('parse-server-example running on port 1337.');
});
```
## Documentation
Documentation for Parse Server is available in the [wiki](https://github.com/ParsePlatform/parse-server/wiki) for this repository. The [Parse Server guide](https://github.com/ParsePlatform/parse-server/wiki/Parse-Server-Guide) is a good place to get started.
If you're interested in developing for Parse Server, the [Development guide](https://github.com/ParsePlatform/parse-server/wiki/Development-Guide) will help you get set up.
### Migration Guide
The hosted version of Parse will be fully retired on January 28th, 2017. If you are planning to migrate an app, you need to begin work as soon as possible. Learn more in the [Migration guide](https://github.com/ParsePlatform/parse-server/wiki/Migrating-an-Existing-Parse-App).
---
@@ -74,7 +112,7 @@ It is possible to leverage the OAuth support with any 3rd party authentication t
oauth: {
my_custom_auth: {
module: "PATH_TO_MODULE" // OR object,
module: "PATH_TO_MODULE" // OR object,
option1: "",
option2: "",
}
@@ -97,56 +135,19 @@ For more informations about custom auth please see the examples:
* databaseAdapter (unfinished) - The backing store can be changed by creating an adapter class (see `DatabaseAdapter.js`)
* loggerAdapter - The default behavior/transport (File) can be changed by creating an adapter class (see [`LoggerAdapter.js`](https://github.com/ParsePlatform/parse-server/blob/master/src/Adapters/Logger/LoggerAdapter.js))
* enableAnonymousUsers - Defaults to true. Set to false to disable anonymous users.
---
### Usage
You can create an instance of ParseServer, and mount it on a new or existing Express website:
```js
var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var app = express();
var port = process.env.PORT || 1337;
// Specify the connection string for your mongodb database
// and the location to your Parse cloud code
var api = new ParseServer({
databaseURI: 'mongodb://localhost:27017/dev',
cloud: '/home/myApp/cloud/main.js', // Provide an absolute path
appId: 'myAppId',
masterKey: '', //Add your master key here. Keep it secret!
fileKey: 'optionalFileKey',
serverURL: 'http://localhost:' + port + '/parse' // Don't forget to change to https if needed
});
// Serve the Parse API on the /parse URL prefix
app.use('/parse', api);
// Hello world
app.get('/', function(req, res) {
res.status(200).send('Express is running here.');
});
app.listen(port, function() {
console.log('parse-server-example running on port ' + port + '.');
});
```
#### Standalone usage
You can configure the Parse Server with environment variables:
```js
```js
PARSE_SERVER_DATABASE_URI
PARSE_SERVER_CLOUD_CODE_MAIN
PARSE_SERVER_COLLECTION_PREFIX
PARSE_SERVER_APPLICATION_ID // required
PARSE_SERVER_CLIENT_KEY
PARSE_SERVER_CLIENT_KEY
PARSE_SERVER_REST_API_KEY
PARSE_SERVER_DOTNET_KEY
PARSE_SERVER_JAVASCRIPT_KEY
@@ -158,8 +159,7 @@ PARSE_SERVER_FACEBOOK_APP_IDS // string of comma separated list
```
Alernatively, you can use the `PARSE_SERVER_OPTIONS` environment variable set to the JSON of your configuration (see Usage).
Alternatively, you can use the `PARSE_SERVER_OPTIONS` environment variable set to the JSON of your configuration (see Usage).
To start the server, just run `npm start`.
@@ -192,3 +192,7 @@ You can also set up an app on Parse, providing the connection string for your mo
### Not supported
* `Parse.User.current()` or `Parse.Cloud.useMasterKey()` in cloud code. Instead of `Parse.User.current()` use `request.user` and instead of `Parse.Cloud.useMasterKey()` pass `useMasterKey: true` to each query. To make queries and writes as a specific user within Cloud Code, you need the user's session token, which is available in `request.user.getSessionToken()`.
## Contributing
We really want Parse to be yours, to see it grow and thrive in the open source community. Please see the [Contributing to Parse Server guide](CONTRIBUTING.md).
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "2.1.0",
"version": "2.1.2",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
+16 -5
View File
@@ -587,7 +587,7 @@ describe('miscellaneous', function() {
done();
});
});
it('test cloud function query parameters', (done) => {
Parse.Cloud.define('echoParams', (req, res) => {
res.success(req.params);
@@ -621,8 +621,8 @@ describe('miscellaneous', function() {
// Register a function with validation
Parse.Cloud.define('functionWithParameterValidation', (req, res) => {
res.success('works');
}, (params) => {
return params.success === 100;
}, (request) => {
return request.params.success === 100;
});
Parse.Cloud.run('functionWithParameterValidation', {"success":100}).then((s) => {
@@ -638,8 +638,8 @@ describe('miscellaneous', function() {
// Register a function with validation
Parse.Cloud.define('functionWithParameterValidationFailure', (req, res) => {
res.success('noway');
}, (params) => {
return params.success === 100;
}, (request) => {
return request.params.success === 100;
});
Parse.Cloud.run('functionWithParameterValidationFailure', {"success":500}).then((s) => {
@@ -721,4 +721,15 @@ describe('miscellaneous', function() {
});
});
it('fails on invalid function', done => {
Parse.Cloud.run('somethingThatDoesDefinitelyNotExist').then((s) => {
fail('This should have never suceeded');
done();
}, (e) => {
expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED);
expect(e.message).toEqual('Invalid function.');
done();
});
});
});
+1 -1
View File
@@ -49,7 +49,7 @@ describe('Parse Role testing', () => {
}).then((x) => {
x.set('foo', 'baz');
// This should fail:
return x.save();
return x.save({},{sessionToken: ""});
}).then((x) => {
fail('Should not have been able to save.');
}, (e) => {
+48
View File
@@ -4,6 +4,9 @@ var cache = require('../src/cache');
var Config = require('../src/Config');
var rest = require('../src/rest');
var querystring = require('querystring');
var request = require('request');
var config = new Config('test');
var nobody = auth.nobody(config);
@@ -92,4 +95,49 @@ describe('rest query', () => {
}).catch((error) => { console.log(error); });
});
it('query with wrongly encoded parameter', (done) => {
rest.create(config, nobody, 'TestParameterEncode', {foo: 'bar'}
).then(() => {
return rest.create(config, nobody,
'TestParameterEncode', {foo: 'baz'});
}).then(() => {
var headers = {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest'
};
request.get({
headers: headers,
url: 'http://localhost:8378/1/classes/TestParameterEncode?'
+ querystring.stringify({
where: '{"foo":{"$ne": "baz"}}',
limit: 1
}).replace('=', '%3D'),
}, (error, response, body) => {
expect(error).toBe(null);
var b = JSON.parse(body);
expect(b.code).toEqual(Parse.Error.INVALID_QUERY);
expect(b.error).toEqual('Improper encode of parameter');
done();
});
}).then(() => {
var headers = {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest'
};
request.get({
headers: headers,
url: 'http://localhost:8378/1/classes/TestParameterEncode?'
+ querystring.stringify({
limit: 1
}).replace('=', '%3D'),
}, (error, response, body) => {
expect(error).toBe(null);
var b = JSON.parse(body);
expect(b.code).toEqual(Parse.Error.INVALID_QUERY);
expect(b.error).toEqual('Improper encode of parameter');
done();
});
});
});
});
+101
View File
@@ -1,6 +1,9 @@
var Parse = require('parse/node').Parse;
var request = require('request');
var dd = require('deep-diff');
var Config = require('../src/Config');
var config = new Config('test');
var hasAllPODobject = () => {
var obj = new Parse.Object('HasAllPOD');
@@ -633,4 +636,102 @@ describe('schemas', () => {
});
});
});
it('requires the master key to delete schemas', done => {
request.del({
url: 'http://localhost:8378/1/schemas/DoesntMatter',
headers: noAuthHeaders,
json: true,
}, (error, response, body) => {
expect(response.statusCode).toEqual(403);
expect(body.error).toEqual('unauthorized');
done();
});
});
it('refuses to delete non-empty collection', done => {
var obj = hasAllPODobject();
obj.save()
.then(() => {
request.del({
url: 'http://localhost:8378/1/schemas/HasAllPOD',
headers: masterKeyHeaders,
json: true,
}, (error, response, body) => {
expect(response.statusCode).toEqual(400);
expect(body.code).toEqual(255);
expect(body.error).toEqual('class HasAllPOD not empty, contains 1 objects, cannot drop schema');
done();
});
});
});
it('fails when deleting collections with invalid class names', done => {
request.del({
url: 'http://localhost:8378/1/schemas/_GlobalConfig',
headers: masterKeyHeaders,
json: true,
}, (error, response, body) => {
expect(response.statusCode).toEqual(400);
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
expect(body.error).toEqual('Invalid classname: _GlobalConfig, classnames can only have alphanumeric characters and _, and must start with an alpha character ');
done();
})
});
it('does not fail when deleting nonexistant collections', done => {
request.del({
url: 'http://localhost:8378/1/schemas/Missing',
headers: masterKeyHeaders,
json: true,
}, (error, response, body) => {
expect(response.statusCode).toEqual(200);
expect(body).toEqual({});
done();
});
});
it('deletes collections including join tables', done => {
var obj = new Parse.Object('MyClass');
obj.set('data', 'data');
obj.save()
.then(() => {
var obj2 = new Parse.Object('MyOtherClass');
var relation = obj2.relation('aRelation');
relation.add(obj);
return obj2.save();
})
.then(obj2 => obj2.destroy())
.then(() => {
request.del({
url: 'http://localhost:8378/1/schemas/MyOtherClass',
headers: masterKeyHeaders,
json: true,
}, (error, response, body) => {
expect(response.statusCode).toEqual(200);
expect(response.body).toEqual({});
config.database.db.collection('test__Join:aRelation:MyOtherClass', { strict: true }, (err, coll) => {
//Expect Join table to be gone
expect(err).not.toEqual(null);
config.database.db.collection('test_MyOtherClass', { strict: true }, (err, coll) => {
// Expect data table to be gone
expect(err).not.toEqual(null);
request.get({
url: 'http://localhost:8378/1/schemas/MyOtherClass',
headers: masterKeyHeaders,
json: true,
}, (error, response, body) => {
//Expect _SCHEMA entry to be gone.
expect(response.statusCode).toEqual(400);
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
expect(body.error).toEqual('class MyOtherClass does not exist');
done();
});
});
});
});
}, error => {
fail(error);
});
});
});
+1 -2
View File
@@ -6,7 +6,6 @@ import * as AWS from 'aws-sdk';
import { FilesAdapter } from './FilesAdapter';
const DEFAULT_S3_REGION = "us-east-1";
const DEFAULT_S3_BUCKET = "parse-files";
export class S3Adapter extends FilesAdapter {
// Creates an S3 session.
@@ -15,8 +14,8 @@ export class S3Adapter extends FilesAdapter {
constructor(
accessKey,
secretKey,
bucket,
{ region = DEFAULT_S3_REGION,
bucket = DEFAULT_S3_BUCKET,
bucketPrefix = '',
directAccess = false } = {}
) {
+1 -1
View File
@@ -80,7 +80,7 @@ Auth.prototype.getUserRoles = function() {
return Promise.resolve(this.userRoles);
}
if (this.rolePromise) {
return rolePromise;
return this.rolePromise;
}
this.rolePromise = this._loadRoles();
return this.rolePromise;
+2 -1
View File
@@ -306,7 +306,8 @@ ExportAdapter.prototype.destroy = function(className, query, options = {}) {
return coll.remove(mongoWhere);
}).then((resp) => {
if (resp.result.n === 0) {
//Check _Session to avoid changing password failed without any session.
if (resp.result.n === 0 && className !== "_Session") {
return Promise.reject(
new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
'Object not found.'));
+24 -10
View File
@@ -27,6 +27,7 @@ function RestWrite(config, auth, className, query, data, originalData) {
this.auth = auth;
this.className = className;
this.storage = {};
this.runOptions = {};
if (!query && data.objectId) {
throw new Parse.Error(Parse.Error.INVALID_KEY_NAME, 'objectId ' +
@@ -66,6 +67,8 @@ function RestWrite(config, auth, className, query, data, originalData) {
// status and location are optional.
RestWrite.prototype.execute = function() {
return Promise.resolve().then(() => {
return this.getUserAndRoleACL();
}).then(() => {
return this.validateSchema();
}).then(() => {
return this.handleInstallation();
@@ -88,6 +91,25 @@ RestWrite.prototype.execute = function() {
});
};
// Uses the Auth object to get the list of roles, adds the user id
RestWrite.prototype.getUserAndRoleACL = function() {
if (this.auth.isMaster) {
return Promise.resolve();
}
this.runOptions.acl = ['*'];
if( this.auth.user ){
return this.auth.getUserRoles().then((roles) => {
roles.push(this.auth.user.id);
this.runOptions.acl = this.runOptions.acl.concat(roles);
return Promise.resolve();
});
}else{
return Promise.resolve();
}
};
// Validates this operation against the schema.
RestWrite.prototype.validateSchema = function() {
return this.config.database.validateObject(this.className, this.data);
@@ -690,18 +712,10 @@ RestWrite.prototype.runDatabaseOperation = function() {
throw new Parse.Error(Parse.Error.INVALID_ACL, 'Invalid ACL.');
}
var options = {};
if (!this.auth.isMaster) {
options.acl = ['*'];
if (this.auth.user) {
options.acl.push(this.auth.user.id);
}
}
if (this.query) {
// Run an update
return this.config.database.update(
this.className, this.query, this.data, options).then((resp) => {
this.className, this.query, this.data, this.runOptions).then((resp) => {
this.response = resp;
this.response.updatedAt = this.updatedAt;
});
@@ -714,7 +728,7 @@ RestWrite.prototype.runDatabaseOperation = function() {
this.data.ACL = ACL;
}
// Run a create
return this.config.database.create(this.className, this.data, options)
return this.config.database.create(this.className, this.data, this.runOptions)
.then(() => {
var resp = {
objectId: this.data.objectId,
+21 -3
View File
@@ -2,11 +2,22 @@
import PromiseRouter from '../PromiseRouter';
import rest from '../rest';
import url from 'url';
export class ClassesRouter {
// Returns a promise that resolves to a {response} object.
handleFind(req) {
let body = Object.assign(req.body, req.query);
let options = {};
let allowConstraints = ['skip', 'limit', 'order', 'count', 'keys',
'include', 'redirectClassNameForKey', 'where'];
for (var key in body) {
if (allowConstraints.indexOf(key) === -1) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Improper encode of parameter');
}
}
if (body.skip) {
options.skip = Number(body.skip);
}
@@ -54,10 +65,17 @@ export class ClassesRouter {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
}
if(req.params.className === "_User"){
if (req.params.className === "_User") {
delete response.results[0].sessionToken;
}
const user = response.results[0];
if (req.auth.user && user.objectId == req.auth.user.id) {
// Force the session token
response.results[0].sessionToken = req.info.sessionToken;
}
}
return { response: response.results[0] };
});
}
+2 -1
View File
@@ -521,7 +521,7 @@ Schema.prototype.deleteField = function(fieldName, className, database, prefix)
});
}
if (schema.data[className][fieldName].startsWith('relation')) {
if (schema.data[className][fieldName].startsWith('relation<')) {
//For relations, drop the _Join table
return database.dropCollection(prefix + '_Join:' + fieldName + ':' + className)
//Save the _SCHEMA object
@@ -714,6 +714,7 @@ function getObjectType(obj) {
module.exports = {
load: load,
classNameIsValid: classNameIsValid,
invalidClassNameMessage: invalidClassNameMessage,
mongoSchemaFromFieldsAndClassName: mongoSchemaFromFieldsAndClassName,
schemaAPITypeToMongoFieldType: schemaAPITypeToMongoFieldType,
buildMergedSchemaObject: buildMergedSchemaObject,
-58
View File
@@ -1,58 +0,0 @@
// Helper functions for accessing the Facebook Graph API.
var https = require('https');
var Parse = require('parse/node').Parse;
// Returns a promise that fulfills iff this user id is valid.
function validateAuthData(authData) {
return graphRequest('me?fields=id&access_token=' + authData.access_token)
.then((data) => {
if (data && data.id == authData.id) {
return;
}
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Facebook auth is invalid for this user.');
});
}
// Returns a promise that fulfills iff this app id is valid.
function validateAppId(appIds, authData) {
var access_token = authData.access_token;
if (!appIds.length) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Facebook auth is not configured.');
}
return graphRequest('app?access_token=' + access_token)
.then((data) => {
if (data && appIds.indexOf(data.id) != -1) {
return;
}
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Facebook auth is invalid for this user.');
});
}
// A promisey wrapper for FB graph requests.
function graphRequest(path) {
return new Promise(function(resolve, reject) {
https.get('https://graph.facebook.com/v2.5/' + path, function(res) {
var data = '';
res.on('data', function(chunk) {
data += chunk;
});
res.on('end', function() {
data = JSON.parse(data);
resolve(data);
});
}).on('error', function(e) {
reject('Failed to validate this access token with Facebook.');
});
});
}
module.exports = {
validateAppId: validateAppId,
validateAuthData: validateAuthData
};
+8 -9
View File
@@ -10,10 +10,15 @@ var router = new PromiseRouter();
function handleCloudFunction(req) {
if (Parse.Cloud.Functions[req.params.functionName]) {
const params = Object.assign({}, req.body, req.query);
var request = {
params: Object.assign({}, req.body, req.query),
master: req.auth && req.auth.isMaster,
user: req.auth && req.auth.user,
installationId: req.info.installationId
};
if (Parse.Cloud.Validators[req.params.functionName]) {
var result = Parse.Cloud.Validators[req.params.functionName](params);
var result = Parse.Cloud.Validators[req.params.functionName](request);
if (!result) {
throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Validation failed.');
}
@@ -21,12 +26,6 @@ function handleCloudFunction(req) {
return new Promise(function (resolve, reject) {
var response = createResponseObject(resolve, reject);
var request = {
params: params,
master: req.auth && req.auth.isMaster,
user: req.auth && req.auth.user,
installationId: req.info.installationId
};
Parse.Cloud.Functions[req.params.functionName](request, response);
});
} else {
+1 -3
View File
@@ -116,9 +116,7 @@ function ParseServer(args) {
// Initialize the node client SDK automatically
Parse.initialize(args.appId, args.javascriptKey || '', args.masterKey);
if(args.serverURL) {
Parse.serverURL = args.serverURL;
}
Parse.serverURL = args.serverURL || '';
// This app serves the Parse API directly.
// It's the equivalent of https://api.parse.com/1 in the hosted Parse API.
+1 -1
View File
@@ -26,7 +26,7 @@ function handleParseHeaders(req, res, next) {
restAPIKey: req.get('X-Parse-REST-API-Key')
};
if (req.body && req.body._noBody) {
if (req.body) {
// Unity SDK sends a _noBody key which needs to be removed.
// Unclear at this point if action needs to be taken.
delete req.body._noBody;
+2 -1
View File
@@ -16,7 +16,8 @@ function validateAuthData(authData) {
}
// Returns a promise that fulfills iff this app id is valid.
function validateAppId(appIds, access_token) {
function validateAppId(appIds, authData) {
var access_token = authData.access_token;
if (!appIds.length) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
+7
View File
@@ -56,12 +56,19 @@ function del(config, auth, className, objectId) {
});
}
return Promise.resolve({});
}).then(() => {
if (!auth.isMaster) {
return auth.getUserRoles();
}else{
return Promise.resolve();
}
}).then(() => {
var options = {};
if (!auth.isMaster) {
options.acl = ['*'];
if (auth.user) {
options.acl.push(auth.user.id);
options.acl = options.acl.concat(auth.userRoles);
}
}
+85
View File
@@ -183,10 +183,95 @@ function modifySchema(req) {
});
}
// A helper function that removes all join tables for a schema. Returns a promise.
var removeJoinTables = (database, prefix, mongoSchema) => {
return Promise.all(Object.keys(mongoSchema)
.filter(field => mongoSchema[field].startsWith('relation<'))
.map(field => {
var joinCollectionName = prefix + '_Join:' + field + ':' + mongoSchema._id;
return new Promise((resolve, reject) => {
database.dropCollection(joinCollectionName, (err, results) => {
if (err) {
reject(err);
} else {
resolve();
}
})
});
})
);
};
function deleteSchema(req) {
if (!req.auth.isMaster) {
return masterKeyRequiredResponse();
}
if (!Schema.classNameIsValid(req.params.className)) {
return Promise.resolve({
status: 400,
response: {
code: Parse.Error.INVALID_CLASS_NAME,
error: Schema.invalidClassNameMessage(req.params.className),
}
});
}
return req.config.database.collection(req.params.className)
.then(coll => new Promise((resolve, reject) => {
coll.count((err, count) => {
if (err) {
reject(err);
} else if (count > 0) {
resolve({
status: 400,
response: {
code: 255,
error: 'class ' + req.params.className + ' not empty, contains ' + count + ' objects, cannot drop schema',
}
});
} else {
coll.drop((err, reply) => {
if (err) {
reject(err);
} else {
// We've dropped the collection now, so delete the item from _SCHEMA
// and clear the _Join collections
req.config.database.collection('_SCHEMA')
.then(coll => new Promise((resolve, reject) => {
coll.findAndRemove({ _id: req.params.className }, [], (err, doc) => {
if (err) {
reject(err);
} else if (doc.value === null) {
//tried to delete non-existant class
resolve({ response: {}});
} else {
removeJoinTables(req.config.database.db, req.config.database.collectionPrefix, doc.value)
.then(resolve, reject);
}
});
}))
.then(resolve.bind(undefined, {response: {}}), reject);
}
});
}
});
}))
.catch(error => {
if (error.message == 'ns not found') {
// If they try to delete a non-existant class, thats fine, just let them.
return Promise.resolve({ response: {} });
} else {
return Promise.reject(error);
}
});
}
router.route('GET', '/schemas', getAllSchemas);
router.route('GET', '/schemas/:className', getOneSchema);
router.route('POST', '/schemas', createSchema);
router.route('POST', '/schemas/:className', createSchema);
router.route('PUT', '/schemas/:className', modifySchema);
router.route('DELETE', '/schemas/:className', deleteSchema);
module.exports = router;