Compare commits

...
8 Commits
Author SHA1 Message Date
Florent Vilmart 1f22ee36e7 ⚡ Release 2.7.1
(#4410)
* Adds failing test for 4409

* Adds fix

* ⚡ Release 2.7.1
2017-12-01 09:16:58 -05:00
Arthur Cinader ca542c3750 Fix test name to match what's tested 2017-11-30 12:40:39 -08:00
Arthur Cinader 1876d3f3fd Add doesNotMatchKeyInQuery case... 2017-11-30 12:40:39 -08:00
Henrik Malmberg 4ceff38811 added test for dot-notation in matchesKeyInQuery 2017-11-30 12:40:39 -08:00
Henrik Malmberg 741f869140 Allows to use dot-notation to match against a complex structure when using matchesKeyInQuery 2017-11-30 12:40:39 -08:00
Vitaly Tomilov 9d1d8515fc pg-promise refactoring (#4401)
initial refactoring of `pg-promise` code.
2017-11-30 08:30:15 +07:00
Addison Elliott 9eed81e636 Remove nsp check from Travis YML (#4403)
nsp is done through a GitHub PR hook and managed separately from Travis CI
2017-11-29 16:01:27 -05:00
Florent Vilmart e2d33678a9 Makes all scripts cross platforms (#4383) 2017-11-27 23:22:07 -05:00
8 changed files with 158 additions and 32 deletions
+3 -1
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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"
},
+85
View File
@@ -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();
}
}))
})
});
});
+30
View File
@@ -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
View File
@@ -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'])) {