Skip to content

Commit 5b8aa22

Browse files
author
Francis Lessard
committed
Merge remote-tracking branch 'ParsePlatform/master' into user-roles
2 parents 5de33ac + c9431a8 commit 5b8aa22

11 files changed

+231
-87
lines changed

.github/parse-server-logo.png

11.3 KB
Loading

README.md

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
<img src="http://parse.com/assets/svgs/parse-infinity.svg" alt="Parse logo" height="70"/>
2-
3-
## Parse Server
1+
![Parse Server logo](.github/parse-server-logo.png?raw=true)
42

53
[![Build Status](https://img.shields.io/travis/ParsePlatform/parse-server/master.svg?style=flat)](https://travis-ci.org/ParsePlatform/parse-server)
64
[![Coverage Status](https://img.shields.io/codecov/c/github/ParsePlatform/parse-server/master.svg)](https://codecov.io/github/ParsePlatform/parse-server?branch=master)
@@ -18,14 +16,9 @@ Documentation for Parse Server is available in the [wiki](https://github.com/Par
1816

1917
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.
2018

21-
### Example Project
22-
23-
Check out the [parse-server-example project](https://github.com/ParsePlatform/parse-server-example) repository for an example of a Node.js application that uses the parse-server module on Express.
24-
2519
### Migration Guide
2620

27-
Migrate your existing Parse apps to your own Parse Server. 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).
28-
21+
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).
2922

3023

3124
---
@@ -112,6 +105,9 @@ For more informations about custom auth please see the examples:
112105
* databaseAdapter (unfinished) - The backing store can be changed by creating an adapter class (see `DatabaseAdapter.js`)
113106
* 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))
114107
* enableAnonymousUsers - Defaults to true. Set to false to disable anonymous users.
108+
109+
110+
115111
---
116112

117113
### Usage

spec/ParseAPI.spec.js

+16-5
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ describe('miscellaneous', function() {
587587
done();
588588
});
589589
});
590-
590+
591591
it('test cloud function query parameters', (done) => {
592592
Parse.Cloud.define('echoParams', (req, res) => {
593593
res.success(req.params);
@@ -621,8 +621,8 @@ describe('miscellaneous', function() {
621621
// Register a function with validation
622622
Parse.Cloud.define('functionWithParameterValidation', (req, res) => {
623623
res.success('works');
624-
}, (params) => {
625-
return params.success === 100;
624+
}, (request) => {
625+
return request.params.success === 100;
626626
});
627627

628628
Parse.Cloud.run('functionWithParameterValidation', {"success":100}).then((s) => {
@@ -638,8 +638,8 @@ describe('miscellaneous', function() {
638638
// Register a function with validation
639639
Parse.Cloud.define('functionWithParameterValidationFailure', (req, res) => {
640640
res.success('noway');
641-
}, (params) => {
642-
return params.success === 100;
641+
}, (request) => {
642+
return request.params.success === 100;
643643
});
644644

645645
Parse.Cloud.run('functionWithParameterValidationFailure', {"success":500}).then((s) => {
@@ -721,4 +721,15 @@ describe('miscellaneous', function() {
721721
});
722722
});
723723

724+
it('fails on invalid function', done => {
725+
Parse.Cloud.run('somethingThatDoesDefinitelyNotExist').then((s) => {
726+
fail('This should have never suceeded');
727+
done();
728+
}, (e) => {
729+
expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED);
730+
expect(e.message).toEqual('Invalid function.');
731+
done();
732+
});
733+
});
734+
724735
});

spec/schemas.spec.js

+101
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
var Parse = require('parse/node').Parse;
22
var request = require('request');
33
var dd = require('deep-diff');
4+
var Config = require('../src/Config');
5+
6+
var config = new Config('test');
47

58
var hasAllPODobject = () => {
69
var obj = new Parse.Object('HasAllPOD');
@@ -633,4 +636,102 @@ describe('schemas', () => {
633636
});
634637
});
635638
});
639+
640+
it('requires the master key to delete schemas', done => {
641+
request.del({
642+
url: 'http://localhost:8378/1/schemas/DoesntMatter',
643+
headers: noAuthHeaders,
644+
json: true,
645+
}, (error, response, body) => {
646+
expect(response.statusCode).toEqual(403);
647+
expect(body.error).toEqual('unauthorized');
648+
done();
649+
});
650+
});
651+
652+
it('refuses to delete non-empty collection', done => {
653+
var obj = hasAllPODobject();
654+
obj.save()
655+
.then(() => {
656+
request.del({
657+
url: 'http://localhost:8378/1/schemas/HasAllPOD',
658+
headers: masterKeyHeaders,
659+
json: true,
660+
}, (error, response, body) => {
661+
expect(response.statusCode).toEqual(400);
662+
expect(body.code).toEqual(255);
663+
expect(body.error).toEqual('class HasAllPOD not empty, contains 1 objects, cannot drop schema');
664+
done();
665+
});
666+
});
667+
});
668+
669+
it('fails when deleting collections with invalid class names', done => {
670+
request.del({
671+
url: 'http://localhost:8378/1/schemas/_GlobalConfig',
672+
headers: masterKeyHeaders,
673+
json: true,
674+
}, (error, response, body) => {
675+
expect(response.statusCode).toEqual(400);
676+
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
677+
expect(body.error).toEqual('Invalid classname: _GlobalConfig, classnames can only have alphanumeric characters and _, and must start with an alpha character ');
678+
done();
679+
})
680+
});
681+
682+
it('does not fail when deleting nonexistant collections', done => {
683+
request.del({
684+
url: 'http://localhost:8378/1/schemas/Missing',
685+
headers: masterKeyHeaders,
686+
json: true,
687+
}, (error, response, body) => {
688+
expect(response.statusCode).toEqual(200);
689+
expect(body).toEqual({});
690+
done();
691+
});
692+
});
693+
694+
it('deletes collections including join tables', done => {
695+
var obj = new Parse.Object('MyClass');
696+
obj.set('data', 'data');
697+
obj.save()
698+
.then(() => {
699+
var obj2 = new Parse.Object('MyOtherClass');
700+
var relation = obj2.relation('aRelation');
701+
relation.add(obj);
702+
return obj2.save();
703+
})
704+
.then(obj2 => obj2.destroy())
705+
.then(() => {
706+
request.del({
707+
url: 'http://localhost:8378/1/schemas/MyOtherClass',
708+
headers: masterKeyHeaders,
709+
json: true,
710+
}, (error, response, body) => {
711+
expect(response.statusCode).toEqual(200);
712+
expect(response.body).toEqual({});
713+
config.database.db.collection('test__Join:aRelation:MyOtherClass', { strict: true }, (err, coll) => {
714+
//Expect Join table to be gone
715+
expect(err).not.toEqual(null);
716+
config.database.db.collection('test_MyOtherClass', { strict: true }, (err, coll) => {
717+
// Expect data table to be gone
718+
expect(err).not.toEqual(null);
719+
request.get({
720+
url: 'http://localhost:8378/1/schemas/MyOtherClass',
721+
headers: masterKeyHeaders,
722+
json: true,
723+
}, (error, response, body) => {
724+
//Expect _SCHEMA entry to be gone.
725+
expect(response.statusCode).toEqual(400);
726+
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
727+
expect(body.error).toEqual('class MyOtherClass does not exist');
728+
done();
729+
});
730+
});
731+
});
732+
});
733+
}, error => {
734+
fail(error);
735+
});
736+
});
636737
});

src/ExportAdapter.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ ExportAdapter.prototype.destroy = function(className, query, options = {}) {
306306

307307
return coll.remove(mongoWhere);
308308
}).then((resp) => {
309-
if (resp.result.n === 0) {
309+
//Check _Session to avoid changing password failed without any session.
310+
if (resp.result.n === 0 && className !== "_Session") {
310311
return Promise.reject(
311312
new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
312313
'Object not found.'));

src/Routers/ClassesRouter.js

+10-3
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,17 @@ export class ClassesRouter {
5454
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
5555
}
5656

57-
if(req.params.className === "_User"){
57+
if (req.params.className === "_User") {
58+
5859
delete response.results[0].sessionToken;
59-
}
60-
60+
61+
const user = response.results[0];
62+
63+
if (req.auth.user && user.objectId == req.auth.user.id) {
64+
// Force the session token
65+
response.results[0].sessionToken = req.info.sessionToken;
66+
}
67+
}
6168
return { response: response.results[0] };
6269
});
6370
}

src/Schema.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ Schema.prototype.deleteField = function(fieldName, className, database, prefix)
521521
});
522522
}
523523

524-
if (schema.data[className][fieldName].startsWith('relation')) {
524+
if (schema.data[className][fieldName].startsWith('relation<')) {
525525
//For relations, drop the _Join table
526526
return database.dropCollection(prefix + '_Join:' + fieldName + ':' + className)
527527
//Save the _SCHEMA object
@@ -714,6 +714,7 @@ function getObjectType(obj) {
714714
module.exports = {
715715
load: load,
716716
classNameIsValid: classNameIsValid,
717+
invalidClassNameMessage: invalidClassNameMessage,
717718
mongoSchemaFromFieldsAndClassName: mongoSchemaFromFieldsAndClassName,
718719
schemaAPITypeToMongoFieldType: schemaAPITypeToMongoFieldType,
719720
buildMergedSchemaObject: buildMergedSchemaObject,

src/facebook.js

-58
This file was deleted.

src/functions.js

+8-9
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,22 @@ var router = new PromiseRouter();
1010
function handleCloudFunction(req) {
1111
if (Parse.Cloud.Functions[req.params.functionName]) {
1212

13-
const params = Object.assign({}, req.body, req.query);
14-
13+
var request = {
14+
params: Object.assign({}, req.body, req.query),
15+
master: req.auth && req.auth.isMaster,
16+
user: req.auth && req.auth.user,
17+
installationId: req.info.installationId
18+
};
19+
1520
if (Parse.Cloud.Validators[req.params.functionName]) {
16-
var result = Parse.Cloud.Validators[req.params.functionName](params);
21+
var result = Parse.Cloud.Validators[req.params.functionName](request);
1722
if (!result) {
1823
throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'Validation failed.');
1924
}
2025
}
2126

2227
return new Promise(function (resolve, reject) {
2328
var response = createResponseObject(resolve, reject);
24-
var request = {
25-
params: params,
26-
master: req.auth && req.auth.isMaster,
27-
user: req.auth && req.auth.user,
28-
installationId: req.info.installationId
29-
};
3029
Parse.Cloud.Functions[req.params.functionName](request, response);
3130
});
3231
} else {

src/oauth/facebook.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ function validateAuthData(authData) {
1616
}
1717

1818
// Returns a promise that fulfills iff this app id is valid.
19-
function validateAppId(appIds, access_token) {
19+
function validateAppId(appIds, authData) {
20+
var access_token = authData.access_token;
2021
if (!appIds.length) {
2122
throw new Parse.Error(
2223
Parse.Error.OBJECT_NOT_FOUND,

0 commit comments

Comments
 (0)