mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a00d795404 | ||
|
|
2e50416cba | ||
|
|
fcd914bdfd | ||
|
|
db76aa376f | ||
|
|
65518cdd64 | ||
|
|
e7e2369132 | ||
|
|
ac705a8da6 | ||
|
|
aeb6880b85 | ||
|
|
bea655e61b | ||
|
|
abeecad907 | ||
|
|
38f02af511 | ||
|
|
c6c9c97b54 |
+1
-1
@@ -12,7 +12,7 @@ env:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^[0-9].[0-9].[0-9](-.*)?$/
|
||||
- /^[0-9]+.[0-9]+.[0-9]+(-.*)?$/
|
||||
cache:
|
||||
directories:
|
||||
- "$HOME/.mongodb/versions/downloads"
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
## Parse Server Changelog
|
||||
|
||||
### 2.2.12 (6/9/2016)
|
||||
|
||||
* New: Custom error codes in cloud code response.error, thanks to [Jeremy Pease](https://github.com/JeremyPlease)
|
||||
* Fix: Crash in beforeSave when response is not an object, thanks to [Tyler Brock](https://github.com/TylerBrock)
|
||||
* Fix: Allow "get" on installations
|
||||
* Fix: Fix overly restrictive Class Level Permissions, thanks to [Florent Vilmart](https://github.com/flovilmart)
|
||||
* Fix: Fix nested date parsing in Cloud Code, thanks to [Marco Cheung](https://github.com/Marco129)
|
||||
* Fix: Support very old file formats from Parse.com
|
||||
|
||||
### 2.2.11 (5/31/2016)
|
||||
|
||||
* Security: Censor user password in logs, thanks to [Marco Cheung](https://github.com/Marco129)
|
||||
|
||||
@@ -183,7 +183,7 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo
|
||||
* `fileKey` - For migrated apps, this is necessary to provide access to files already hosted on Parse.
|
||||
* `allowClientClassCreation` - Set to false to disable client class creation. Defaults to true.
|
||||
* `enableAnonymousUsers` - Set to false to disable anonymous users. Defaults to true.
|
||||
* `oauth` - Used to configure support for [3rd party authentication](https://github.com/ParsePlatform/parse-server/wiki/Parse-Server-Guide#oauth).
|
||||
* `oauth` - Used to configure support for [3rd party authentication](https://github.com/ParsePlatform/parse-server/wiki/OAuth).
|
||||
* `facebookAppIds` - An array of valid Facebook application IDs that users may authenticate with.
|
||||
* `mountPath` - Mount path for the server. Defaults to `/parse`.
|
||||
* `filesAdapter` - The default behavior (GridStore) can be changed by creating an adapter class (see [`FilesAdapter.js`](https://github.com/ParsePlatform/parse-server/blob/master/src/Adapters/Files/FilesAdapter.js)).
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "parse-server",
|
||||
"version": "2.2.11",
|
||||
"version": "2.2.12",
|
||||
"description": "An express module providing a Parse-compatible API server",
|
||||
"main": "lib/index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -64,6 +64,23 @@ describe('Cloud Code', () => {
|
||||
})
|
||||
});
|
||||
|
||||
it('beforeSave rejection with custom error code', function(done) {
|
||||
Parse.Cloud.beforeSave('BeforeSaveFailWithErrorCode', function (req, res) {
|
||||
res.error(999, 'Nope');
|
||||
});
|
||||
|
||||
var obj = new Parse.Object('BeforeSaveFailWithErrorCode');
|
||||
obj.set('foo', 'bar');
|
||||
obj.save().then(function() {
|
||||
fail('Should not have been able to save BeforeSaveFailWithErrorCode class.');
|
||||
done();
|
||||
}, function(error) {
|
||||
expect(error.code).toEqual(999);
|
||||
expect(error.message).toEqual('Nope');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('basic beforeSave rejection via promise', function(done) {
|
||||
Parse.Cloud.beforeSave('BeforeSaveFailWithPromise', function (req, res) {
|
||||
var query = new Parse.Query('Yolo');
|
||||
@@ -299,6 +316,63 @@ describe('Cloud Code', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('test cloud function request params types', function(done) {
|
||||
Parse.Cloud.define('params', function(req, res) {
|
||||
expect(req.params.date instanceof Date).toBe(true);
|
||||
expect(req.params.date.getTime()).toBe(1463907600000);
|
||||
expect(req.params.dateList[0] instanceof Date).toBe(true);
|
||||
expect(req.params.dateList[0].getTime()).toBe(1463907600000);
|
||||
expect(req.params.complexStructure.date[0] instanceof Date).toBe(true);
|
||||
expect(req.params.complexStructure.date[0].getTime()).toBe(1463907600000);
|
||||
expect(req.params.complexStructure.deepDate.date[0] instanceof Date).toBe(true);
|
||||
expect(req.params.complexStructure.deepDate.date[0].getTime()).toBe(1463907600000);
|
||||
expect(req.params.complexStructure.deepDate2[0].date instanceof Date).toBe(true);
|
||||
expect(req.params.complexStructure.deepDate2[0].date.getTime()).toBe(1463907600000);
|
||||
return res.success({});
|
||||
});
|
||||
|
||||
let params = {
|
||||
'date': {
|
||||
'__type': 'Date',
|
||||
'iso': '2016-05-22T09:00:00.000Z'
|
||||
},
|
||||
'dateList': [
|
||||
{
|
||||
'__type': 'Date',
|
||||
'iso': '2016-05-22T09:00:00.000Z'
|
||||
}
|
||||
],
|
||||
'lol': 'hello',
|
||||
'complexStructure': {
|
||||
'date': [
|
||||
{
|
||||
'__type': 'Date',
|
||||
'iso': '2016-05-22T09:00:00.000Z'
|
||||
}
|
||||
],
|
||||
'deepDate': {
|
||||
'date': [
|
||||
{
|
||||
'__type': 'Date',
|
||||
'iso': '2016-05-22T09:00:00.000Z'
|
||||
}
|
||||
]
|
||||
},
|
||||
'deepDate2': [
|
||||
{
|
||||
'date': {
|
||||
'__type': 'Date',
|
||||
'iso': '2016-05-22T09:00:00.000Z'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
Parse.Cloud.run('params', params).then((result) => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test cloud function should echo keys', function(done) {
|
||||
Parse.Cloud.define('echoKeys', function(req, res){
|
||||
return res.success({
|
||||
|
||||
@@ -724,6 +724,36 @@ describe('miscellaneous', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('test cloud function error handling with custom error code', (done) => {
|
||||
// Register a function which will fail
|
||||
Parse.Cloud.define('willFail', (req, res) => {
|
||||
res.error(999, 'noway');
|
||||
});
|
||||
Parse.Cloud.run('willFail').then((s) => {
|
||||
fail('Should not have succeeded.');
|
||||
done();
|
||||
}, (e) => {
|
||||
expect(e.code).toEqual(999);
|
||||
expect(e.message).toEqual('noway');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test cloud function error handling with standard error code', (done) => {
|
||||
// Register a function which will fail
|
||||
Parse.Cloud.define('willFail', (req, res) => {
|
||||
res.error('noway');
|
||||
});
|
||||
Parse.Cloud.run('willFail').then((s) => {
|
||||
fail('Should not have succeeded.');
|
||||
done();
|
||||
}, (e) => {
|
||||
expect(e.code).toEqual(Parse.Error.SCRIPT_FAILED);
|
||||
expect(e.message).toEqual('noway');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test beforeSave/afterSave get installationId', function(done) {
|
||||
let triggerTime = 0;
|
||||
Parse.Cloud.beforeSave('GameScore', function(req, res) {
|
||||
|
||||
+21
-1
@@ -491,7 +491,7 @@ describe('Parse.File testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates correct url for old files hosted on parse', done => {
|
||||
it('creates correct url for old files hosted on files.parsetfss.com', done => {
|
||||
var file = {
|
||||
__type: 'File',
|
||||
url: 'http://irrelevant.elephant/',
|
||||
@@ -511,6 +511,26 @@ describe('Parse.File testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('creates correct url for old files hosted on files.parse.com', done => {
|
||||
var file = {
|
||||
__type: 'File',
|
||||
url: 'http://irrelevant.elephant/',
|
||||
name: 'd6e80979-a128-4c57-a167-302f874700dc-123.txt'
|
||||
};
|
||||
var obj = new Parse.Object('OldFileTest');
|
||||
obj.set('oldfile', file);
|
||||
obj.save().then(() => {
|
||||
var query = new Parse.Query('OldFileTest');
|
||||
return query.first();
|
||||
}).then((result) => {
|
||||
var fileAgain = result.get('oldfile');
|
||||
expect(fileAgain.url()).toEqual(
|
||||
'http://files.parse.com/test/d6e80979-a128-4c57-a167-302f874700dc-123.txt'
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('supports files in objects without urls', done => {
|
||||
var file = {
|
||||
__type: 'File',
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
// These tests check the Installations functionality of the REST API.
|
||||
// Ported from installation_collection_test.go
|
||||
|
||||
var auth = require('../src/Auth');
|
||||
var cache = require('../src/cache');
|
||||
var Config = require('../src/Config');
|
||||
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||
var Parse = require('parse/node').Parse;
|
||||
var rest = require('../src/rest');
|
||||
let auth = require('../src/Auth');
|
||||
let cache = require('../src/cache');
|
||||
let Config = require('../src/Config');
|
||||
let DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||
let Parse = require('parse/node').Parse;
|
||||
let rest = require('../src/rest');
|
||||
let request = require("request");
|
||||
|
||||
var config = new Config('test');
|
||||
let config = new Config('test');
|
||||
let database = DatabaseAdapter.getDatabaseConnection('test', 'test_');
|
||||
let defaultColumns = require('../src/Controllers/SchemaController').defaultColumns;
|
||||
|
||||
@@ -808,8 +809,36 @@ describe('Installations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('allows you to get your own installation (regression test for #1718)', done => {
|
||||
let installId = '12345678-abcd-abcd-abcd-123456789abc';
|
||||
let device = 'android';
|
||||
let input = {
|
||||
'installationId': installId,
|
||||
'deviceType': device
|
||||
};
|
||||
rest.create(config, auth.nobody(config), '_Installation', input)
|
||||
.then(createResult => {
|
||||
let headers = {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
};
|
||||
request.get({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/installations/' + createResult.response.objectId,
|
||||
json: true,
|
||||
}, (error, response, body) => {
|
||||
expect(body.objectId).toEqual(createResult.response.objectId);
|
||||
done();
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
fail('failed');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Look at additional tests from installation_collection_test.go:882
|
||||
// TODO: Do we need to support _tombstone disabling of installations?
|
||||
// TODO: Test deletion, badge increments
|
||||
|
||||
});
|
||||
|
||||
@@ -282,7 +282,7 @@ describe('Pointer Permissions', () => {
|
||||
it('tests CLP / Pointer Perms / ACL write (PP Locked)', (done) => {
|
||||
/*
|
||||
tests:
|
||||
CLP: update open ({"*": true})
|
||||
CLP: update closed ({})
|
||||
PointerPerm: "owner"
|
||||
ACL: logged in user has access
|
||||
|
||||
@@ -300,7 +300,7 @@ describe('Pointer Permissions', () => {
|
||||
password: 'password'
|
||||
});
|
||||
let obj = new Parse.Object('AnObject');
|
||||
Parse.Object.saveAll([user, user2]).then(() => {
|
||||
Parse.Object.saveAll([user, user2]).then(() => {
|
||||
let ACL = new Parse.ACL();
|
||||
ACL.setReadAccess(user, true);
|
||||
ACL.setWriteAccess(user, true);
|
||||
@@ -310,7 +310,7 @@ describe('Pointer Permissions', () => {
|
||||
}).then(() => {
|
||||
return config.database.loadSchema().then((schema) => {
|
||||
// Lock the update, and let only owner write
|
||||
return schema.updateClass('AnObject', {}, {update: {"*": true}, writeUserFields: ['owner']});
|
||||
return schema.updateClass('AnObject', {}, {update: {}, writeUserFields: ['owner']});
|
||||
});
|
||||
}).then(() => {
|
||||
return Parse.User.logIn('user1', 'password');
|
||||
@@ -329,7 +329,7 @@ describe('Pointer Permissions', () => {
|
||||
it('tests CLP / Pointer Perms / ACL write (ACL Locked)', (done) => {
|
||||
/*
|
||||
tests:
|
||||
CLP: update open ({"*": true})
|
||||
CLP: update closed ({})
|
||||
PointerPerm: "owner"
|
||||
ACL: logged in user has access
|
||||
*/
|
||||
@@ -355,7 +355,7 @@ describe('Pointer Permissions', () => {
|
||||
}).then(() => {
|
||||
return config.database.loadSchema().then((schema) => {
|
||||
// Lock the update, and let only owner write
|
||||
return schema.updateClass('AnObject', {}, {update: {"*": true}, writeUserFields: ['owner']});
|
||||
return schema.updateClass('AnObject', {}, {update: {}, writeUserFields: ['owner']});
|
||||
});
|
||||
}).then(() => {
|
||||
return Parse.User.logIn('user2', 'password');
|
||||
@@ -374,7 +374,7 @@ describe('Pointer Permissions', () => {
|
||||
it('tests CLP / Pointer Perms / ACL write (ACL/PP OK)', (done) => {
|
||||
/*
|
||||
tests:
|
||||
CLP: update open ({"*": true})
|
||||
CLP: update closed ({})
|
||||
PointerPerm: "owner"
|
||||
ACL: logged in user has access
|
||||
*/
|
||||
@@ -400,7 +400,7 @@ describe('Pointer Permissions', () => {
|
||||
}).then(() => {
|
||||
return config.database.loadSchema().then((schema) => {
|
||||
// Lock the update, and let only owner write
|
||||
return schema.updateClass('AnObject', {}, {update: {"*": true}, writeUserFields: ['owner']});
|
||||
return schema.updateClass('AnObject', {}, {update: {}, writeUserFields: ['owner']});
|
||||
});
|
||||
}).then(() => {
|
||||
return Parse.User.logIn('user2', 'password');
|
||||
@@ -419,7 +419,7 @@ describe('Pointer Permissions', () => {
|
||||
it('tests CLP / Pointer Perms / ACL read (PP locked)', (done) => {
|
||||
/*
|
||||
tests:
|
||||
CLP: find/get open ({"*": true})
|
||||
CLP: find/get open ({})
|
||||
PointerPerm: "owner" : read
|
||||
ACL: logged in user has access
|
||||
|
||||
@@ -447,7 +447,7 @@ describe('Pointer Permissions', () => {
|
||||
}).then(() => {
|
||||
return config.database.loadSchema().then((schema) => {
|
||||
// Lock the update, and let only owner write
|
||||
return schema.updateClass('AnObject', {}, {find: {"*": true}, get: {"*": true}, readUserFields: ['owner']});
|
||||
return schema.updateClass('AnObject', {}, {find: {}, get: {}, readUserFields: ['owner']});
|
||||
});
|
||||
}).then(() => {
|
||||
return Parse.User.logIn('user1', 'password');
|
||||
|
||||
@@ -132,6 +132,7 @@ describe('SchemaController', () => {
|
||||
var get = {};
|
||||
get[user.id] = true;
|
||||
return schema.setPermissions('Stuff', {
|
||||
'create': {'*': true},
|
||||
'find': find,
|
||||
'get': get
|
||||
});
|
||||
@@ -152,6 +153,7 @@ describe('SchemaController', () => {
|
||||
done();
|
||||
}, (e) => {
|
||||
fail('Class permissions should have allowed this get query');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+50
-18
@@ -963,18 +963,10 @@ describe('schemas', () => {
|
||||
create: {
|
||||
'role:admin': true
|
||||
},
|
||||
get: {
|
||||
'*': true
|
||||
},
|
||||
update: {
|
||||
'*': true
|
||||
},
|
||||
addField: {
|
||||
'*': true
|
||||
},
|
||||
delete: {
|
||||
'*': true
|
||||
}
|
||||
get: {},
|
||||
update: {},
|
||||
delete: {},
|
||||
addField: {}
|
||||
});
|
||||
done();
|
||||
});
|
||||
@@ -1018,6 +1010,9 @@ describe('schemas', () => {
|
||||
json: true,
|
||||
body: {
|
||||
classLevelPermissions: {
|
||||
create: {
|
||||
'*': true
|
||||
},
|
||||
find: {
|
||||
'*': true
|
||||
},
|
||||
@@ -1040,14 +1035,14 @@ describe('schemas', () => {
|
||||
})
|
||||
});
|
||||
|
||||
it('should not be able to add a field', done => {
|
||||
it('should be able to add a field', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas/AClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
classLevelPermissions: {
|
||||
find: {
|
||||
create: {
|
||||
'*': true
|
||||
},
|
||||
addField: {
|
||||
@@ -1243,7 +1238,7 @@ describe('schemas', () => {
|
||||
}).then(() => {
|
||||
return Parse.User.logIn('user', 'user').then(() => {
|
||||
let obj = new Parse.Object('AClass');
|
||||
return obj.save();
|
||||
return obj.save(null, {useMasterKey: true});
|
||||
})
|
||||
}).then(() => {
|
||||
let query = new Parse.Query('AClass');
|
||||
@@ -1292,7 +1287,7 @@ describe('schemas', () => {
|
||||
}).then(() => {
|
||||
return Parse.User.logIn('user', 'user').then(() => {
|
||||
let obj = new Parse.Object('AClass');
|
||||
return obj.save();
|
||||
return obj.save(null, {useMasterKey: true});
|
||||
})
|
||||
}).then(() => {
|
||||
let query = new Parse.Query('AClass');
|
||||
@@ -1357,7 +1352,7 @@ describe('schemas', () => {
|
||||
}).then(() => {
|
||||
return Parse.User.logIn('user', 'user').then(() => {
|
||||
let obj = new Parse.Object('AClass');
|
||||
return obj.save();
|
||||
return obj.save(null, {useMasterKey: true});
|
||||
})
|
||||
}).then(() => {
|
||||
let query = new Parse.Query('AClass');
|
||||
@@ -1415,7 +1410,7 @@ describe('schemas', () => {
|
||||
}).then(() => {
|
||||
return Parse.User.logIn('user', 'user').then(() => {
|
||||
let obj = new Parse.Object('AClass');
|
||||
return obj.save();
|
||||
return obj.save(null, {useMasterKey: true});
|
||||
})
|
||||
}).then(() => {
|
||||
let query = new Parse.Query('AClass');
|
||||
@@ -1544,6 +1539,7 @@ describe('schemas', () => {
|
||||
|
||||
it('can login when addFields is false (issue #1355)', (done) => {
|
||||
setPermissionsOnClass('_User', {
|
||||
'create': {'*': true},
|
||||
'addField': {}
|
||||
}).then(() => {
|
||||
return Parse.User.signUp('foo', 'bar');
|
||||
@@ -1573,4 +1569,40 @@ describe('schemas', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("regression test for #1991", done => {
|
||||
let user = new Parse.User();
|
||||
user.setUsername('user');
|
||||
user.setPassword('user');
|
||||
let role = new Parse.Role('admin', new Parse.ACL());
|
||||
let obj = new Parse.Object('AnObject');
|
||||
Parse.Object.saveAll([user, role]).then(() => {
|
||||
role.relation('users').add(user);
|
||||
return role.save(null, {useMasterKey: true});
|
||||
}).then(() => {
|
||||
return setPermissionsOnClass('AnObject', {
|
||||
'get': {"*": true},
|
||||
'find': {"*": true},
|
||||
'create': {'*': true},
|
||||
'update': {'role:admin': true},
|
||||
'delete': {'role:admin': true}
|
||||
})
|
||||
}).then(() => {
|
||||
return obj.save();
|
||||
}).then(() => {
|
||||
return Parse.User.logIn('user', 'user')
|
||||
}).then(() => {
|
||||
return obj.destroy();
|
||||
}).then((result) => {
|
||||
let query = new Parse.Query('AnObject');
|
||||
return query.find();
|
||||
}).then((results) => {
|
||||
expect(results.length).toBe(0);
|
||||
done();
|
||||
}).catch((err) => {
|
||||
fail('should not fail');
|
||||
console.error(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,6 +43,15 @@ function mongoSchemaFieldsToParseSchemaFields(schema) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const emptyCLPS = Object.freeze({
|
||||
find: {},
|
||||
get: {},
|
||||
create: {},
|
||||
update: {},
|
||||
delete: {},
|
||||
addField: {},
|
||||
});
|
||||
|
||||
const defaultCLPS = Object.freeze({
|
||||
find: {'*': true},
|
||||
get: {'*': true},
|
||||
@@ -53,14 +62,14 @@ const defaultCLPS = Object.freeze({
|
||||
});
|
||||
|
||||
function mongoSchemaToParseSchema(mongoSchema) {
|
||||
let clpsFromMongoObject = {};
|
||||
let clps = defaultCLPS;
|
||||
if (mongoSchema._metadata && mongoSchema._metadata.class_permissions) {
|
||||
clpsFromMongoObject = mongoSchema._metadata.class_permissions;
|
||||
clps = {...emptyCLPS, ...mongoSchema._metadata.class_permissions};
|
||||
}
|
||||
return {
|
||||
className: mongoSchema._id,
|
||||
fields: mongoSchemaFieldsToParseSchemaFields(mongoSchema),
|
||||
classLevelPermissions: {...defaultCLPS, ...clpsFromMongoObject},
|
||||
classLevelPermissions: clps,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -704,6 +704,11 @@ DatabaseController.prototype.deleteSchema = function(className) {
|
||||
}
|
||||
|
||||
DatabaseController.prototype.addPointerPermissions = function(schema, className, operation, query, aclGroup = []) {
|
||||
// Check if class has public permission for operation
|
||||
// If the BaseCLP pass, let go through
|
||||
if (schema.testBaseCLP(className, aclGroup, operation)) {
|
||||
return query;
|
||||
}
|
||||
let perms = schema.perms[className];
|
||||
let field = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
|
||||
let userACL = aclGroup.filter((acl) => {
|
||||
|
||||
@@ -6,6 +6,8 @@ import { FilesAdapter } from '../Adapters/Files/FilesAdapter';
|
||||
import path from 'path';
|
||||
import mime from 'mime';
|
||||
|
||||
const legacyFilesRegex = new RegExp("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}-.*");
|
||||
|
||||
export class FilesController extends AdaptableController {
|
||||
|
||||
getFileData(config, filename) {
|
||||
@@ -59,8 +61,13 @@ export class FilesController extends AdaptableController {
|
||||
continue;
|
||||
}
|
||||
let filename = fileObject['name'];
|
||||
// all filenames starting with "tfss-" should be from files.parsetfss.com
|
||||
// all filenames starting with a "-" seperated UUID should be from files.parse.com
|
||||
// all other filenames have been migrated or created from Parse Server
|
||||
if (filename.indexOf('tfss-') === 0) {
|
||||
fileObject['url'] = 'http://files.parsetfss.com/' + config.fileKey + '/' + encodeURIComponent(filename);
|
||||
} else if (legacyFilesRegex.test(filename)) {
|
||||
fileObject['url'] = 'http://files.parse.com/' + config.fileKey + '/' + encodeURIComponent(filename);
|
||||
} else {
|
||||
fileObject['url'] = this.adapter.getFileLocation(config, filename);
|
||||
}
|
||||
|
||||
@@ -202,11 +202,14 @@ function wrapToHTTPRequest(hook, key) {
|
||||
err = body.error;
|
||||
}
|
||||
}
|
||||
|
||||
if (err) {
|
||||
return res.error(err);
|
||||
} else if (hook.triggerName === 'beforeSave') {
|
||||
delete result.createdAt;
|
||||
delete result.updatedAt;
|
||||
if (typeof result === 'object') {
|
||||
delete result.createdAt;
|
||||
delete result.updatedAt;
|
||||
}
|
||||
return res.success({object: result});
|
||||
} else {
|
||||
return res.success(result);
|
||||
|
||||
@@ -632,30 +632,36 @@ class SchemaController {
|
||||
}
|
||||
return Promise.resolve(this);
|
||||
}
|
||||
|
||||
// Validates an operation passes class-level-permissions set in the schema
|
||||
validatePermission(className, aclGroup, operation) {
|
||||
|
||||
// Validates the base CLP for an operation
|
||||
testBaseCLP(className, aclGroup, operation) {
|
||||
if (!this.perms[className] || !this.perms[className][operation]) {
|
||||
return Promise.resolve();
|
||||
return true;
|
||||
}
|
||||
let classPerms = this.perms[className];
|
||||
let perms = classPerms[operation];
|
||||
// Handle the public scenario quickly
|
||||
if (perms['*']) {
|
||||
return Promise.resolve();
|
||||
return true;
|
||||
}
|
||||
// Check permissions against the aclGroup provided (array of userId/roles)
|
||||
let found = false;
|
||||
for (let i = 0; i < aclGroup.length && !found; i++) {
|
||||
if (perms[aclGroup[i]]) {
|
||||
found = true;
|
||||
}
|
||||
if (aclGroup.some(acl => { return perms[acl] === true })) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
// Validates an operation passes class-level-permissions set in the schema
|
||||
validatePermission(className, aclGroup, operation) {
|
||||
if (this.testBaseCLP(className, aclGroup, operation)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (!this.perms[className] || !this.perms[className][operation]) {
|
||||
return true;
|
||||
}
|
||||
let classPerms = this.perms[className];
|
||||
let perms = classPerms[operation];
|
||||
// No matching CLP, let's check the Pointer permissions
|
||||
// And handle those later
|
||||
let permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
|
||||
@@ -666,6 +672,7 @@ class SchemaController {
|
||||
'Permission denied for this action.');
|
||||
}
|
||||
|
||||
// Process the readUserFields later
|
||||
if (Array.isArray(classPerms[permissionField]) && classPerms[permissionField].length > 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ export class ClassesRouter extends PromiseRouter {
|
||||
options.include = String(body.include);
|
||||
}
|
||||
|
||||
return rest.find(req.config, req.auth, req.params.className, {objectId: req.params.objectId}, options)
|
||||
return rest.get(req.config, req.auth, req.params.className, req.params.objectId, options)
|
||||
.then((response) => {
|
||||
if (!response.results || response.results.length == 0) {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Object not found.');
|
||||
|
||||
@@ -5,6 +5,29 @@ var express = require('express'),
|
||||
triggers = require('../triggers');
|
||||
|
||||
import PromiseRouter from '../PromiseRouter';
|
||||
import _ from 'lodash';
|
||||
|
||||
function parseDate(params) {
|
||||
return _.mapValues(params, (obj) => {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => {
|
||||
if (item && item.__type == 'Date') {
|
||||
return new Date(item.iso);
|
||||
} else if (item && typeof item === 'object') {
|
||||
return parseDate(item);
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
});
|
||||
} else if (obj && obj.__type == 'Date') {
|
||||
return new Date(obj.iso);
|
||||
} else if (obj && typeof obj === 'object') {
|
||||
return parseDate(obj);
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export class FunctionsRouter extends PromiseRouter {
|
||||
|
||||
@@ -21,8 +44,12 @@ export class FunctionsRouter extends PromiseRouter {
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(error) {
|
||||
reject(new Parse.Error(Parse.Error.SCRIPT_FAILED, error));
|
||||
error: function(code, message) {
|
||||
if (!message) {
|
||||
message = code;
|
||||
code = Parse.Error.SCRIPT_FAILED;
|
||||
}
|
||||
reject(new Parse.Error(code, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,15 +59,8 @@ export class FunctionsRouter extends PromiseRouter {
|
||||
var theFunction = triggers.getFunction(req.params.functionName, applicationId);
|
||||
var theValidator = triggers.getValidator(req.params.functionName, applicationId);
|
||||
if (theFunction) {
|
||||
const params = Object.assign({}, req.body, req.query);
|
||||
for (var key in params) {
|
||||
if (params.hasOwnProperty(key)) {
|
||||
var value = params[key];
|
||||
if (value && value.__type == 'Date') {
|
||||
params[key] = new Date(value.iso);
|
||||
}
|
||||
}
|
||||
}
|
||||
let params = Object.assign({}, req.body, req.query);
|
||||
params = parseDate(params);
|
||||
var request = {
|
||||
params: params,
|
||||
master: req.auth && req.auth.isMaster,
|
||||
@@ -69,4 +89,3 @@ export class FunctionsRouter extends PromiseRouter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-6
@@ -17,8 +17,14 @@ var triggers = require('./triggers');
|
||||
// Returns a promise for an object with optional keys 'results' and 'count'.
|
||||
function find(config, auth, className, restWhere, restOptions) {
|
||||
enforceRoleSecurity('find', className, auth);
|
||||
var query = new RestQuery(config, auth, className,
|
||||
restWhere, restOptions);
|
||||
let query = new RestQuery(config, auth, className, restWhere, restOptions);
|
||||
return query.execute();
|
||||
}
|
||||
|
||||
// get is just like find but only queries an objectId.
|
||||
const get = (config, auth, className, objectId, restOptions) => {
|
||||
enforceRoleSecurity('get', className, auth);
|
||||
let query = new RestQuery(config, auth, className, { objectId }, restOptions);
|
||||
return query.execute();
|
||||
}
|
||||
|
||||
@@ -128,8 +134,9 @@ function enforceRoleSecurity(method, className, auth) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
create: create,
|
||||
del: del,
|
||||
find: find,
|
||||
update: update
|
||||
create,
|
||||
del,
|
||||
find,
|
||||
get,
|
||||
update
|
||||
};
|
||||
|
||||
+6
-2
@@ -141,8 +141,12 @@ export function getResponseObject(request, resolve, reject) {
|
||||
}
|
||||
return resolve(response);
|
||||
},
|
||||
error: function(error) {
|
||||
var scriptError = new Parse.Error(Parse.Error.SCRIPT_FAILED, error);
|
||||
error: function(code, message) {
|
||||
if (!message) {
|
||||
message = code;
|
||||
code = Parse.Error.SCRIPT_FAILED;
|
||||
}
|
||||
var scriptError = new Parse.Error(code, message);
|
||||
return reject(scriptError);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user