Compare commits

...
17 Commits
Author SHA1 Message Date
Natan Rolnik 7a3c9cd33a Changelog 2.3.4 (#3533) 2017-02-19 08:44:33 -05:00
Florent Vilmart 193e5a4278 Make sure we don't treat dot notation keys as topLevel atoms (#3531)
Fixing GeoPoints and Files in _GlobalConfig
2017-02-19 12:07:54 +02:00
Jeremy Louie 6ae0675010 Use flushdb instead of flushall in RedisCacheAdapter (#3523) 2017-02-18 19:36:43 -05:00
David Starke a520ea0243 Fix LiveQuery unsafe user (#3525)
* LiveQuery should not use unsafe user setting

* server should issue queries with the master key
2017-02-18 19:36:20 -05:00
Greenkeeper 58bdeeee2d chore(package): update pg-promise to version 5.5.7 (#3528)
https://greenkeeper.io/
2017-02-18 19:24:32 -05:00
Vitaly Tomilov fc7f9f76f5 removing inline sql. (#3529)
* removing inline sql.

* Update index.js

trying to fix indentation.

* Update index.js

commenting out console output for errors.

* Rename json-object-set-key.sql to json-object-set-keys.sql

* Update index.js

* Update add-unique.sql

correcting sql.

* Update add.sql

correcting sql.

* Update contains-all.sql

correcting sql.

* Update contains.sql

correcting sql.

* Update remove.sql

correcting sql.

* Update json-object-set-keys.sql

fixing sql reserved words.
2017-02-18 14:26:25 -05:00
Greenkeeper ef8d534679 chore(package): update babel-core to version 6.23.1 (#3512)
https://greenkeeper.io/
2017-02-17 10:18:12 -08:00
Greenkeeper 1c9852e53f chore(package): update babel-core to version 6.23.0 (#3509)
https://greenkeeper.io/
2017-02-17 10:16:43 -08:00
Greenkeeper 3f0de0c2ed chore(package): update babel-register to version 6.23.0 (#3510)
https://greenkeeper.io/
2017-02-17 10:14:27 -08:00
Greenkeeper eccdeccb59 chore(package): update babel-cli to version 6.23.0 (#3511)
https://greenkeeper.io/
2017-02-17 10:14:10 -08:00
Greenkeeper f16f8423db chore(package): update mongodb to version 2.2.24 (#3513)
https://greenkeeper.io/
2017-02-17 10:13:55 -08:00
Greenkeeper bd60a5c1d1 chore(package): update body-parser to version 1.16.1 (#3499)
https://greenkeeper.io/
2017-02-11 15:03:05 -08:00
Robin Naundorf 40b9f953ca Fix small display errors (#3500)
* Add missing '$' to declare variable
* Move \n to correct position
2017-02-11 12:24:09 -05:00
Héctor Ramos 15eafd4dcc Update ISSUE_TEMPLATE.md 2017-02-10 11:56:58 -08:00
Héctor Ramos 11cde12245 Merge pull request #3497 from acinader/allow-empty-client-key
Allow empty client key
2017-02-09 16:16:49 -08:00
Arthur Cinader 5861996cb0 explicitly check if auth keys are undefined
Simply checking if they are truthy causes a false negative
if the value is ''.
2017-02-09 15:20:10 -08:00
Arthur Cinader ca70ad0141 Add a unit test to fail when clientKey='' 2017-02-09 14:50:28 -08:00
20 changed files with 231 additions and 145 deletions
+9 -33
View File
@@ -1,50 +1,28 @@
Please read the following instructions carefully.
We use GitHub Issues for bugs.
Check out https://github.com/ParsePlatform/parse-server/issues/1271 for an ideal bug report.
The closer your issue report is to that one, the more likely we are to be able to help, and the more likely we will be to fix the issue quickly!
If you have a non-bug question, ask on Stack Overflow or Server Fault:
- https://stackoverflow.com/questions/tagged/parse.com
- https://serverfault.com/tags/parse
Many members of the community use Stack Overflow and Server Fault to ask questions.
Read through the existing questions or ask your own!
- Stack Overflow: http://stackoverflow.com/questions/tagged/parse.com
- Server Fault: https://serverfault.com/tags/parse
You may also search through existing issues before opening a new one: https://github.com/ParsePlatform/Parse-Server/issues?utf8=%E2%9C%93&q=is%3Aissue
For database migration help, please file a bug report at https://parse.com/help#report
Make sure these boxes are checked before submitting your issue -- thanks for reporting issues back to Parse Server!
- [ ] You've met the prerequisites: https://github.com/ParsePlatform/parse-server/wiki/Parse-Server-Guide#prerequisites.
- [ ] You're running the latest version of Parse Server: https://github.com/ParsePlatform/parse-server/releases
- [ ] You've searched through existing issues: https://github.com/ParsePlatform/Parse-Server/issues?utf8=%E2%9C%93&q=is%3Aissue Chances are that your issue has been reported or resolved before.
- [ ] You have filled out every section below. Issues without sufficient information are more likely to be closed.
--
--- Please use this template. If you don't use this template, your issue may be closed without comment. ---
### Issue Description
[DELETE EVERYTHING ABOVE THIS LINE BEFORE SUBMITTING YOUR ISSUE]
Describe your issue in as much detail as possible.
[FILL THIS OUT]
### Steps to reproduce
Please include a detailed list of steps that reproduce the issue. Include curl commands when applicable.
1. [FILL THIS OUT]
2. [FILL THIS OUT]
3. [FILL THIS OUT]
#### Expected Results
[FILL THIS OUT]
What you expected to happen.
#### Actual Outcome
[FILL THIS OUT]
What is happening instead.
### Environment Setup
@@ -62,6 +40,4 @@ Please include a detailed list of steps that reproduce the issue. Include curl c
### Logs/Trace
You can turn on additional logging by configuring VERBOSE=1 in your environment.
[FILL THIS OUT]
Include all relevant logs. You can turn on additional logging by configuring VERBOSE=1 in your environment.
+15 -1
View File
@@ -1,5 +1,19 @@
## Parse Server Changelog
### 2.3.4
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.3.3...2.3.4)
#### Bug Fixes
* Allow empty client key
(#3497), thanks to [Arthur Cinader](https://github.com/acinader)
* Fix LiveQuery unsafe user
(#3525), thanks to [David Starke](https://github.com/dstarke)
* Use `flushdb` instead of `flushall` in RedisCacheAdapter
(#3523), thanks to [Jeremy Louie](https://github.com/JeremyPlease)
* Fix saving GeoPoints and Files in `_GlobalConfig` (Make sure we don't treat
dot notation keys as topLevel atoms)
(#3531), thanks to [Florent Vilmart](https://github.com/flovilmart)
### 2.3.3
[Full Changelog](https://github.com/ParsePlatform/parse-server/compare/2.3.2...2.3.3)
@@ -101,7 +115,7 @@ Postgres support requires v9.5
* Better Postgres support, thanks to [Kulshekhar Kabra](https://github.com/kulshekhar)
* Logs the function name when failing (#2963), thanks to [Michael Helvey](https://github.com/michaelhelvey)
* CLI: forces closing the connections with SIGINT/SIGTERM (#2964), thanks to [Kulshekhar Kabra](https://github.com/kulshekhar)
* Reduce the number of calls to the _SCHEMA table (#2912), thanks to [Steven Shipton](https://github.com/steven-supersolid)
* Reduce the number of calls to the `_SCHEMA` table (#2912), thanks to [Steven Shipton](https://github.com/steven-supersolid)
* LiveQuery: Support for Role ACL's, thanks to [Aaron Blondeau](https://github.com/aaron-blondeau-dose)
#### Bug Fixes
+2 -2
View File
@@ -95,10 +95,10 @@ confirm 'Y' 'Do you want to continue? (Y/n): '
check_node
check_npm
printf "Setting up parse-server in %s" "${INSTALL_DIR}\n"
printf "Setting up parse-server in %s\n" "${INSTALL_DIR}"
if [ -d "${INSTALL_DIR}" ]; then
echo "{CHECK} ${INSTALL_DIR} exists"
echo "${CHECK} ${INSTALL_DIR} exists"
else
mkdir -p "${INSTALL_DIR}"
echo "${CHECK} Created ${INSTALL_DIR}"
+8 -7
View File
@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "2.3.3",
"version": "2.3.4",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
@@ -19,7 +19,7 @@
"license": "BSD-3-Clause",
"dependencies": {
"bcryptjs": "2.4.3",
"body-parser": "1.16.0",
"body-parser": "1.16.1",
"commander": "2.9.0",
"deepcopy": "0.6.3",
"express": "4.14.1",
@@ -27,14 +27,14 @@
"lodash": "4.17.4",
"lru-cache": "4.0.2",
"mime": "1.3.4",
"mongodb": "2.2.22",
"mongodb": "2.2.24",
"multer": "1.3.0",
"parse": "1.9.2",
"parse-server-fs-adapter": "1.0.1",
"parse-server-push-adapter": "1.2.0",
"parse-server-s3-adapter": "1.0.6",
"parse-server-simple-mailgun-adapter": "1.0.0",
"pg-promise": "5.5.6",
"pg-promise": "5.5.7",
"redis": "2.6.5",
"request": "2.79.0",
"semver": "5.2.0",
@@ -44,14 +44,15 @@
"ws": "2.0.3"
},
"devDependencies": {
"babel-cli": "6.22.2",
"babel-core": "6.22.1",
"babel-cli": "6.23.0",
"babel-core": "6.23.1",
"babel-eslint": "^7.1.1",
"babel-plugin-syntax-flow": "6.13.0",
"babel-plugin-transform-flow-strip-types": "6.22.0",
"babel-preset-es2015": "6.22.0",
"babel-preset-stage-0": "6.22.0",
"babel-register": "6.22.0",
"babel-register": "6.23.0",
"bcrypt-nodejs": "0.0.3",
"cross-env": "3.1.4",
"deep-diff": "0.3.4",
+13
View File
@@ -79,6 +79,19 @@ describe('middlewares', () => {
});
});
it('should succeed when client key supplied but empty', (done) => {
AppCache.put(fakeReq.body._ApplicationId, {
clientKey: '',
masterKey: 'masterKey',
restAPIKey: 'restAPIKey'
});
fakeReq.headers['x-parse-client-key'] = '';
middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
expect(fakeRes.status).not.toHaveBeenCalled();
done();
});
});
it('should succeed when no keys are configured and none supplied', (done) => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey'
+43
View File
@@ -56,6 +56,49 @@ describe('a GlobalConfig', () => {
});
});
it('can add and retrive files', (done) => {
request.put({
url : 'http://localhost:8378/1/config',
json : true,
body : { params: { file: { __type: 'File', name: 'name', url: 'http://url' } } },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key' : 'test'
}
}, (error, response, body) => {
expect(response.statusCode).toEqual(200);
expect(body.result).toEqual(true);
Parse.Config.get().then((res) => {
const file = res.get('file');
expect(file.name()).toBe('name');
expect(file.url()).toBe('http://url');
done();
});
});
});
it('can add and retrive Geopoints', (done) => {
const geopoint = new Parse.GeoPoint(10,-20);
request.put({
url : 'http://localhost:8378/1/config',
json : true,
body : { params: { point: geopoint.toJSON() } },
headers: {
'X-Parse-Application-Id': 'test',
'X-Parse-Master-Key' : 'test'
}
}, (error, response, body) => {
expect(response.statusCode).toEqual(200);
expect(body.result).toEqual(true);
Parse.Config.get().then((res) => {
const point = res.get('point');
expect(point.latitude).toBe(10);
expect(point.longitude).toBe(-20);
done();
});
});
});
it('properly handles delete op', (done) => {
request.put({
url : 'http://localhost:8378/1/config',
+8 -10
View File
@@ -4,13 +4,14 @@ describe('SessionTokenCache', function() {
beforeEach(function(done) {
var Parse = require('parse/node');
// Mock parse
var mockUser = {
become: jasmine.createSpy('become').and.returnValue(Parse.Promise.as({
id: 'userId'
}))
}
jasmine.mockLibrary('parse/node', 'User', mockUser);
spyOn(Parse, "Query").and.returnValue({
first: jasmine.createSpy("first").and.returnValue(Parse.Promise.as(new Parse.Object("_Session", {
user: new Parse.User({id:"userId"})
}))),
equalTo: function(){}
})
done();
});
@@ -46,7 +47,4 @@ describe('SessionTokenCache', function() {
});
});
afterEach(function() {
jasmine.restoreLibrary('parse/node', 'User');
});
});
+1 -1
View File
@@ -71,7 +71,7 @@ export class RedisCacheAdapter {
debug('clear');
this.p = this.p.then(() => {
return new Promise((resolve) => {
this.client.flushall(function() {
this.client.flushdb(function() {
resolve();
});
});
+4 -1
View File
@@ -89,6 +89,9 @@ const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSc
if (timeField && (typeof value === 'string')) {
value = new Date(value);
}
if (restKey.indexOf('.') > 0) {
return {key, value: restValue}
}
return {key, value};
}
@@ -98,7 +101,7 @@ const transformKeyValueForUpdate = (className, restKey, restValue, parseFormatSc
return {key, value};
}
// Handle update operators
// Handle update operators
if (typeof restValue === 'object' && '__op' in restValue) {
return {key, value: transformUpdateOperator(restValue, false)};
}
@@ -1,6 +1,7 @@
import { createClient } from './PostgresClient';
import Parse from 'parse/node';
import _ from 'lodash';
import sql from './sql';
const PostgresRelationDoesNotExistError = '42P01';
const PostgresDuplicateRelationError = '42P07';
@@ -1171,22 +1172,22 @@ export class PostgresStorageAdapter {
});
/* eslint-disable no-console */
promises = promises.concat([
this._client.any(json_object_set_key).catch((err) => {
this._client.none(sql.misc.jsonObjectSetKeys).catch((err) => {
console.error(err);
}),
this._client.any(array_add).catch((err) => {
this._client.none(sql.array.add).catch((err) => {
console.error(err);
}),
this._client.any(array_add_unique).catch((err) => {
this._client.none(sql.array.addUnique).catch((err) => {
console.error(err);
}),
this._client.any(array_remove).catch((err) => {
this._client.none(sql.array.remove).catch((err) => {
console.error(err);
}),
this._client.any(array_contains_all).catch((err) => {
this._client.none(sql.array.containsAll).catch((err) => {
console.error(err);
}),
this._client.any(array_contains).catch((err) => {
this._client.none(sql.array.contains).catch((err) => {
console.error(err);
})
]);
@@ -1270,84 +1271,5 @@ function literalizeRegexPart(s) {
);
}
// Function to set a key on a nested JSON document
const json_object_set_key = 'CREATE OR REPLACE FUNCTION "json_object_set_key"(\
"json" jsonb,\
"key_to_set" TEXT,\
"value_to_set" anyelement\
)\
RETURNS jsonb \
LANGUAGE sql \
IMMUTABLE \
STRICT \
AS $function$\
SELECT concat(\'{\', string_agg(to_json("key") || \':\' || "value", \',\'), \'}\')::jsonb\
FROM (SELECT *\
FROM jsonb_each("json")\
WHERE "key" <> "key_to_set"\
UNION ALL\
SELECT "key_to_set", to_json("value_to_set")::jsonb) AS "fields"\
$function$;'
const array_add = `CREATE OR REPLACE FUNCTION "array_add"(
"array" jsonb,
"values" jsonb
)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT array_to_json(ARRAY(SELECT unnest(ARRAY(SELECT DISTINCT jsonb_array_elements("array")) || ARRAY(SELECT jsonb_array_elements("values")))))::jsonb;
$function$;`;
const array_add_unique = `CREATE OR REPLACE FUNCTION "array_add_unique"(
"array" jsonb,
"values" jsonb
)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT array_to_json(ARRAY(SELECT DISTINCT unnest(ARRAY(SELECT DISTINCT jsonb_array_elements("array")) || ARRAY(SELECT DISTINCT jsonb_array_elements("values")))))::jsonb;
$function$;`;
const array_remove = `CREATE OR REPLACE FUNCTION "array_remove"(
"array" jsonb,
"values" jsonb
)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT array_to_json(ARRAY(SELECT * FROM jsonb_array_elements("array") as elt WHERE elt NOT IN (SELECT * FROM (SELECT jsonb_array_elements("values")) AS sub)))::jsonb;
$function$;`;
const array_contains_all = `CREATE OR REPLACE FUNCTION "array_contains_all"(
"array" jsonb,
"values" jsonb
)
RETURNS boolean
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT RES.CNT = jsonb_array_length("values") FROM (SELECT COUNT(*) as CNT FROM jsonb_array_elements("array") as elt WHERE elt IN (SELECT jsonb_array_elements("values"))) as RES ;
$function$;`;
const array_contains = `CREATE OR REPLACE FUNCTION "array_contains"(
"array" jsonb,
"values" jsonb
)
RETURNS boolean
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT RES.CNT >= 1 FROM (SELECT COUNT(*) as CNT FROM jsonb_array_elements("array") as elt WHERE elt IN (SELECT jsonb_array_elements("values"))) as RES ;
$function$;`;
export default PostgresStorageAdapter;
module.exports = PostgresStorageAdapter; // Required for tests
@@ -0,0 +1,11 @@
CREATE OR REPLACE FUNCTION array_add_unique(
"array" jsonb,
"values" jsonb
)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT array_to_json(ARRAY(SELECT DISTINCT unnest(ARRAY(SELECT DISTINCT jsonb_array_elements("array")) || ARRAY(SELECT DISTINCT jsonb_array_elements("values")))))::jsonb;
$function$;
@@ -0,0 +1,11 @@
CREATE OR REPLACE FUNCTION array_add(
"array" jsonb,
"values" jsonb
)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT array_to_json(ARRAY(SELECT unnest(ARRAY(SELECT DISTINCT jsonb_array_elements("array")) || ARRAY(SELECT jsonb_array_elements("values")))))::jsonb;
$function$;
@@ -0,0 +1,11 @@
CREATE OR REPLACE FUNCTION array_contains_all(
"array" jsonb,
"values" jsonb
)
RETURNS boolean
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT RES.CNT = jsonb_array_length("values") FROM (SELECT COUNT(*) as CNT FROM jsonb_array_elements("array") as elt WHERE elt IN (SELECT jsonb_array_elements("values"))) as RES;
$function$;
@@ -0,0 +1,11 @@
CREATE OR REPLACE FUNCTION array_contains(
"array" jsonb,
"values" jsonb
)
RETURNS boolean
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT RES.CNT >= 1 FROM (SELECT COUNT(*) as CNT FROM jsonb_array_elements("array") as elt WHERE elt IN (SELECT jsonb_array_elements("values"))) as RES;
$function$;
@@ -0,0 +1,11 @@
CREATE OR REPLACE FUNCTION array_remove(
"array" jsonb,
"values" jsonb
)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT array_to_json(ARRAY(SELECT * FROM jsonb_array_elements("array") as elt WHERE elt NOT IN (SELECT * FROM (SELECT jsonb_array_elements("values")) AS sub)))::jsonb;
$function$;
@@ -0,0 +1,32 @@
'use strict';
var QueryFile = require('pg-promise').QueryFile;
var path = require('path');
module.exports = {
array: {
add: sql('array/add.sql'),
addUnique: sql('array/add-unique.sql'),
contains: sql('array/contains.sql'),
containsAll: sql('array/contains-all.sql'),
remove: sql('array/remove.sql')
},
misc: {
jsonObjectSetKeys: sql('misc/json-object-set-keys.sql')
}
};
///////////////////////////////////////////////
// Helper for linking to external query files;
function sql(file) {
var fullPath = path.join(__dirname, file); // generating full path;
var qf = new QueryFile(fullPath, {minify: true});
if (qf.error) {
throw qf.error;
}
return qf;
}
@@ -0,0 +1,19 @@
-- Function to set a key on a nested JSON document
CREATE OR REPLACE FUNCTION json_object_set_key(
"json" jsonb,
key_to_set TEXT,
value_to_set anyelement
)
RETURNS jsonb
LANGUAGE sql
IMMUTABLE
STRICT
AS $function$
SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::jsonb
FROM (SELECT *
FROM jsonb_each("json")
WHERE key <> key_to_set
UNION ALL
SELECT key_to_set, to_json("value_to_set")::jsonb) AS fields
$function$;
+1 -2
View File
@@ -37,7 +37,6 @@ class ParseLiveQueryServer {
// Initialize Parse
Parse.Object.disableSingleInstance();
Parse.User.enableUnsafeCurrentUser();
const serverURL = config.serverURL || Parse.serverURL;
Parse.serverURL = serverURL;
@@ -363,7 +362,7 @@ class ParseLiveQueryServer {
// Then get the user's roles
var rolesQuery = new Parse.Query(Parse.Role);
rolesQuery.equalTo("users", user);
return rolesQuery.find();
return rolesQuery.find({useMasterKey:true});
}).
then((roles) => {
+12 -1
View File
@@ -2,6 +2,17 @@ import Parse from 'parse/node';
import LRU from 'lru-cache';
import logger from '../logger';
function userForSessionToken(sessionToken){
var q = new Parse.Query("_Session");
q.equalTo("sessionToken", sessionToken);
return q.first({useMasterKey:true}).then(function(session){
if(!session){
return Parse.Promise.error("No session found for session token");
}
return session.get("user");
});
}
class SessionTokenCache {
cache: Object;
@@ -21,7 +32,7 @@ class SessionTokenCache {
logger.verbose('Fetch userId %s of sessionToken %s from Cache', userId, sessionToken);
return Parse.Promise.as(userId);
}
return Parse.User.become(sessionToken).then((user) => {
return userForSessionToken(sessionToken).then((user) => {
logger.verbose('Fetch userId %s of sessionToken %s from Parse', user.id, sessionToken);
const userId = user.id;
this.cache.set(sessionToken, userId);
+2 -2
View File
@@ -122,10 +122,10 @@ export function handleParseHeaders(req, res, next) {
// to preserve original behavior.
const keys = ["clientKey", "javascriptKey", "dotNetKey", "restAPIKey"];
const oneKeyConfigured = keys.some(function(key) {
return req.config[key];
return req.config[key] !== undefined;
});
const oneKeyMatches = keys.some(function(key){
return req.config[key] && info[key] == req.config[key];
return req.config[key] !== undefined && info[key] === req.config[key];
});
if (oneKeyConfigured && !oneKeyMatches) {