mirror of
https://github.com/parse-community/parse-server
synced 2026-08-09 13:03:18 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f22ee36e7 | ||
|
|
ca542c3750 | ||
|
|
1876d3f3fd | ||
|
|
4ceff38811 | ||
|
|
741f869140 | ||
|
|
9d1d8515fc | ||
|
|
9eed81e636 | ||
|
|
e2d33678a9 |
+3
-1
@@ -41,6 +41,8 @@ before_script:
|
||||
- psql -c 'CREATE EXTENSION postgis;' -U postgres -d parse_server_postgres_adapter_test_database
|
||||
- psql -c 'CREATE EXTENSION postgis_topology;' -U postgres -d parse_server_postgres_adapter_test_database
|
||||
- silent=1 mongodb-runner --start
|
||||
script:
|
||||
- npm run coverage
|
||||
after_script:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
@@ -52,7 +54,7 @@ jobs:
|
||||
env:
|
||||
before_script: skip
|
||||
after_script: skip
|
||||
script: npm install -g nsp && nsp check
|
||||
script: skip
|
||||
deploy:
|
||||
- provider: script
|
||||
skip_cleanup: true
|
||||
|
||||
@@ -3,9 +3,18 @@
|
||||
### master
|
||||
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.7.0...master)
|
||||
|
||||
### 2.7.1
|
||||
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.7.1...2.7.0)
|
||||
|
||||
:warning: Fixes a security issue affecting Class Level Permissions
|
||||
|
||||
* Adds support for dot notation when using matchesKeyInQuery, thanks to [Henrik](https://github.com/bohemima) and [Arthur Cinader](https://github.com/acinader)
|
||||
|
||||
### 2.7.0
|
||||
[Full Changelog](https://github.com/parse-community/parse-server/compare/2.7.0...2.6.5)
|
||||
|
||||
:warning: This version contains an issue affecting Class Level Permissions on mongoDB. Please upgrade to 2.7.1.
|
||||
|
||||
Starting parse-server 2.7.0, the minimun nodejs version is 6.11.4, please update your engines before updating parse-server
|
||||
|
||||
#### New Features:
|
||||
|
||||
+3
-5
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "parse-server",
|
||||
"version": "2.7.0",
|
||||
"version": "2.7.1",
|
||||
"description": "An express module providing a Parse-compatible API server",
|
||||
"main": "lib/index.js",
|
||||
"repository": {
|
||||
@@ -69,10 +69,8 @@
|
||||
"lint": "eslint --cache ./",
|
||||
"build": "babel src/ -d lib/ --copy-files",
|
||||
"pretest": "npm run lint",
|
||||
"test": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 $COVERAGE_OPTION jasmine",
|
||||
"test:win": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 jasmine",
|
||||
"coverage": "cross-env COVERAGE_OPTION='./node_modules/.bin/nyc' npm test",
|
||||
"coverage:win": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 node ./node_modules/.bin/nyc ./node_modules/jasmine/bin/jasmine.js",
|
||||
"test": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 jasmine",
|
||||
"coverage": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=3.2.6} MONGODB_STORAGE_ENGINE=mmapv1 TESTING=1 nyc jasmine",
|
||||
"start": "node ./bin/parse-server",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
|
||||
@@ -3206,4 +3206,89 @@ describe('Parse.Query testing', () => {
|
||||
.then(() => q.find({ useMasterKey: true }))
|
||||
.then(done.fail, done);
|
||||
});
|
||||
|
||||
it('should match complex structure with dot notation when using matchesKeyInQuery', function(done) {
|
||||
const group1 = new Parse.Object('Group', {
|
||||
name: 'Group #1'
|
||||
});
|
||||
|
||||
const group2 = new Parse.Object('Group', {
|
||||
name: 'Group #2'
|
||||
});
|
||||
|
||||
Parse.Object.saveAll([group1, group2])
|
||||
.then(() => {
|
||||
const role1 = new Parse.Object('Role', {
|
||||
name: 'Role #1',
|
||||
type: 'x',
|
||||
belongsTo: group1
|
||||
});
|
||||
|
||||
const role2 = new Parse.Object('Role', {
|
||||
name: 'Role #2',
|
||||
type: 'y',
|
||||
belongsTo: group1
|
||||
});
|
||||
|
||||
return Parse.Object.saveAll([role1, role2]);
|
||||
})
|
||||
.then(() => {
|
||||
const rolesOfTypeX = new Parse.Query('Role');
|
||||
rolesOfTypeX.equalTo('type', 'x');
|
||||
|
||||
const groupsWithRoleX = new Parse.Query('Group');
|
||||
groupsWithRoleX.matchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX);
|
||||
|
||||
groupsWithRoleX.find(expectSuccess({
|
||||
success: function(results) {
|
||||
equal(results.length, 1);
|
||||
equal(results[0].get('name'), group1.get('name'));
|
||||
done();
|
||||
}
|
||||
}))
|
||||
})
|
||||
});
|
||||
|
||||
it('should match complex structure with dot notation when using doesNotMatchKeyInQuery', function(done) {
|
||||
const group1 = new Parse.Object('Group', {
|
||||
name: 'Group #1'
|
||||
});
|
||||
|
||||
const group2 = new Parse.Object('Group', {
|
||||
name: 'Group #2'
|
||||
});
|
||||
|
||||
Parse.Object.saveAll([group1, group2])
|
||||
.then(() => {
|
||||
const role1 = new Parse.Object('Role', {
|
||||
name: 'Role #1',
|
||||
type: 'x',
|
||||
belongsTo: group1
|
||||
});
|
||||
|
||||
const role2 = new Parse.Object('Role', {
|
||||
name: 'Role #2',
|
||||
type: 'y',
|
||||
belongsTo: group1
|
||||
});
|
||||
|
||||
return Parse.Object.saveAll([role1, role2]);
|
||||
})
|
||||
.then(() => {
|
||||
const rolesOfTypeX = new Parse.Query('Role');
|
||||
rolesOfTypeX.equalTo('type', 'x');
|
||||
|
||||
const groupsWithRoleX = new Parse.Query('Group');
|
||||
groupsWithRoleX.doesNotMatchKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX);
|
||||
|
||||
groupsWithRoleX.find(expectSuccess({
|
||||
success: function(results) {
|
||||
equal(results.length, 1);
|
||||
equal(results[0].get('name'), group2.get('name'));
|
||||
done();
|
||||
}
|
||||
}))
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
var Parse = require('parse/node').Parse;
|
||||
var request = require('request');
|
||||
const rp = require('request-promise');
|
||||
var dd = require('deep-diff');
|
||||
var Config = require('../src/Config');
|
||||
|
||||
@@ -1721,6 +1722,35 @@ describe('schemas', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("regression test for #4409 (indexes override the clp)", done => {
|
||||
setPermissionsOnClass('_Role', {
|
||||
'get': {"*": true},
|
||||
'find': {"*": true},
|
||||
'create': {'*': true},
|
||||
}, true).then(() => {
|
||||
const config = Config.get('test');
|
||||
return config.database.adapter.updateSchemaWithIndexes();
|
||||
}).then(() => {
|
||||
return rp.get({
|
||||
url: 'http://localhost:8378/1/schemas/_Role',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
});
|
||||
}).then((res) => {
|
||||
expect(res.classLevelPermissions).toEqual({
|
||||
'get': {"*": true},
|
||||
'find': {"*": true},
|
||||
'create': {'*': true},
|
||||
'update': {},
|
||||
'delete': {},
|
||||
'addField': {},
|
||||
});
|
||||
console.log(res);
|
||||
}).then(done).catch(done.fail);
|
||||
});
|
||||
|
||||
|
||||
it('regression test for #2246', done => {
|
||||
const profile = new Parse.Object('UserProfile');
|
||||
const user = new Parse.User();
|
||||
|
||||
@@ -166,7 +166,7 @@ export class MongoStorageAdapter {
|
||||
setClassLevelPermissions(className, CLPs) {
|
||||
return this._schemaCollection()
|
||||
.then(schemaCollection => schemaCollection.updateSchema(className, {
|
||||
$set: { _metadata: { class_permissions: CLPs } }
|
||||
$set: { '_metadata.class_permissions': CLPs }
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ export class MongoStorageAdapter {
|
||||
.then(() => insertPromise)
|
||||
.then(() => this._schemaCollection())
|
||||
.then(schemaCollection => schemaCollection.updateSchema(className, {
|
||||
$set: { _metadata: { indexes: existingIndexes } }
|
||||
$set: { '_metadata.indexes': existingIndexes }
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ export class MongoStorageAdapter {
|
||||
}, {});
|
||||
return this._schemaCollection()
|
||||
.then(schemaCollection => schemaCollection.updateSchema(className, {
|
||||
$set: { _metadata: { indexes: indexes } }
|
||||
$set: { '_metadata.indexes': indexes }
|
||||
}));
|
||||
}).catch(() => {
|
||||
// Ignore if collection not found
|
||||
|
||||
@@ -601,9 +601,7 @@ export class PostgresStorageAdapter {
|
||||
}
|
||||
|
||||
classExists(name) {
|
||||
return this._client.one(`SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = $1)`, [name]).then((res) => {
|
||||
return res.exists;
|
||||
});
|
||||
return this._client.one('SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = $1)', [name], a => a.exists);
|
||||
}
|
||||
|
||||
setClassLevelPermissions(className, CLPs) {
|
||||
@@ -655,17 +653,19 @@ export class PostgresStorageAdapter {
|
||||
if (deletedIndexes.length > 0) {
|
||||
deletePromise = this.dropIndexes(className, deletedIndexes, conn);
|
||||
}
|
||||
return deletePromise
|
||||
.then(() => insertPromise)
|
||||
.then(() => this._ensureSchemaCollectionExists())
|
||||
.then(() => {
|
||||
const values = [className, 'schema', 'indexes', JSON.stringify(existingIndexes)]
|
||||
return conn.none(`UPDATE "_SCHEMA" SET $2:name = json_object_set_key($2:name, $3::text, $4::jsonb) WHERE "className"=$1 `, values);
|
||||
});
|
||||
return conn.task(t => {
|
||||
const values = [className, 'schema', 'indexes', JSON.stringify(existingIndexes)];
|
||||
return t.batch([
|
||||
deletePromise,
|
||||
insertPromise,
|
||||
this._ensureSchemaCollectionExists(t),
|
||||
t.none('UPDATE "_SCHEMA" SET $2:name = json_object_set_key($2:name, $3::text, $4::jsonb) WHERE "className"=$1', values)
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
createClass(className, schema) {
|
||||
return this._client.tx(t => {
|
||||
return this._client.tx('create-class', t => {
|
||||
const q1 = this.createTable(className, schema, t);
|
||||
const q2 = t.none('INSERT INTO "_SCHEMA" ("className", "schema", "isParseClass") VALUES ($<className>, $<schema>, true)', { className, schema });
|
||||
const q3 = this.setIndexesWithSchemaFormat(className, schema.indexes, {}, schema.fields, t);
|
||||
@@ -727,15 +727,17 @@ export class PostgresStorageAdapter {
|
||||
});
|
||||
const qs = `CREATE TABLE IF NOT EXISTS $1:name (${patternsArray.join(',')})`;
|
||||
const values = [className, ...valuesArray];
|
||||
return this._ensureSchemaCollectionExists(conn)
|
||||
.then(() => conn.none(qs, values))
|
||||
.catch(error => {
|
||||
if (error.code === PostgresDuplicateRelationError) {
|
||||
// Table already exists, must have been created by a different request. Ignore error.
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}).then(() => {
|
||||
return conn.task(t => {
|
||||
return this._ensureSchemaCollectionExists(t)
|
||||
.then(() => conn.none(qs, values))
|
||||
.catch(error => {
|
||||
if (error.code === PostgresDuplicateRelationError) {
|
||||
// Table already exists, must have been created by a different request. Ignore error.
|
||||
} else {
|
||||
throw error;
|
||||
}})
|
||||
})
|
||||
.then(() => {
|
||||
return conn.tx('create-relation-tables', t => {
|
||||
const queries = relations.map((fieldName) => {
|
||||
return t.none('CREATE TABLE IF NOT EXISTS $<joinTable:name> ("relatedId" varChar(120), "owningId" varChar(120), PRIMARY KEY("relatedId", "owningId") )', {joinTable: `_Join:${fieldName}:${className}`});
|
||||
@@ -748,7 +750,7 @@ export class PostgresStorageAdapter {
|
||||
addFieldIfNotExists(className, fieldName, type) {
|
||||
// TODO: Must be revised for invalid logic...
|
||||
debug('addFieldIfNotExists', {className, fieldName, type});
|
||||
return this._client.tx("addFieldIfNotExists", t=> {
|
||||
return this._client.tx('add-field-if-not-exists', t => {
|
||||
let promise = Promise.resolve();
|
||||
if (type.type !== 'Relation') {
|
||||
promise = t.none('ALTER TABLE $<className:name> ADD COLUMN $<fieldName:name> $<postgresType:raw>', {
|
||||
|
||||
+2
-2
@@ -337,7 +337,7 @@ RestQuery.prototype.replaceNotInQuery = function() {
|
||||
const transformSelect = (selectObject, key ,objects) => {
|
||||
var values = [];
|
||||
for (var result of objects) {
|
||||
values.push(result[key]);
|
||||
values.push(key.split('.').reduce((o,i)=>o[i], result));
|
||||
}
|
||||
delete selectObject['$select'];
|
||||
if (Array.isArray(selectObject['$in'])) {
|
||||
@@ -392,7 +392,7 @@ RestQuery.prototype.replaceSelect = function() {
|
||||
const transformDontSelect = (dontSelectObject, key, objects) => {
|
||||
var values = [];
|
||||
for (var result of objects) {
|
||||
values.push(result[key]);
|
||||
values.push(key.split('.').reduce((o,i)=>o[i], result));
|
||||
}
|
||||
delete dontSelectObject['$dontSelect'];
|
||||
if (Array.isArray(dontSelectObject['$nin'])) {
|
||||
|
||||
Reference in New Issue
Block a user