diff --git a/.travis.yml b/.travis.yml
index 44e903339d..f5383e20e0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,6 +17,7 @@ branches:
- master
- "/^[0-9]+.[0-9]+.[0-9]+(-.*)?$/"
- 3.x
+ - 4.x
- "/^greenkeeper/.*$/"
cache:
directories:
@@ -54,7 +55,7 @@ after_script:
jobs:
allow_failures:
- env: NODE_VERSION=12.12.0
- include:
+ include:
- stage: release
node_js: '10'
env:
@@ -64,18 +65,18 @@ jobs:
- "./release_docs.sh"
deploy:
- provider: pages
- cleanup: true
+ skip_cleanup: true
token:
secure: YU2lUqmW036AHBRu7xO/AwEeQ900Q/5O6FL96ZqWEfD7Gadaq4iapkNvhPR0HcZYRHqQV2/2LCHVnhd0dbj0ShkmVIHdQE7O+MF+j0v0GIVc8FPTPe1/d2Hy4apSWNe6FeCrYKVeliAu+ZqvNuFLhVmYvIeJdlJrMGOb6P76UZXRDv2srXhq4uBcCVUJuTajyyd5ttJfcNapymTP+xzEDYc7Hr4LJaubmv/wVD/xwPBbvfYFuBqysUpkPKi/ODlQbB0ybYh2fFnX71WUyFUGbtB5xI9ma951Zp4v3t73c31uUl27dJaHzO62EtVTdcUKAa804EtAtsvpeJWMVWKUgigm9UZcXdEwKa79fl5nLaq34lrSktAYOkexwPqYj+vbS6sn52JrluSxLE+4cEke7tYbnJ9X12SAQXKgcXY3n30+6gKf9RVqYdlNsYpEAqKVIDb6SlEkk86dP+uIg/XXb5RKEwxbDXnb6xdl9JRc+GTbeeY3/vg2h9QTdmFVblPtBhHrNenQYP/BS0n+EfUnAIBKqRmQgyhao3SiY5FMACM8higI2Lvvhpq46pDhXqsexYCz5F008C6YXGDh5gC93rJFec0pjh55DNdQu6uw3YMQ5jtf3QUXoPAoMFud3cTulMlnjC1WZG8QbqER8dzUZ4TcaQUdzribJI/mRriheBg=
local_dir: docs/
on:
all_branches: true
- provider: npm
+ skip_cleanup: true
api_token:
secure: Yrng6jsnMAtzrrln9DwRuY4xpcxl/WYS/1A5fckyQF6DsLmNlvqVtu3MWF+zdJgANF63G3jIee11tNBpYRecHl8FjPGwO0kc1vEgvIRVnveyR0bwIIDH6s/mR9dg4rZikflwG4XExsLyaQd7ko8aTIOIayfxJiv/u0yqwuBuBW2bFrL/41b1cKGK5+Iq2a+PdFENUPenKXISkACGaMnQF4Y/KVF98UwCiGLf957yFWc2sD6TFbjNDbAENSccvg1J3fBb+djbtzKzldl29ntp2mKVGSVASiKCRa6hSfgQulHiidFqFIKgpYJ1uATRr3UFr/NRVt1WbrgLnzY74OCX7y02c/xiYQMMEPRl/P8hHJu+KjQ3PBWsBvmsRN0QMUJv3PEjUPE3AY8sw49NoxiJZzJr7574vUBuM/dk0byYI6K/gwmUrPIhQjljzZqinS8JJJ4FjGjCdzXRhT5Q+PZvt5bF1rMOZXayNJZa+cICtmiJoU3vO2Yf6TXC3wY1veKvmcs24nws5lp4keJePTKAi1Ig7VoSxwAlgs3EGANIh7oBKx9zWnXQYMdmxbYsvLXAWcnnM+PcSCvooLmXWso3BQBeRuUUSS2oLaBpsFMsiniLNbA1cW4fwMkgUGz4oEDRi8wTD89E/1J2oNxzqtWRPawcVKpMpnBbDJHrWJPEHMw=
email:
secure: lomzDl71N995SzRczm7VE9OZE+PzMo4X8t3zU97agu/FMH5Qcj8BLwE+uVDTnA9Vblyj62ZsFKsNjP2Qp53Vcd+jHM4EJNWNRZYpEMIRO3LngX43r83qoFEHUvPu9s1oaa04a6FojcsJx0wl6B6Ke2AX74MXnJDLb9iZBy1mkpLUMVccVhSfhdoIzhkq3dhUw+6d8C024tNMHcgDW3VnRAsWFtiL7dCMpjLOdI+UxlkeGkQkxXXuRsZ0ZdjoSoM8NSkiYMc8x6EnekyRDoHTujX3OFxuU6+GAjrUmVzNmJWrBIqHVb0DXBQxjEaG3d/cNu5UsQyZYq5sxRRH0BaLs7F4oIQg95etasEtTtUkmsZ3pshVlsweiLU366UdbfuAf5hrJjqLrU12BKZyLjaAwyeKz031r8dA4sJtGIp5uVdXobQQTH6r958A88byJ20uaYSqhqjhZo3hkWXIQP0WQN2Ej/g57HbVNLB/nPKkMILfk/tpp7nBDLT0QrjbZxeo1dwCHqsBEV6z7ZyWyFf4xwpDsir4txL4t8ElzeGdlACjCqAJvIh5w9YzfrwijtoVMvvP6pWvn/iI640d4rsdIDe8egxgqZ1R/TMd/tdHYX+eI+ZfFmCVGj36/uXwdG7KIoIZVjRQ2tvWr9ZuydEPWPSRVGT4ycFeu6wbm3elM3I=
- cleanup: true
on:
tags: true
all_branches: true
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1488539480..157b2bd0b3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,44 @@
## Parse Server Changelog
### master
-
-[Full Changelog](https://github.com/parse-community/parse-server/compare/3.10.0...master)
-- FIX: FIX: Prevent new usernames or emails that clash with existing users' email or username if it only differs by case. For example, don't allow a new user with the name 'Jane' if we already have a user 'jane'. [#5634](https://github.com/parse-community/parse-server/pull/5634). Thanks to [Arthur Cinader](https://github.com/acinader)
+[Full Changelog](https://github.com/parse-community/parse-server/compare/4.1.0...master)
+
+### 4.1.0
+[Full Changelog](https://github.com/parse-community/parse-server/compare/4.0.2...4.1.0)
+_SECURITY RELEASE_: see [advisory](https://github.com/parse-community/parse-server/security/advisories/GHSA-h4mf-75hf-67w4) for details
+- SECURITY FIX: Patch Regex vulnerabilities. See [3a3a5ee](https://github.com/parse-community/parse-server/commit/3a3a5eee5ffa48da1352423312cb767de14de269). Special thanks to [W0lfw00d](https://github.com/W0lfw00d) for identifying and [responsibly reporting](https://github.com/parse-community/parse-server/blob/master/SECURITY.md) the vulnerability. Thanks to [Antonio Davi Macedo Coelho de Castro](https://github.com/davimacedo) for the speedy fix.
+
+### 4.0.2
+[Full Changelog](https://github.com/parse-community/parse-server/compare/4.0.1...4.0.2)
+__BREAKING CHANGES:__
+1. Remove Support for Mongo 3.2 & 3.4. The new minimum supported version is Mongo 3.6.
+2. Change username and email validation to be case insensitive. This change should be transparent in most use cases. The validation behavior should now behave 'as expected'. See [#6414](https://github.com/parse-community/parse-server/pull/6414) for details.
+
+FIX: attempt to get travis to deploy to npmjs again. See [#6475](https://github.com/parse-community/parse-server/pull/6457). Thanks to [Arthur Cinader](https://github.com/acinader).
+
+### 4.0.1
+[Full Changelog](https://github.com/parse-community/parse-server/compare/4.0.0...4.0.1)
+- FIX: correct 'new' travis config to properly deploy. See [#6452](https://github.com/parse-community/parse-server/pull/6452). Thanks to [Arthur Cinader](https://github.com/acinader).
+- FIX: Better message on not allowed to protect default fields. See [#6439](https://github.com/parse-community/parse-server/pull/6439).Thanks to [Old Grandpa](https://github.com/BufferUnderflower)
+
+### 4.0
+[Full Changelog](https://github.com/parse-community/parse-server/compare/3.10.0...4.0.0)
+- NEW: add hint option to Parse.Query [#6322](https://github.com/parse-community/parse-server/pull/6322). Thanks to [Steve Stencil](https://github.com/stevestencil)
+- FIX: CLP objectId size validation fix [#6332](https://github.com/parse-community/parse-server/pull/6332). Thanks to [Old Grandpa](https://github.com/BufferUnderflower)
+- FIX: Add volumes to Docker command [#6356](https://github.com/parse-community/parse-server/pull/6356). Thanks to [Kasra Bigdeli](https://github.com/githubsaturn)
+- NEW: GraphQL 3rd Party LoginWith Support [#6371](https://github.com/parse-community/parse-server/pull/6371). Thanks to [Antoine Cormouls](https://github.com/Moumouls)
+- FIX: GraphQL Geo Queries [#6363](https://github.com/parse-community/parse-server/pull/6363). Thanks to [Antoine Cormouls](https://github.com/Moumouls)
+- NEW: GraphQL Nested File Upload [#6372](https://github.com/parse-community/parse-server/pull/6372). Thanks to [Antoine Cormouls](https://github.com/Moumouls)
+- NEW: Granular CLP pointer permissions [#6352](https://github.com/parse-community/parse-server/pull/6352). Thanks to [Old Grandpa](https://github.com/BufferUnderflower)
+- FIX: Add missing colon for customPages [#6393](https://github.com/parse-community/parse-server/pull/6393). Thanks to [Jerome De Leon](https://github.com/JeromeDeLeon)
+NEW: `afterLogin` cloud code hook [#6387](https://github.com/parse-community/parse-server/pull/6387). Thanks to [David Corona](https://github.com/davesters)
+- FIX: __BREAKING CHANGE__ Prevent new usernames or emails that clash with existing users' email or username if it only differs by case. For example, don't allow a new user with the name 'Jane' if we already have a user 'jane'. [#5634](https://github.com/parse-community/parse-server/pull/5634). Thanks to [Arthur Cinader](https://github.com/acinader)
+- FIX: Support Travis CI V2. [#6414](https://github.com/parse-community/parse-server/pull/6414). Thanks to [Diamond Lewis](https://github.com/dplewis)
+- FIX: Prevent crashing on websocket error. [#6418](https://github.com/parse-community/parse-server/pull/6418). Thanks to [Diamond Lewis](https://github.com/dplewis)
+- NEW: Allow protectedFields for Authenticated users and Public. [$6415](https://github.com/parse-community/parse-server/pull/6415). Thanks to [Old Grandpa](https://github.com/BufferUnderflower)
+- FIX: Correct bug in determining GraphQL pointer errors when mutating. [#6413](https://github.com/parse-community/parse-server/pull/6431). Thanks to [Antoine Cormouls](https://github.com/Moumouls)
+- NEW: Allow true GraphQL Schema Customization. [#6360](https://github.com/parse-community/parse-server/pull/6360). Thanks to [Antoine Cormouls](https://github.com/Moumouls)
+- __BREAKING CHANGE__: Remove Support for Mongo version < 3.6 [#6445](https://github.com/parse-community/parse-server/pull/6445). Thanks to [Arthur Cinader](https://github.com/acinader)
### 3.10.0
[Full Changelog](https://github.com/parse-community/parse-server/compare/3.9.0...3.10.0)
@@ -21,8 +56,8 @@
- NEW: GraphQL: DX Relational Where Query [#6255](https://github.com/parse-community/parse-server/pull/6255). Thanks to [Antoine Cormouls](https://github.com/Moumouls)
- CHANGE: test against Postgres 11 [#6260](https://github.com/parse-community/parse-server/pull/6260). Thanks to [Diamond Lewis](https://github.com/dplewis)
- CHANGE: test against Postgres 11 [#6260](https://github.com/parse-community/parse-server/pull/6260). Thanks to [Diamond Lewis](https://github.com/dplewis)
-- NEW: GraphQL alias for mutations in classConfigs [#6258](https://github.com/parse-community/parse-server/pull/6258). Thanks to [Old Grandpa ](https://github.com/BufferUnderflower)
-- NEW: GraphQL classConfig query alias [#6257](https://github.com/parse-community/parse-server/pull/6257). Thanks to [Old Grandpa ](https://github.com/BufferUnderflower)
+- NEW: GraphQL alias for mutations in classConfigs [#6258](https://github.com/parse-community/parse-server/pull/6258). Thanks to [Old Grandpa](https://github.com/BufferUnderflower)
+- NEW: GraphQL classConfig query alias [#6257](https://github.com/parse-community/parse-server/pull/6257). Thanks to [Old Grandpa](https://github.com/BufferUnderflower)
- NEW: Allow validateFilename to return a string or Parse Error [#6246](https://github.com/parse-community/parse-server/pull/6246). Thanks to [Mike Patnode](https://github.com/mpatnode)
- NEW: Relay Spec [#6089](https://github.com/parse-community/parse-server/pull/6089). Thanks to [Antonio Davi Macedo Coelho de Castro](https://github.com/davimacedo)
- CHANGE: Set default ACL for GraphQL [#6249](https://github.com/parse-community/parse-server/pull/6249). Thanks to [Antoine Cormouls](https://github.com/Moumouls)
diff --git a/README.md b/README.md
index e73f9b9044..b935b83e03 100644
--- a/README.md
+++ b/README.md
@@ -18,8 +18,6 @@
-
-
@@ -80,7 +78,7 @@ The fastest and easiest way to get started is to run MongoDB and Parse Server lo
Before you start make sure you have installed:
-- [NodeJS](https://www.npmjs.com/) that includes `npm`
+- [NodeJS](https://www.npmjs.com/) that includes `npm`
- [MongoDB](https://www.mongodb.com/) or [PostgreSQL](https://www.postgresql.org/)
- Optionally [Docker](https://www.docker.com/)
@@ -337,7 +335,7 @@ It’s possible to change the default pages of the app and redirect the user to
```js
var server = ParseServer({
...otherOptions,
-
+
customPages: {
passwordResetSuccess: "http://yourapp.com/passwordResetSuccess",
verifyEmailSuccess: "http://yourapp.com/verifyEmailSuccess",
diff --git a/package-lock.json b/package-lock.json
index 957b5f97da..36d463bbb9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "parse-server",
- "version": "3.10.0",
+ "version": "4.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -25,9 +25,9 @@
},
"dependencies": {
"@types/node": {
- "version": "10.17.15",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.15.tgz",
- "integrity": "sha512-daFGV9GSs6USfPgxceDA8nlSe48XrVCJfDeYm7eokxq/ye7iuOH87hKXgMtEAVLFapkczbZsx868PMDT1Y0a6A=="
+ "version": "10.17.17",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.17.tgz",
+ "integrity": "sha512-gpNnRnZP3VWzzj5k3qrpRC6Rk3H/uclhAVo1aIvwzK5p5cOrs9yEyQ8H/HBsBY0u5rrWxXEiVPQ0dEB6pkjE8Q=="
}
}
},
@@ -79,9 +79,9 @@
}
},
"@babel/compat-data": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.8.4.tgz",
- "integrity": "sha512-lHLhlsvFjJAqNU71b7k6Vv9ewjmTXKvqaMv7n0G1etdCabWLw3nEYE8mmgoVOxMIFE07xOvo7H7XBASirX6Rrg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.8.6.tgz",
+ "integrity": "sha512-CurCIKPTkS25Mb8mz267vU95vy+TyUpnctEX2lV33xWNmHAfjruztgiPBbXZRh3xZZy1CYvGx6XfxyTVS+sk7Q==",
"dev": true,
"requires": {
"browserslist": "^4.8.5",
@@ -98,18 +98,18 @@
}
},
"@babel/core": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz",
- "integrity": "sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.6.tgz",
+ "integrity": "sha512-Sheg7yEJD51YHAvLEV/7Uvw95AeWqYPL3Vk3zGujJKIhJ+8oLw2ALaf3hbucILhKsgSoADOvtKRJuNVdcJkOrg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.4",
+ "@babel/generator": "^7.8.6",
"@babel/helpers": "^7.8.4",
- "@babel/parser": "^7.8.4",
- "@babel/template": "^7.8.3",
- "@babel/traverse": "^7.8.4",
- "@babel/types": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/template": "^7.8.6",
+ "@babel/traverse": "^7.8.6",
+ "@babel/types": "^7.8.6",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.1",
@@ -130,12 +130,12 @@
}
},
"@babel/generator": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz",
- "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz",
+ "integrity": "sha512-4bpOR5ZBz+wWcMeVtcf7FbjcFzCp+817z2/gHNncIRcM9MmKzUhtWCYAq27RAfUrAFwb+OCG1s9WEaVxfi6cjg==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3",
+ "@babel/types": "^7.8.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
@@ -182,43 +182,43 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/traverse": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz",
- "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz",
+ "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.4",
+ "@babel/generator": "^7.8.6",
"@babel/helper-function-name": "^7.8.3",
"@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.4",
- "@babel/types": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -279,9 +279,9 @@
},
"dependencies": {
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -302,9 +302,9 @@
},
"dependencies": {
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -335,12 +335,12 @@
}
},
"@babel/generator": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz",
- "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz",
+ "integrity": "sha512-4bpOR5ZBz+wWcMeVtcf7FbjcFzCp+817z2/gHNncIRcM9MmKzUhtWCYAq27RAfUrAFwb+OCG1s9WEaVxfi6cjg==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3",
+ "@babel/types": "^7.8.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
@@ -387,43 +387,43 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/traverse": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz",
- "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz",
+ "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.4",
+ "@babel/generator": "^7.8.6",
"@babel/helper-function-name": "^7.8.3",
"@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.4",
- "@babel/types": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -449,12 +449,12 @@
}
},
"@babel/helper-compilation-targets": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.4.tgz",
- "integrity": "sha512-3k3BsKMvPp5bjxgMdrFyq0UaEO48HciVrOVF0+lon8pp95cyJ2ujAh0TrBHNMnJGT2rr0iKOJPFFbSqjDyf/Pg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.6.tgz",
+ "integrity": "sha512-UrJdk27hKVJSnibFcUWYLkCL0ZywTUoot8yii1lsHJcvwrypagmYKjHLMWivQPm4s6GdyygCL8fiH5EYLxhQwQ==",
"dev": true,
"requires": {
- "@babel/compat-data": "^7.8.4",
+ "@babel/compat-data": "^7.8.6",
"browserslist": "^4.8.5",
"invariant": "^2.2.4",
"levenary": "^1.1.1",
@@ -470,11 +470,12 @@
}
},
"@babel/helper-create-regexp-features-plugin": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz",
- "integrity": "sha512-Gcsm1OHCUr9o9TcJln57xhWHtdXbA2pgQ58S0Lxlks0WMGNXuki4+GLfX0p+L2ZkINUGZvfkz8rzoqJQSthI+Q==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.6.tgz",
+ "integrity": "sha512-bPyujWfsHhV/ztUkwGHz/RPV1T1TDEsSZDsN42JPehndA+p1KKTh3npvTadux0ZhCrytx9tvjpWNowKby3tM6A==",
"dev": true,
"requires": {
+ "@babel/helper-annotate-as-pure": "^7.8.3",
"@babel/helper-regex": "^7.8.3",
"regexpu-core": "^4.6.0"
}
@@ -531,26 +532,26 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -580,12 +581,12 @@
}
},
"@babel/generator": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz",
- "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz",
+ "integrity": "sha512-4bpOR5ZBz+wWcMeVtcf7FbjcFzCp+817z2/gHNncIRcM9MmKzUhtWCYAq27RAfUrAFwb+OCG1s9WEaVxfi6cjg==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3",
+ "@babel/types": "^7.8.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
@@ -632,43 +633,43 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/traverse": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz",
- "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz",
+ "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.4",
+ "@babel/generator": "^7.8.6",
"@babel/helper-function-name": "^7.8.3",
"@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.4",
- "@babel/types": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -723,9 +724,9 @@
},
"dependencies": {
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -745,9 +746,9 @@
},
"dependencies": {
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -767,9 +768,9 @@
},
"dependencies": {
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -780,16 +781,17 @@
}
},
"@babel/helper-module-transforms": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.3.tgz",
- "integrity": "sha512-C7NG6B7vfBa/pwCOshpMbOYUmrYQDfCpVL/JCRu0ek8B5p8kue1+BCXpg2vOYs7w5ACB9GTOBYQ5U6NwrMg+3Q==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.6.tgz",
+ "integrity": "sha512-RDnGJSR5EFBJjG3deY0NiL0K9TO8SXxS9n/MPsbPK/s9LbQymuLNtlzvDiNS7IpecuL45cMeLVkA+HfmlrnkRg==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.8.3",
+ "@babel/helper-replace-supers": "^7.8.6",
"@babel/helper-simple-access": "^7.8.3",
"@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/template": "^7.8.3",
- "@babel/types": "^7.8.3",
+ "@babel/template": "^7.8.6",
+ "@babel/types": "^7.8.6",
"lodash": "^4.17.13"
},
"dependencies": {
@@ -823,26 +825,26 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -862,9 +864,9 @@
},
"dependencies": {
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -912,12 +914,12 @@
}
},
"@babel/generator": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz",
- "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz",
+ "integrity": "sha512-4bpOR5ZBz+wWcMeVtcf7FbjcFzCp+817z2/gHNncIRcM9MmKzUhtWCYAq27RAfUrAFwb+OCG1s9WEaVxfi6cjg==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3",
+ "@babel/types": "^7.8.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
@@ -964,43 +966,43 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/traverse": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz",
- "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz",
+ "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.4",
+ "@babel/generator": "^7.8.6",
"@babel/helper-function-name": "^7.8.3",
"@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.4",
- "@babel/types": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -1026,15 +1028,15 @@
}
},
"@babel/helper-replace-supers": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz",
- "integrity": "sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz",
+ "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==",
"dev": true,
"requires": {
"@babel/helper-member-expression-to-functions": "^7.8.3",
"@babel/helper-optimise-call-expression": "^7.8.3",
- "@babel/traverse": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/traverse": "^7.8.6",
+ "@babel/types": "^7.8.6"
},
"dependencies": {
"@babel/code-frame": {
@@ -1047,12 +1049,12 @@
}
},
"@babel/generator": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz",
- "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz",
+ "integrity": "sha512-4bpOR5ZBz+wWcMeVtcf7FbjcFzCp+817z2/gHNncIRcM9MmKzUhtWCYAq27RAfUrAFwb+OCG1s9WEaVxfi6cjg==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3",
+ "@babel/types": "^7.8.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
@@ -1099,43 +1101,43 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/traverse": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz",
- "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz",
+ "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.4",
+ "@babel/generator": "^7.8.6",
"@babel/helper-function-name": "^7.8.3",
"@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.4",
- "@babel/types": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -1191,26 +1193,26 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -1251,12 +1253,12 @@
}
},
"@babel/generator": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz",
- "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz",
+ "integrity": "sha512-4bpOR5ZBz+wWcMeVtcf7FbjcFzCp+817z2/gHNncIRcM9MmKzUhtWCYAq27RAfUrAFwb+OCG1s9WEaVxfi6cjg==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3",
+ "@babel/types": "^7.8.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
@@ -1303,43 +1305,43 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/traverse": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz",
- "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz",
+ "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.4",
+ "@babel/generator": "^7.8.6",
"@babel/helper-function-name": "^7.8.3",
"@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.4",
- "@babel/types": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -1385,12 +1387,12 @@
}
},
"@babel/generator": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz",
- "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz",
+ "integrity": "sha512-4bpOR5ZBz+wWcMeVtcf7FbjcFzCp+817z2/gHNncIRcM9MmKzUhtWCYAq27RAfUrAFwb+OCG1s9WEaVxfi6cjg==",
"dev": true,
"requires": {
- "@babel/types": "^7.8.3",
+ "@babel/types": "^7.8.6",
"jsesc": "^2.5.1",
"lodash": "^4.17.13",
"source-map": "^0.5.0"
@@ -1437,43 +1439,43 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/traverse": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz",
- "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz",
+ "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/generator": "^7.8.4",
+ "@babel/generator": "^7.8.6",
"@babel/helper-function-name": "^7.8.3",
"@babel/helper-split-export-declaration": "^7.8.3",
- "@babel/parser": "^7.8.4",
- "@babel/types": "^7.8.3",
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.13"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -1717,9 +1719,9 @@
}
},
"@babel/plugin-transform-classes": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.3.tgz",
- "integrity": "sha512-SjT0cwFJ+7Rbr1vQsvphAHwUHvSUPmMjMU/0P59G8U2HLFqSa082JO7zkbDNWs9kH/IUqpHI6xWNesGf8haF1w==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.6.tgz",
+ "integrity": "sha512-k9r8qRay/R6v5aWZkrEclEhKO6mc1CCQr2dLsVHBmOQiMpN6I2bpjX3vgnldUWeEI1GHVNByULVxZ4BdP4Hmdg==",
"dev": true,
"requires": {
"@babel/helper-annotate-as-pure": "^7.8.3",
@@ -1727,7 +1729,7 @@
"@babel/helper-function-name": "^7.8.3",
"@babel/helper-optimise-call-expression": "^7.8.3",
"@babel/helper-plugin-utils": "^7.8.3",
- "@babel/helper-replace-supers": "^7.8.3",
+ "@babel/helper-replace-supers": "^7.8.6",
"@babel/helper-split-export-declaration": "^7.8.3",
"globals": "^11.1.0"
},
@@ -1782,26 +1784,26 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -1869,9 +1871,9 @@
}
},
"@babel/plugin-transform-for-of": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.4.tgz",
- "integrity": "sha512-iAXNlOWvcYUYoV8YIxwS7TxGRJcxyl8eQCfT+A5j8sKUzRFvJdcyjp97jL2IghWSRDaL2PU2O2tX8Cu9dTBq5A==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.6.tgz",
+ "integrity": "sha512-M0pw4/1/KI5WAxPsdcUL/w2LJ7o89YHN3yLkzNjg7Yl15GlVGgzHyCU+FMeAxevHGsLVmUqbirlUIKTafPmzdw==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.8.3"
@@ -1928,26 +1930,26 @@
}
},
"@babel/parser": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz",
- "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz",
+ "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==",
"dev": true
},
"@babel/template": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz",
- "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+ "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.8.3",
- "@babel/parser": "^7.8.3",
- "@babel/types": "^7.8.3"
+ "@babel/parser": "^7.8.6",
+ "@babel/types": "^7.8.6"
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -2069,9 +2071,9 @@
}
},
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -2166,13 +2168,13 @@
}
},
"@babel/preset-env": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.4.tgz",
- "integrity": "sha512-HihCgpr45AnSOHRbS5cWNTINs0TwaR8BS8xIIH+QwiW8cKL0llV91njQMpeMReEPVs+1Ao0x3RLEBLtt1hOq4w==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.6.tgz",
+ "integrity": "sha512-M5u8llV9DIVXBFB/ArIpqJuvXpO+ymxcJ6e8ZAmzeK3sQeBNOD1y+rHvHCGG4TlEmsNpIrdecsHGHT8ZCoOSJg==",
"dev": true,
"requires": {
- "@babel/compat-data": "^7.8.4",
- "@babel/helper-compilation-targets": "^7.8.4",
+ "@babel/compat-data": "^7.8.6",
+ "@babel/helper-compilation-targets": "^7.8.6",
"@babel/helper-module-imports": "^7.8.3",
"@babel/helper-plugin-utils": "^7.8.3",
"@babel/plugin-proposal-async-generator-functions": "^7.8.3",
@@ -2195,13 +2197,13 @@
"@babel/plugin-transform-async-to-generator": "^7.8.3",
"@babel/plugin-transform-block-scoped-functions": "^7.8.3",
"@babel/plugin-transform-block-scoping": "^7.8.3",
- "@babel/plugin-transform-classes": "^7.8.3",
+ "@babel/plugin-transform-classes": "^7.8.6",
"@babel/plugin-transform-computed-properties": "^7.8.3",
"@babel/plugin-transform-destructuring": "^7.8.3",
"@babel/plugin-transform-dotall-regex": "^7.8.3",
"@babel/plugin-transform-duplicate-keys": "^7.8.3",
"@babel/plugin-transform-exponentiation-operator": "^7.8.3",
- "@babel/plugin-transform-for-of": "^7.8.4",
+ "@babel/plugin-transform-for-of": "^7.8.6",
"@babel/plugin-transform-function-name": "^7.8.3",
"@babel/plugin-transform-literals": "^7.8.3",
"@babel/plugin-transform-member-expression-literals": "^7.8.3",
@@ -2222,7 +2224,7 @@
"@babel/plugin-transform-template-literals": "^7.8.3",
"@babel/plugin-transform-typeof-symbol": "^7.8.4",
"@babel/plugin-transform-unicode-regex": "^7.8.3",
- "@babel/types": "^7.8.3",
+ "@babel/types": "^7.8.6",
"browserslist": "^4.8.5",
"core-js-compat": "^3.6.2",
"invariant": "^2.2.2",
@@ -2231,9 +2233,9 @@
},
"dependencies": {
"@babel/types": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz",
- "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==",
+ "version": "7.8.6",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz",
+ "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
@@ -2536,9 +2538,9 @@
}
},
"@types/body-parser": {
- "version": "1.17.1",
- "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz",
- "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==",
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
"requires": {
"@types/connect": "*",
"@types/node": "*"
@@ -2626,9 +2628,9 @@
"integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw=="
},
"@types/koa": {
- "version": "2.11.1",
- "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.11.1.tgz",
- "integrity": "sha512-/kqQs+8Qd9GL0cdl39HEhK91k7xq6+Zci76RUdqtTLj1Mg1aVG7zwJm3snkeyFUeAvY8noY27eMXgqg1wHZgwA==",
+ "version": "2.11.2",
+ "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.11.2.tgz",
+ "integrity": "sha512-2UPelagNNW6bnc1I5kIzluCaheXRA9S+NyOdXEFFj9Az7jc15ek5V03kb8OTbb3tdZ5i2BIJObe86PhHvpMolg==",
"requires": {
"@types/accepts": "*",
"@types/cookies": "*",
@@ -2879,12 +2881,12 @@
}
},
"apollo-cache-control": {
- "version": "0.8.11",
- "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.8.11.tgz",
- "integrity": "sha512-8yz4qbRBIFDWRHdT8uPh0HHh+VbQXxoFGJQRAG8hyMRvR+EuURXX1ltXYkn5J3YJ3MKEqgsvwGaq60dFZq63UQ==",
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.9.0.tgz",
+ "integrity": "sha512-iLT6IT4Ul5cMfBcJAvhpk3a7AD6fXqvFxNmJEPVapVJHbSKYIjra4PTis13sOyN5Y3WQS6a+NRFxAW8+hL3q3Q==",
"requires": {
"apollo-server-env": "^2.4.3",
- "graphql-extensions": "^0.10.10"
+ "graphql-extensions": "^0.11.0"
}
},
"apollo-cache-inmemory": {
@@ -2926,18 +2928,18 @@
}
},
"apollo-engine-reporting": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/apollo-engine-reporting/-/apollo-engine-reporting-1.6.0.tgz",
- "integrity": "sha512-prA17Tp/WYBJdCd4ey1CnGX8d4Xis1n9PsFmT7x8PV/oNpxG21/x3yNw5kPBZuKAoKz8yEggYtHhkYie1ZBjPQ==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/apollo-engine-reporting/-/apollo-engine-reporting-1.7.0.tgz",
+ "integrity": "sha512-jsjSnoHrRmk4XXK4aFU17YSJILmWsilKRwIeN74QJsSxjn5SCVF4EI/ebf/MNrTHpft8EhShx+wdkAcOD9ivqA==",
"requires": {
"apollo-engine-reporting-protobuf": "^0.4.4",
"apollo-graphql": "^0.4.0",
"apollo-server-caching": "^0.5.1",
"apollo-server-env": "^2.4.3",
- "apollo-server-errors": "^2.3.4",
- "apollo-server-types": "^0.2.10",
+ "apollo-server-errors": "^2.4.0",
+ "apollo-server-types": "^0.3.0",
"async-retry": "^1.2.1",
- "graphql-extensions": "^0.10.10"
+ "graphql-extensions": "^0.11.0"
}
},
"apollo-engine-reporting-protobuf": {
@@ -3020,25 +3022,25 @@
}
},
"apollo-server-core": {
- "version": "2.10.1",
- "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.10.1.tgz",
- "integrity": "sha512-BVITSJRMnj+CWFkjt7FMcaoqg/Ni9gfyVE9iu8bUc1IebBfFDcQj652Iolr7dTqyUziN2jbf0wfcybKYJLQHQQ==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.11.0.tgz",
+ "integrity": "sha512-jHLOqwTRlyWzqWNRlwr2M/xfrt+lw2pHtKYyxUGRjWFo8EM5TX1gDcTKtbtvx9p5m+ZBDAhcWp/rpq0vSz4tqg==",
"requires": {
"@apollographql/apollo-tools": "^0.4.3",
"@apollographql/graphql-playground-html": "1.6.24",
"@types/graphql-upload": "^8.0.0",
"@types/ws": "^6.0.0",
- "apollo-cache-control": "^0.8.11",
+ "apollo-cache-control": "^0.9.0",
"apollo-datasource": "^0.7.0",
- "apollo-engine-reporting": "^1.6.0",
+ "apollo-engine-reporting": "^1.7.0",
"apollo-server-caching": "^0.5.1",
"apollo-server-env": "^2.4.3",
- "apollo-server-errors": "^2.3.4",
- "apollo-server-plugin-base": "^0.6.10",
- "apollo-server-types": "^0.2.10",
- "apollo-tracing": "^0.8.11",
+ "apollo-server-errors": "^2.4.0",
+ "apollo-server-plugin-base": "^0.7.0",
+ "apollo-server-types": "^0.3.0",
+ "apollo-tracing": "^0.9.0",
"fast-json-stable-stringify": "^2.0.0",
- "graphql-extensions": "^0.10.10",
+ "graphql-extensions": "^0.11.0",
"graphql-tag": "^2.9.2",
"graphql-tools": "^4.0.0",
"graphql-upload": "^8.0.2",
@@ -3083,23 +3085,23 @@
}
},
"apollo-server-errors": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-2.3.4.tgz",
- "integrity": "sha512-Y0PKQvkrb2Kd18d1NPlHdSqmlr8TgqJ7JQcNIfhNDgdb45CnqZlxL1abuIRhr8tiw8OhVOcFxz2KyglBi8TKdA=="
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-2.4.0.tgz",
+ "integrity": "sha512-ZouZfr2sGavvI18rgdRcyY2ausRAlVtWNOax9zca8ZG2io86dM59jXBmUVSNlVZSmBsIh45YxYC0eRvr2vmRdg=="
},
"apollo-server-express": {
- "version": "2.10.1",
- "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.10.1.tgz",
- "integrity": "sha512-NkuWGBOCTiju/aDjfvDImm+4yzfrM0dwiRxu9fKwwh2h1oYBUKJNqjQ1mzJRi0ks6Sn1egwl/fQkTBTkWwGx7Q==",
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-2.11.0.tgz",
+ "integrity": "sha512-9bbiD+zFAx+xyurc9lxYmNa9y79k/gsA1vEyPFVcv7jxzCFC5wc0tcbV7NPX2qi1Nn7K76fxo2fPNYbPFX/y0g==",
"requires": {
"@apollographql/graphql-playground-html": "1.6.24",
"@types/accepts": "^1.3.5",
- "@types/body-parser": "1.17.1",
+ "@types/body-parser": "1.19.0",
"@types/cors": "^2.8.4",
"@types/express": "4.17.2",
"accepts": "^1.3.5",
- "apollo-server-core": "^2.10.1",
- "apollo-server-types": "^0.2.10",
+ "apollo-server-core": "^2.11.0",
+ "apollo-server-types": "^0.3.0",
"body-parser": "^1.18.3",
"cors": "^2.8.4",
"express": "^4.17.1",
@@ -3111,17 +3113,17 @@
}
},
"apollo-server-plugin-base": {
- "version": "0.6.10",
- "resolved": "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-0.6.10.tgz",
- "integrity": "sha512-/xT7UT/tbCDIoTQ4lcEQsJ0ACh7h7QG0BDmeSlDXjwDuENRI50bQ2QoluCMPitZXGe+FCQfLhvzFgzbsZGT0IA==",
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-0.7.0.tgz",
+ "integrity": "sha512-//xgYrBYLQSr92W0z3mYsFGoVz3wxKNsv3KcOUBhbOCGTbjZgP7vHOE1vhHhRcpZKKXmjXTVONdrnNJ+XVGi6A==",
"requires": {
- "apollo-server-types": "^0.2.10"
+ "apollo-server-types": "^0.3.0"
}
},
"apollo-server-types": {
- "version": "0.2.10",
- "resolved": "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-0.2.10.tgz",
- "integrity": "sha512-ke9ViPEWfW+2XLe66CaKGVZdS7duSLbamSKSprmmeMBd8s6tmjf0FumUVxV7X4quxPZi0OPo8x0LoLU7GWsmaA==",
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/apollo-server-types/-/apollo-server-types-0.3.0.tgz",
+ "integrity": "sha512-FMo7kbTkhph9dfIQ3xDbRLObqmdQH9mwSjxhGsX+JxGMRPPXgd3+GZvCeVKOi/udxh//w1otSeAqItjvbj0tfQ==",
"requires": {
"apollo-engine-reporting-protobuf": "^0.4.4",
"apollo-server-caching": "^0.5.1",
@@ -3129,12 +3131,12 @@
}
},
"apollo-tracing": {
- "version": "0.8.11",
- "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.8.11.tgz",
- "integrity": "sha512-Z0wDZ5QOBmpGoajB74ZKGTM7GzG6rqZRzAph4kxud6axcyNqUDKiKZ3Eere+NSLwvvt8M3qnPW4UJSUy/wwOXg==",
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.9.0.tgz",
+ "integrity": "sha512-oqspTrf4BLGbKkIk1vF+I31C2v7PPJmF36TFpT/+zJxNvJw54ji4ZMhtytgVqbVldQEintJmdHQIidYBGKmu+g==",
"requires": {
"apollo-server-env": "^2.4.3",
- "graphql-extensions": "^0.10.10"
+ "graphql-extensions": "^0.11.0"
}
},
"apollo-upload-client": {
@@ -3351,15 +3353,15 @@
"integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A=="
},
"babel-eslint": {
- "version": "10.0.3",
- "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz",
- "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
+ "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
- "@babel/parser": "^7.0.0",
- "@babel/traverse": "^7.0.0",
- "@babel/types": "^7.0.0",
+ "@babel/parser": "^7.7.0",
+ "@babel/traverse": "^7.7.0",
+ "@babel/types": "^7.7.0",
"eslint-visitor-keys": "^1.0.0",
"resolve": "^1.12.0"
}
@@ -3457,12 +3459,12 @@
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
},
"bcrypt": {
- "version": "3.0.8",
- "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-3.0.8.tgz",
- "integrity": "sha512-jKV6RvLhI36TQnPDvUFqBEnGX9c8dRRygKxCZu7E+MgLfKZbmmXL8a7/SFFOyHoPNX9nV81cKRC5tbQfvEQtpw==",
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-4.0.1.tgz",
+ "integrity": "sha512-hSIZHkUxIDS5zA2o00Kf2O5RfVbQ888n54xQoF/eIaquU4uaLxK8vhhBdktd0B3n2MjkcAWzv4mnhogykBKOUQ==",
"optional": true,
"requires": {
- "nan": "2.14.0",
+ "node-addon-api": "^2.0.0",
"node-pre-gyp": "0.14.0"
}
},
@@ -3630,14 +3632,14 @@
}
},
"browserslist": {
- "version": "4.8.6",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz",
- "integrity": "sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg==",
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.0.tgz",
+ "integrity": "sha512-seffIXhwgB84+OCeT/aMjpZnsAsYDiMSC+CEs3UkF8iU64BZGYcu+TZYs/IBpo4nRi0vJywUJWYdbTsOhFTweg==",
"dev": true,
"requires": {
- "caniuse-lite": "^1.0.30001023",
- "electron-to-chromium": "^1.3.341",
- "node-releases": "^1.1.47"
+ "caniuse-lite": "^1.0.30001030",
+ "electron-to-chromium": "^1.3.361",
+ "node-releases": "^1.1.50"
}
},
"bson": {
@@ -3776,9 +3778,9 @@
"dev": true
},
"caniuse-lite": {
- "version": "1.0.30001023",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001023.tgz",
- "integrity": "sha512-C5TDMiYG11EOhVOA62W1p3UsJ2z4DsHtMBQtjzp3ZsUglcQn62WOUgW0y795c7A5uZ+GCEIvzkMatLIlAsbNTA==",
+ "version": "1.0.30001030",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001030.tgz",
+ "integrity": "sha512-QGK0W4Ft/Ac+zTjEiRJfwDNATvS3fodDczBXrH42784kcfqcDKpEPfN08N0HQjrAp8He/Jw8QiSS9QRn7XAbUw==",
"dev": true
},
"caseless": {
@@ -3870,9 +3872,9 @@
}
},
"chownr": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
- "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
"optional": true
},
"ci-info": {
@@ -4298,9 +4300,9 @@
}
},
"cross-env": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.0.tgz",
- "integrity": "sha512-rV6M9ldNgmwP7bx5u6rZsTbYidzwvrwIYZnT08hSGLcQCcggofgFW+sNe7IhA1SRauPS0QuLbbX+wdNtpqE5CQ==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.1.tgz",
+ "integrity": "sha512-1+DmLosu38kC4s1H4HzNkcolwdANifu9+5bE6uKQCV4L6jvVdV9qdRAk8vV3GoWRe0x4z+K2fFhgoDMqwNsPqQ==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.1"
@@ -4858,9 +4860,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"electron-to-chromium": {
- "version": "1.3.344",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.344.tgz",
- "integrity": "sha512-tvbx2Wl8WBR+ym3u492D0L6/jH+8NoQXqe46+QhbWH3voVPauGuZYeb1QAXYoOAWuiP2dbSvlBx0kQ1F3hu/Mw==",
+ "version": "1.3.363",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.363.tgz",
+ "integrity": "sha512-4w19wPBkeunBjOA53lNFT36IdOD3Tk1OoIDtTX+VToJUUDX42QfuhtsNKXv25wmSnoBOExM3kTbj7/WDNBwHuQ==",
"dev": true
},
"elegant-spinner": {
@@ -5885,9 +5887,9 @@
"dev": true
},
"flow-bin": {
- "version": "0.118.0",
- "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.118.0.tgz",
- "integrity": "sha512-jlbUu0XkbpXeXhan5xyTqVK1jmEKNxE8hpzznI3TThHTr76GiFwK0iRzhDo4KNy+S9h/KxHaqVhTP86vA6wHCg==",
+ "version": "0.119.1",
+ "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.119.1.tgz",
+ "integrity": "sha512-mX6qjJVi7aLqR9sDf8QIHt8yYEWQbkMLw7qFoC7sM/AbJwvqFm3pATPN96thsaL9o1rrshvxJpSgoj1PJSC3KA==",
"dev": true
},
"follow-redirects": {
@@ -6830,13 +6832,13 @@
}
},
"graphql-extensions": {
- "version": "0.10.10",
- "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.10.10.tgz",
- "integrity": "sha512-pNb1DmUk6vsGtCjCRecpKoXadKNMyKxyLyE9IX65N9aKSmLL+AF7dJOOc4MWhdaAXlzxaDDhe54GpaOfoH7AOw==",
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.11.0.tgz",
+ "integrity": "sha512-zd4qfUiJoYBx2MwJusM36SEJ+YmJ1ki8YF8nlm9mgaPDUzsnmFq4lxULxUfhLAXFwZw7MbEN2vV4V6WiNgSJLg==",
"requires": {
"@apollographql/apollo-tools": "^0.4.3",
"apollo-server-env": "^2.4.3",
- "apollo-server-types": "^0.2.10"
+ "apollo-server-types": "^0.3.0"
}
},
"graphql-list-fields": {
@@ -8168,9 +8170,9 @@
}
},
"lint-staged": {
- "version": "10.0.7",
- "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.0.7.tgz",
- "integrity": "sha512-Byj0F4l7GYUpYYHEqyFH69NiI6ICTg0CeCKbhRorL+ickbzILKUlZLiyCkljZV02wnoh7yH7PmFyYm9PRNwk9g==",
+ "version": "10.0.8",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.0.8.tgz",
+ "integrity": "sha512-Oa9eS4DJqvQMVdywXfEor6F4vP+21fPHF8LUXgBbVWUSWBddjqsvO6Bv1LwMChmgQZZqwUvgJSHlu8HFHAPZmA==",
"dev": true,
"requires": {
"chalk": "^3.0.0",
@@ -9032,9 +9034,9 @@
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
},
"mongodb": {
- "version": "3.5.3",
- "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.3.tgz",
- "integrity": "sha512-II7P7A3XUdPiXRgcN96qIoRa1oesM6qLNZkzfPluNZjVkgQk3jnQwOT6/uDk4USRDTTLjNFw2vwfmbRGTA7msg==",
+ "version": "3.5.4",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.4.tgz",
+ "integrity": "sha512-xGH41Ig4dkSH5ROGezkgDbsgt/v5zbNUwE3TcFsSbDc6Qn3Qil17dhLsESSDDPTiyFDCPJRpfd4887dtsPgKtA==",
"requires": {
"bl": "^2.2.0",
"bson": "^1.1.1",
@@ -9448,9 +9450,9 @@
"optional": true
},
"needle": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.2.tgz",
- "integrity": "sha512-DUzITvPVDUy6vczKKYTnWc/pBZ0EnjMJnQ3y+Jo5zfKFimJs7S3HFCxCRZYB9FUZcrzUQr3WsmvZgddMEIZv6w==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.3.tgz",
+ "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==",
"optional": true,
"requires": {
"debug": "^3.2.6",
@@ -9480,6 +9482,12 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
+ "node-addon-api": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
+ "integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA==",
+ "optional": true
+ },
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
@@ -9535,9 +9543,9 @@
}
},
"node-releases": {
- "version": "1.1.47",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.47.tgz",
- "integrity": "sha512-k4xjVPx5FpwBUj0Gw7uvFOTF4Ep8Hok1I6qjwL3pLfwe7Y0REQSAqOwwv9TWBCUtMHxcXfY4PgRLRozcChvTcA==",
+ "version": "1.1.50",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz",
+ "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==",
"dev": true,
"requires": {
"semver": "^6.3.0"
@@ -10172,9 +10180,9 @@
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"pg": {
- "version": "7.18.1",
- "resolved": "https://registry.npmjs.org/pg/-/pg-7.18.1.tgz",
- "integrity": "sha512-1KtKBKg/zWrjEEv//klBbVOPGucuc7HHeJf6OEMueVcUeyF3yueHf+DvhVwBjIAe9/97RAydO/lWjkcMwssuEw==",
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-7.18.2.tgz",
+ "integrity": "sha512-Mvt0dGYMwvEADNKy5PMQGlzPudKcKKzJds/VbOeZJpb6f/pI3mmoXX0JksPgI3l3JPP/2Apq7F36O63J7mgveA==",
"requires": {
"buffer-writer": "2.0.0",
"packet-reader": "1.0.0",
@@ -10219,12 +10227,12 @@
"integrity": "sha512-qdwzY92bHf3nwzIUcj+zJ0Qo5lpG/YxchahxIN8+ZVmXqkahKXsnl2aiJPHLYN9o5mB/leG+Xh6XKxtP7e0sjg=="
},
"pg-promise": {
- "version": "10.4.3",
- "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.4.3.tgz",
- "integrity": "sha512-hdZTQ/NlqSlhKM9bMgDhKFiJX+hTuxUygEu1NBjGUtdTdenvnrxjaheeob6OEIxiYSmZFMG8uIsoVCIIPMgCsg==",
+ "version": "10.4.4",
+ "resolved": "https://registry.npmjs.org/pg-promise/-/pg-promise-10.4.4.tgz",
+ "integrity": "sha512-N2NsOgKxrnNPwP0Q609ZmxmAZEo2TQ26SzSvlbZWQb8vteqUhOPpU/pHi9DGatJrPcXNoyr4xjRw42CNfEBg/w==",
"requires": {
"assert-options": "0.6.1",
- "pg": "7.18.1",
+ "pg": "7.18.2",
"pg-minify": "1.5.2",
"spex": "3.0.1"
}
@@ -10640,9 +10648,9 @@
"dev": true
},
"regjsparser": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.2.tgz",
- "integrity": "sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q==",
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.3.tgz",
+ "integrity": "sha512-8uZvYbnfAtEm9Ab8NTb3hdLwL4g/LQzEYP7Xs27T96abJCCE2d6r3cPZPQEsLKy0vRSGVNG+/zVGtLr86HQduA==",
"dev": true,
"requires": {
"jsesc": "~0.5.0"
diff --git a/package.json b/package.json
index 52004a063f..d5e58a5103 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "parse-server",
- "version": "3.10.0",
+ "version": "4.1.0",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
@@ -24,7 +24,7 @@
"@parse/push-adapter": "3.2.0",
"@parse/s3-files-adapter": "1.4.0",
"@parse/simple-mailgun-adapter": "1.1.0",
- "apollo-server-express": "2.10.1",
+ "apollo-server-express": "2.11.0",
"bcryptjs": "2.4.3",
"body-parser": "1.19.0",
"commander": "4.1.1",
@@ -43,10 +43,10 @@
"lodash": "4.17.15",
"lru-cache": "5.1.1",
"mime": "2.4.4",
- "mongodb": "3.5.3",
+ "mongodb": "3.5.4",
"node-rsa": "1.0.7",
"parse": "2.11.0",
- "pg-promise": "10.4.3",
+ "pg-promise": "10.4.4",
"pluralize": "^8.0.0",
"redis": "3.0.0",
"semver": "7.1.3",
@@ -59,10 +59,10 @@
},
"devDependencies": {
"@babel/cli": "7.8.4",
- "@babel/core": "7.8.4",
+ "@babel/core": "7.8.6",
"@babel/plugin-proposal-object-rest-spread": "7.8.3",
"@babel/plugin-transform-flow-strip-types": "7.8.3",
- "@babel/preset-env": "7.8.4",
+ "@babel/preset-env": "7.8.6",
"@parse/minami": "1.0.0",
"apollo-cache-inmemory": "1.6.5",
"apollo-client": "2.6.6",
@@ -71,13 +71,13 @@
"apollo-link-ws": "1.0.19",
"apollo-upload-client": "12.1.0",
"apollo-utilities": "1.3.3",
- "babel-eslint": "10.0.3",
+ "babel-eslint": "10.1.0",
"bcrypt-nodejs": "0.0.3",
- "cross-env": "7.0.0",
+ "cross-env": "7.0.1",
"deep-diff": "1.0.2",
"eslint": "6.8.0",
"eslint-plugin-flowtype": "4.5.0",
- "flow-bin": "0.118.0",
+ "flow-bin": "0.119.1",
"form-data": "3.0.0",
"gaze": "1.1.3",
"graphql-tag": "^2.10.1",
@@ -85,7 +85,7 @@
"jasmine": "3.5.0",
"jsdoc": "3.6.3",
"jsdoc-babel": "0.5.0",
- "lint-staged": "10.0.7",
+ "lint-staged": "10.0.8",
"mongodb-runner": "4.8.0",
"node-fetch": "2.6.0",
"nyc": "15.0.0",
@@ -114,7 +114,7 @@
"parse-server": "./bin/parse-server"
},
"optionalDependencies": {
- "bcrypt": "3.0.8"
+ "bcrypt": "4.0.1"
},
"collective": {
"type": "opencollective",
diff --git a/spec/DatabaseController.spec.js b/spec/DatabaseController.spec.js
index 70b554f83a..af373e64f2 100644
--- a/spec/DatabaseController.spec.js
+++ b/spec/DatabaseController.spec.js
@@ -3,118 +3,56 @@ const validateQuery = DatabaseController._validateQuery;
describe('DatabaseController', function() {
describe('validateQuery', function() {
- describe('with skipMongoDBServer13732Workaround disabled (the default)', function() {
- it('should restructure simple cases of SERVER-13732', done => {
- const query = {
- $or: [{ a: 1 }, { a: 2 }],
- _rperm: { $in: ['a', 'b'] },
- foo: 3,
- };
- validateQuery(query, false);
- expect(query).toEqual({
- $or: [
- { a: 1, _rperm: { $in: ['a', 'b'] }, foo: 3 },
- { a: 2, _rperm: { $in: ['a', 'b'] }, foo: 3 },
- ],
- });
- done();
- });
-
- it('should not restructure SERVER-13732 queries with $nears', done => {
- let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } };
- validateQuery(query, false);
- expect(query).toEqual({
- $or: [{ a: 1 }, { b: 1 }],
- c: { $nearSphere: {} },
- });
- query = { $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } };
- validateQuery(query, false);
- expect(query).toEqual({ $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } });
- done();
- });
-
- it('should push refactored keys down a tree for SERVER-13732', done => {
- const query = {
- a: 1,
- $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }],
- };
- validateQuery(query, false);
- expect(query).toEqual({
- $or: [
- { $or: [{ b: 1, a: 1 }, { b: 2, a: 1 }] },
- { $or: [{ c: 1, a: 1 }, { c: 2, a: 1 }] },
- ],
- });
- done();
- });
-
- it('should reject invalid queries', done => {
- expect(() => validateQuery({ $or: { a: 1 } }, false)).toThrow();
- done();
- });
-
- it('should accept valid queries', done => {
- expect(() =>
- validateQuery({ $or: [{ a: 1 }, { b: 2 }] }, false)
- ).not.toThrow();
- done();
- });
+ it('should not restructure simple cases of SERVER-13732', done => {
+ const query = {
+ $or: [{ a: 1 }, { a: 2 }],
+ _rperm: { $in: ['a', 'b'] },
+ foo: 3,
+ };
+ validateQuery(query);
+ expect(query).toEqual({
+ $or: [{ a: 1 }, { a: 2 }],
+ _rperm: { $in: ['a', 'b'] },
+ foo: 3,
+ });
+ done();
});
- describe('with skipMongoDBServer13732Workaround enabled', function() {
- it('should not restructure simple cases of SERVER-13732', done => {
- const query = {
- $or: [{ a: 1 }, { a: 2 }],
- _rperm: { $in: ['a', 'b'] },
- foo: 3,
- };
- validateQuery(query, true);
- expect(query).toEqual({
- $or: [{ a: 1 }, { a: 2 }],
- _rperm: { $in: ['a', 'b'] },
- foo: 3,
- });
- done();
- });
+ it('should not restructure SERVER-13732 queries with $nears', done => {
+ let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } };
+ validateQuery(query);
+ expect(query).toEqual({
+ $or: [{ a: 1 }, { b: 1 }],
+ c: { $nearSphere: {} },
+ });
+ query = { $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } };
+ validateQuery(query);
+ expect(query).toEqual({ $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } });
+ done();
+ });
- it('should not restructure SERVER-13732 queries with $nears', done => {
- let query = { $or: [{ a: 1 }, { b: 1 }], c: { $nearSphere: {} } };
- validateQuery(query, true);
- expect(query).toEqual({
- $or: [{ a: 1 }, { b: 1 }],
- c: { $nearSphere: {} },
- });
- query = { $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } };
- validateQuery(query, true);
- expect(query).toEqual({ $or: [{ a: 1 }, { b: 1 }], c: { $near: {} } });
- done();
+ it('should not push refactored keys down a tree for SERVER-13732', done => {
+ const query = {
+ a: 1,
+ $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }],
+ };
+ validateQuery(query);
+ expect(query).toEqual({
+ a: 1,
+ $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }],
});
- it('should not push refactored keys down a tree for SERVER-13732', done => {
- const query = {
- a: 1,
- $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }],
- };
- validateQuery(query, true);
- expect(query).toEqual({
- a: 1,
- $or: [{ $or: [{ b: 1 }, { b: 2 }] }, { $or: [{ c: 1 }, { c: 2 }] }],
- });
-
- done();
- });
+ done();
+ });
- it('should reject invalid queries', done => {
- expect(() => validateQuery({ $or: { a: 1 } }, true)).toThrow();
- done();
- });
+ it('should reject invalid queries', done => {
+ expect(() => validateQuery({ $or: { a: 1 } })).toThrow();
+ done();
+ });
- it('should accept valid queries', done => {
- expect(() =>
- validateQuery({ $or: [{ a: 1 }, { b: 2 }] }, true)
- ).not.toThrow();
- done();
- });
+ it('should accept valid queries', done => {
+ expect(() => validateQuery({ $or: [{ a: 1 }, { b: 2 }] })).not.toThrow();
+ done();
});
});
});
diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js
index ca08f227db..e9cbc64dc2 100644
--- a/spec/ParseGraphQLServer.spec.js
+++ b/spec/ParseGraphQLServer.spec.js
@@ -5,6 +5,8 @@ const fetch = require('node-fetch');
const FormData = require('form-data');
const ws = require('ws');
require('./helper');
+const { updateCLP } = require('./dev');
+
const pluralize = require('pluralize');
const { getMainDefinition } = require('apollo-utilities');
const { ApolloLink, split } = require('apollo-link');
@@ -15,6 +17,14 @@ const { SubscriptionClient } = require('subscriptions-transport-ws');
const { WebSocketLink } = require('apollo-link-ws');
const ApolloClient = require('apollo-client').default;
const gql = require('graphql-tag');
+const {
+ GraphQLObjectType,
+ GraphQLString,
+ GraphQLNonNull,
+ GraphQLEnumType,
+ GraphQLInputObjectType,
+ GraphQLSchema,
+} = require('graphql');
const { ParseServer } = require('../');
const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer');
const ReadPreference = require('mongodb').ReadPreference;
@@ -4632,6 +4642,84 @@ describe('ParseGraphQLServer', () => {
).toBeDefined();
});
+ it('should respect protectedFields', async done => {
+ await prepareData();
+ await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
+
+ const className = 'GraphQLClass';
+
+ await updateCLP(
+ {
+ get: { '*': true },
+ find: { '*': true },
+
+ protectedFields: {
+ '*': ['someField', 'someOtherField'],
+ authenticated: ['someField'],
+ 'userField:pointerToUser': [],
+ [user2.id]: [],
+ },
+ },
+ className
+ );
+
+ const getObject = async (className, id, user) => {
+ const headers = user
+ ? { ['X-Parse-Session-Token']: user.getSessionToken() }
+ : undefined;
+
+ const specificQueryResult = await apolloClient.query({
+ query: gql`
+ query GetSomeObject($id: ID!) {
+ get: graphQLClass(id: $id) {
+ pointerToUser {
+ username
+ id
+ }
+ someField
+ someOtherField
+ }
+ }
+ `,
+ variables: {
+ id: id,
+ },
+ context: {
+ headers: headers,
+ },
+ });
+
+ return specificQueryResult.data.get;
+ };
+
+ const id = object3.id;
+
+ /* not authenticated */
+ const objectPublic = await getObject(className, id, undefined);
+
+ expect(objectPublic.someField).toBeNull();
+ expect(objectPublic.someOtherField).toBeNull();
+
+ /* authenticated */
+ const objectAuth = await getObject(className, id, user1);
+
+ expect(objectAuth.someField).toBeNull();
+ expect(objectAuth.someOtherField).toBe('B');
+
+ /* pointer field */
+ const objectPointed = await getObject(className, id, user5);
+
+ expect(objectPointed.someField).toBe('someValue3');
+ expect(objectPointed.someOtherField).toBe('B');
+
+ /* for user id */
+ const objectForUser = await getObject(className, id, user2);
+
+ expect(objectForUser.someField).toBe('someValue3');
+ expect(objectForUser.someOtherField).toBe('B');
+
+ done();
+ });
describe_only_db('mongo')('read preferences', () => {
it('should read from primary by default', async () => {
try {
@@ -10514,130 +10602,296 @@ describe('ParseGraphQLServer', () => {
});
describe('Custom API', () => {
- let httpServer;
- const headers = {
- 'X-Parse-Application-Id': 'test',
- 'X-Parse-Javascript-Key': 'test',
- };
- let apolloClient;
-
- beforeAll(async () => {
- const expressApp = express();
- httpServer = http.createServer(expressApp);
- parseGraphQLServer = new ParseGraphQLServer(parseServer, {
- graphQLPath: '/graphql',
- graphQLCustomTypeDefs: gql`
- extend type Query {
- hello: String @resolve
- hello2: String @resolve(to: "hello")
- userEcho(user: CreateUserFieldsInput!): User! @resolve
- hello3: String! @mock(with: "Hello world!")
- hello4: User! @mock(with: { username: "somefolk" })
- }
- `,
- });
- parseGraphQLServer.applyGraphQL(expressApp);
- await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve));
- const httpLink = createUploadLink({
- uri: 'http://localhost:13377/graphql',
- fetch,
- headers,
- });
- apolloClient = new ApolloClient({
- link: httpLink,
- cache: new InMemoryCache(),
- defaultOptions: {
- query: {
- fetchPolicy: 'no-cache',
+ describe('GraphQL Schema Based', () => {
+ let httpServer;
+ const headers = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-Javascript-Key': 'test',
+ };
+ let apolloClient;
+ beforeAll(async () => {
+ const expressApp = express();
+ httpServer = http.createServer(expressApp);
+ parseGraphQLServer = new ParseGraphQLServer(parseServer, {
+ graphQLPath: '/graphql',
+ graphQLCustomTypeDefs: gql`
+ extend type Query {
+ hello: String @resolve
+ hello2: String @resolve(to: "hello")
+ userEcho(user: CreateUserFieldsInput!): User! @resolve
+ hello3: String! @mock(with: "Hello world!")
+ hello4: User! @mock(with: { username: "somefolk" })
+ }
+ `,
+ });
+ parseGraphQLServer.applyGraphQL(expressApp);
+ await new Promise(resolve =>
+ httpServer.listen({ port: 13377 }, resolve)
+ );
+ const httpLink = createUploadLink({
+ uri: 'http://localhost:13377/graphql',
+ fetch,
+ headers,
+ });
+ apolloClient = new ApolloClient({
+ link: httpLink,
+ cache: new InMemoryCache(),
+ defaultOptions: {
+ query: {
+ fetchPolicy: 'no-cache',
+ },
},
- },
+ });
});
- });
-
- afterAll(async () => {
- await httpServer.close();
- });
- it('can resolve a custom query using default function name', async () => {
- Parse.Cloud.define('hello', async () => {
- return 'Hello world!';
+ afterAll(async () => {
+ await httpServer.close();
});
- const result = await apolloClient.query({
- query: gql`
- query Hello {
- hello
- }
- `,
- });
+ it('can resolve a custom query using default function name', async () => {
+ Parse.Cloud.define('hello', async () => {
+ return 'Hello world!';
+ });
- expect(result.data.hello).toEqual('Hello world!');
- });
+ const result = await apolloClient.query({
+ query: gql`
+ query Hello {
+ hello
+ }
+ `,
+ });
- it('can resolve a custom query using function name set by "to" argument', async () => {
- Parse.Cloud.define('hello', async () => {
- return 'Hello world!';
+ expect(result.data.hello).toEqual('Hello world!');
});
- const result = await apolloClient.query({
- query: gql`
- query Hello {
- hello2
- }
- `,
- });
+ it('can resolve a custom query using function name set by "to" argument', async () => {
+ Parse.Cloud.define('hello', async () => {
+ return 'Hello world!';
+ });
+
+ const result = await apolloClient.query({
+ query: gql`
+ query Hello {
+ hello2
+ }
+ `,
+ });
- expect(result.data.hello2).toEqual('Hello world!');
+ expect(result.data.hello2).toEqual('Hello world!');
+ });
});
- it('should resolve auto types', async () => {
- Parse.Cloud.define('userEcho', async req => {
- return req.params.user;
+ describe('SDL Based', () => {
+ let httpServer;
+ const headers = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-Javascript-Key': 'test',
+ };
+ let apolloClient;
+
+ beforeAll(async () => {
+ const expressApp = express();
+ httpServer = http.createServer(expressApp);
+ const TypeEnum = new GraphQLEnumType({
+ name: 'TypeEnum',
+ values: {
+ human: { value: 'human' },
+ robot: { value: 'robot' },
+ },
+ });
+ parseGraphQLServer = new ParseGraphQLServer(parseServer, {
+ graphQLPath: '/graphql',
+ graphQLCustomTypeDefs: new GraphQLSchema({
+ query: new GraphQLObjectType({
+ name: 'Query',
+ fields: {
+ customQuery: {
+ type: new GraphQLNonNull(GraphQLString),
+ args: {
+ message: { type: new GraphQLNonNull(GraphQLString) },
+ },
+ resolve: (p, { message }) => message,
+ },
+ },
+ }),
+ types: [
+ new GraphQLInputObjectType({
+ name: 'CreateSomeClassFieldsInput',
+ fields: {
+ type: { type: TypeEnum },
+ },
+ }),
+ new GraphQLInputObjectType({
+ name: 'UpdateSomeClassFieldsInput',
+ fields: {
+ type: { type: TypeEnum },
+ },
+ }),
+ new GraphQLObjectType({
+ name: 'SomeClass',
+ fields: {
+ nameUpperCase: {
+ type: new GraphQLNonNull(GraphQLString),
+ resolve: p => p.name.toUpperCase(),
+ },
+ type: { type: TypeEnum },
+ language: {
+ type: new GraphQLEnumType({
+ name: 'LanguageEnum',
+ values: {
+ fr: { value: 'fr' },
+ en: { value: 'en' },
+ },
+ }),
+ resolve: () => 'fr',
+ },
+ },
+ }),
+ ],
+ }),
+ });
+
+ parseGraphQLServer.applyGraphQL(expressApp);
+ await new Promise(resolve =>
+ httpServer.listen({ port: 13377 }, resolve)
+ );
+ const httpLink = createUploadLink({
+ uri: 'http://localhost:13377/graphql',
+ fetch,
+ headers,
+ });
+ apolloClient = new ApolloClient({
+ link: httpLink,
+ cache: new InMemoryCache(),
+ defaultOptions: {
+ query: {
+ fetchPolicy: 'no-cache',
+ },
+ },
+ });
+ });
+
+ afterAll(async () => {
+ await httpServer.close();
});
- const result = await apolloClient.query({
- query: gql`
- query UserEcho($user: CreateUserFieldsInput!) {
- userEcho(user: $user) {
- username
+ it('can resolve a custom query', async () => {
+ const result = await apolloClient.query({
+ variables: { message: 'hello' },
+ query: gql`
+ query CustomQuery($message: String!) {
+ customQuery(message: $message)
}
- }
- `,
- variables: {
- user: {
- username: 'somefolk',
- password: 'somepassword',
- },
- },
+ `,
+ });
+ expect(result.data.customQuery).toEqual('hello');
});
- expect(result.data.userEcho.username).toEqual('somefolk');
+ it('can resolve a custom extend type', async () => {
+ const obj = new Parse.Object('SomeClass');
+ await obj.save({ name: 'aname', type: 'robot' });
+ await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
+ const result = await apolloClient.query({
+ variables: { id: obj.id },
+ query: gql`
+ query someClass($id: ID!) {
+ someClass(id: $id) {
+ nameUpperCase
+ language
+ type
+ }
+ }
+ `,
+ });
+ expect(result.data.someClass.nameUpperCase).toEqual('ANAME');
+ expect(result.data.someClass.language).toEqual('fr');
+ expect(result.data.someClass.type).toEqual('robot');
+
+ const result2 = await apolloClient.query({
+ variables: { id: obj.id },
+ query: gql`
+ query someClass($id: ID!) {
+ someClass(id: $id) {
+ name
+ language
+ }
+ }
+ `,
+ });
+ expect(result2.data.someClass.name).toEqual('aname');
+ expect(result.data.someClass.language).toEqual('fr');
+ const result3 = await apolloClient.mutate({
+ variables: { id: obj.id, name: 'anewname' },
+ mutation: gql`
+ mutation someClass($id: ID!, $name: String!) {
+ updateSomeClass(
+ input: { id: $id, fields: { name: $name, type: human } }
+ ) {
+ someClass {
+ nameUpperCase
+ type
+ }
+ }
+ }
+ `,
+ });
+ expect(result3.data.updateSomeClass.someClass.nameUpperCase).toEqual(
+ 'ANEWNAME'
+ );
+ expect(result3.data.updateSomeClass.someClass.type).toEqual('human');
+ });
});
+ describe('Async Function Based Merge', () => {
+ let httpServer;
+ const headers = {
+ 'X-Parse-Application-Id': 'test',
+ 'X-Parse-Javascript-Key': 'test',
+ };
+ let apolloClient;
- it('can mock a custom query with string', async () => {
- const result = await apolloClient.query({
- query: gql`
- query Hello {
- hello3
- }
- `,
+ beforeAll(async () => {
+ const expressApp = express();
+ httpServer = http.createServer(expressApp);
+ parseGraphQLServer = new ParseGraphQLServer(parseServer, {
+ graphQLPath: '/graphql',
+ graphQLCustomTypeDefs: ({ autoSchema, mergeSchemas }) =>
+ mergeSchemas({ schemas: [autoSchema] }),
+ });
+
+ parseGraphQLServer.applyGraphQL(expressApp);
+ await new Promise(resolve =>
+ httpServer.listen({ port: 13377 }, resolve)
+ );
+ const httpLink = createUploadLink({
+ uri: 'http://localhost:13377/graphql',
+ fetch,
+ headers,
+ });
+ apolloClient = new ApolloClient({
+ link: httpLink,
+ cache: new InMemoryCache(),
+ defaultOptions: {
+ query: {
+ fetchPolicy: 'no-cache',
+ },
+ },
+ });
});
- expect(result.data.hello3).toEqual('Hello world!');
- });
+ afterAll(async () => {
+ await httpServer.close();
+ });
- it('can mock a custom query with auto type', async () => {
- const result = await apolloClient.query({
- query: gql`
- query Hello {
- hello4 {
- username
+ it('can resolve a query', async () => {
+ const result = await apolloClient.query({
+ query: gql`
+ query Health {
+ health
}
- }
- `,
+ `,
+ });
+ expect(result.data.health).toEqual(true);
});
-
- expect(result.data.hello4.username).toEqual('somefolk');
});
});
});
diff --git a/spec/ParseLiveQuery.spec.js b/spec/ParseLiveQuery.spec.js
index 74faa9aedc..fa83588b9d 100644
--- a/spec/ParseLiveQuery.spec.js
+++ b/spec/ParseLiveQuery.spec.js
@@ -24,6 +24,43 @@ describe('ParseLiveQuery', function() {
await object.save();
});
+ it('handle invalid websocket payload length', async done => {
+ await reconfigureServer({
+ liveQuery: {
+ classNames: ['TestObject'],
+ },
+ startLiveQueryServer: true,
+ verbose: false,
+ silent: true,
+ websocketTimeout: 100,
+ });
+ const object = new TestObject();
+ await object.save();
+
+ const query = new Parse.Query(TestObject);
+ query.equalTo('objectId', object.id);
+ const subscription = await query.subscribe();
+
+ // All control frames must have a payload length of 125 bytes or less.
+ // https://tools.ietf.org/html/rfc6455#section-5.5
+ //
+ // 0x89 = 10001001 = ping
+ // 0xfe = 11111110 = first bit is masking the remaining 7 are 1111110 or 126 the payload length
+ // https://tools.ietf.org/html/rfc6455#section-5.2
+ const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
+ client.socket._socket.write(Buffer.from([0x89, 0xfe]));
+
+ subscription.on('update', async object => {
+ expect(object.get('foo')).toBe('bar');
+ done();
+ });
+ // Wait for Websocket timeout to reconnect
+ setTimeout(async () => {
+ object.set({ foo: 'bar' });
+ await object.save();
+ }, 1000);
+ });
+
afterEach(async function(done) {
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
client.close();
diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js
index 260a48f217..ee4727404a 100644
--- a/spec/ParseQuery.spec.js
+++ b/spec/ParseQuery.spec.js
@@ -4868,4 +4868,36 @@ describe('Parse.Query testing', () => {
const results = await query.find();
equal(results[0].get('array').length, 105);
});
+
+ it('exclude keys (sdk query)', async done => {
+ const obj = new TestObject({ foo: 'baz', hello: 'world' });
+ await obj.save();
+
+ const query = new Parse.Query('TestObject');
+ query.exclude('foo');
+
+ const object = await query.get(obj.id);
+ expect(object.get('foo')).toBeUndefined();
+ expect(object.get('hello')).toBe('world');
+ done();
+ });
+
+ xit('todo: exclude keys with select key (sdk query get)', async done => {
+ // there is some problem with js sdk caching
+
+ const obj = new TestObject({ foo: 'baz', hello: 'world' });
+ await obj.save();
+
+ const query = new Parse.Query('TestObject');
+
+ query.withJSON({
+ keys: 'hello',
+ excludeKeys: 'hello',
+ });
+
+ const object = await query.get(obj.id);
+ expect(object.get('foo')).toBeUndefined();
+ expect(object.get('hello')).toBeUndefined();
+ done();
+ });
});
diff --git a/spec/ParseWebSocketServer.spec.js b/spec/ParseWebSocketServer.spec.js
index c9416f4c08..7bf8a5c57d 100644
--- a/spec/ParseWebSocketServer.spec.js
+++ b/spec/ParseWebSocketServer.spec.js
@@ -1,11 +1,12 @@
const {
ParseWebSocketServer,
} = require('../lib/LiveQuery/ParseWebSocketServer');
+const EventEmitter = require('events');
describe('ParseWebSocketServer', function() {
beforeEach(function(done) {
// Mock ws server
- const EventEmitter = require('events');
+
const mockServer = function() {
return new EventEmitter();
};
@@ -22,11 +23,11 @@ describe('ParseWebSocketServer', function() {
onConnectCallback,
{ websocketTimeout: 5 }
).server;
- const ws = {
- readyState: 0,
- OPEN: 0,
- ping: jasmine.createSpy('ping'),
- };
+ const ws = new EventEmitter();
+ ws.readyState = 0;
+ ws.OPEN = 0;
+ ws.ping = jasmine.createSpy('ping');
+
parseWebSocketServer.onConnection(ws);
// Make sure callback is called
diff --git a/spec/PointerPermissions.spec.js b/spec/PointerPermissions.spec.js
index bd7e34b308..a3a1b9f498 100644
--- a/spec/PointerPermissions.spec.js
+++ b/spec/PointerPermissions.spec.js
@@ -2047,7 +2047,7 @@ describe('Pointer Permissions', () => {
}
async function logIn(userObject) {
- await Parse.User.logIn(userObject.getUsername(), 'password');
+ return await Parse.User.logIn(userObject.getUsername(), 'password');
}
async function updateCLP(clp) {
@@ -3098,5 +3098,55 @@ describe('Pointer Permissions', () => {
done();
});
});
+
+ describe('using pointer-fields and queries with keys projection', () => {
+ let user1;
+ /**
+ * owner: user1
+ *
+ * testers: [user1]
+ */
+ let obj;
+
+ /**
+ * Clear cache, create user and object, login user
+ */
+ async function initialize() {
+ await Config.get(Parse.applicationId).database.schemaCache.clear();
+
+ user1 = await createUser('user1');
+ user1 = await logIn(user1);
+
+ obj = new Parse.Object(className);
+
+ obj.set('owner', user1);
+ obj.set('field', 'field');
+ obj.set('test', 'test');
+
+ await Parse.Object.saveAll([obj], { useMasterKey: true });
+
+ await obj.fetch();
+ }
+
+ beforeEach(async () => {
+ await initialize();
+ });
+
+ it('should be enforced regardless of pointer-field being included in keys (select)', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { pointerFields: ['owner'] },
+ update: { pointerFields: ['owner'] },
+ });
+
+ const query = new Parse.Query('AnObject');
+ query.select('field', 'test');
+
+ const [object] = await query.find({ objectId: obj.id });
+ expect(object.get('field')).toBe('field');
+ expect(object.get('test')).toBe('test');
+ done();
+ });
+ });
});
});
diff --git a/spec/ProtectedFields.spec.js b/spec/ProtectedFields.spec.js
index 78e44f3bc1..fdc3c2d10a 100644
--- a/spec/ProtectedFields.spec.js
+++ b/spec/ProtectedFields.spec.js
@@ -1,5 +1,13 @@
const Config = require('../lib/Config');
const Parse = require('parse/node');
+const request = require('../lib/request');
+const {
+ className,
+ createRole,
+ createUser,
+ logIn,
+ updateCLP,
+} = require('./dev');
describe('ProtectedFields', function() {
it('should handle and empty protectedFields', async function() {
@@ -310,7 +318,7 @@ describe('ProtectedFields', function() {
done();
});
- it('should create merge protected fields when using multiple pointer-permission fields', async done => {
+ it('should intersect protected fields when using multiple pointer-permission fields', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
@@ -327,8 +335,8 @@ describe('ProtectedFields', function() {
get: { '*': true },
find: { '*': true },
protectedFields: {
- '*': [],
- 'userField:owners': ['owners'],
+ '*': ['owners', 'owner', 'test'],
+ 'userField:owners': ['owners', 'owner'],
'userField:owner': ['owner'],
},
}
@@ -337,7 +345,7 @@ describe('ProtectedFields', function() {
// Check if protectFields from pointer-permissions got combined
await Parse.User.logIn('user1', 'password');
const objectAgain = await obj.fetch();
- expect(objectAgain.get('owners')).toBe(undefined);
+ expect(objectAgain.get('owners').length).toBe(1);
expect(objectAgain.get('owner')).toBe(undefined);
expect(objectAgain.get('test')).toBe('test');
done();
@@ -605,7 +613,7 @@ describe('ProtectedFields', function() {
done();
});
- it('should create merge protected fields when using multiple pointer-permission fields', async done => {
+ it('should intersect protected fields when using multiple pointer-permission fields', async done => {
const config = Config.get(Parse.applicationId);
const obj = new Parse.Object('AnObject');
const obj2 = new Parse.Object('AnObject');
@@ -614,7 +622,6 @@ describe('ProtectedFields', function() {
obj.set('owner', user1);
obj.set('test', 'test');
obj2.set('owners', [user1]);
- obj2.set('owner', user1);
obj2.set('test', 'test2');
await Parse.Object.saveAll([obj, obj2]);
@@ -626,8 +633,8 @@ describe('ProtectedFields', function() {
get: { '*': true },
find: { '*': true },
protectedFields: {
- '*': [],
- 'userField:owners': ['owners'],
+ '*': ['owners', 'owner', 'test'],
+ 'userField:owners': ['owners', 'owner'],
'userField:owner': ['owner'],
},
}
@@ -642,7 +649,7 @@ describe('ProtectedFields', function() {
results.sort((a, b) => a.get('test').localeCompare(b.get('test')));
expect(results.length).toBe(2);
- expect(results[0].get('owners')).toBe(undefined);
+ expect(results[0].get('owners').length).toBe(1);
expect(results[0].get('owner')).toBe(undefined);
expect(results[0].get('test')).toBe('test');
expect(results[1].get('owners')).toBe(undefined);
@@ -760,20 +767,24 @@ describe('ProtectedFields', function() {
});
describe('schema setup', () => {
- const className = 'AObject';
- async function updateCLP(clp) {
- const config = Config.get(Parse.applicationId);
- const schemaController = await config.database.loadSchema();
+ let object;
- await schemaController.updateClass(className, {}, clp);
+ async function initialize() {
+ await Config.get(Parse.applicationId).database.schemaCache.clear();
+
+ object = new Parse.Object(className);
+
+ object.set('revision', 0);
+ object.set('test', 'test');
+
+ await object.save(null, { useMasterKey: true });
}
- it('should fail setting non-existing protected field', async () => {
- const object = new Parse.Object(className, {
- revision: 0,
- });
- await object.save();
+ beforeEach(async () => {
+ await initialize();
+ });
+ it('should fail setting non-existing protected field', async done => {
const field = 'non-existing';
const entity = '*';
@@ -789,6 +800,936 @@ describe('ProtectedFields', function() {
`Field '${field}' in protectedFields:${entity} does not exist`
)
);
+ done();
+ });
+
+ it('should allow setting authenticated', async () => {
+ await expectAsync(
+ updateCLP({
+ protectedFields: {
+ authenticated: ['test'],
+ },
+ })
+ ).toBeResolved();
+ });
+
+ it('should not allow protecting default fields', async () => {
+ const defaultFields = ['objectId', 'createdAt', 'updatedAt', 'ACL'];
+ for (const field of defaultFields) {
+ await expectAsync(
+ updateCLP({
+ protectedFields: {
+ '*': [field],
+ },
+ })
+ ).toBeRejectedWith(
+ new Parse.Error(
+ Parse.Error.INVALID_JSON,
+ `Default field '${field}' can not be protected`
+ )
+ );
+ }
+ });
+ });
+
+ describe('targeting public access', () => {
+ let obj1;
+
+ async function initialize() {
+ await Config.get(Parse.applicationId).database.schemaCache.clear();
+
+ obj1 = new Parse.Object(className);
+
+ obj1.set('foo', 'foo');
+ obj1.set('bar', 'bar');
+ obj1.set('qux', 'qux');
+
+ await obj1.save(null, {
+ useMasterKey: true,
+ });
+ }
+
+ beforeEach(async () => {
+ await initialize();
+ });
+
+ it('should hide field', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['foo'],
+ },
+ });
+
+ // unauthenticated
+ const object = await obj1.fetch();
+
+ expect(object.get('foo')).toBe(undefined);
+ expect(object.get('bar')).toBeDefined();
+ expect(object.get('qux')).toBeDefined();
+
+ done();
+ });
+
+ it('should hide mutiple fields', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['foo', 'bar'],
+ },
+ });
+
+ // unauthenticated
+ const object = await obj1.fetch();
+
+ expect(object.get('foo')).toBe(undefined);
+ expect(object.get('bar')).toBe(undefined);
+ expect(object.get('qux')).toBeDefined();
+
+ done();
+ });
+
+ it('should not hide any fields when set as empty array', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': [],
+ },
+ });
+
+ // unauthenticated
+ const object = await obj1.fetch();
+
+ expect(object.get('foo')).toBeDefined();
+ expect(object.get('bar')).toBeDefined();
+ expect(object.get('qux')).toBeDefined();
+ expect(object.id).toBeDefined();
+ expect(object.createdAt).toBeDefined();
+ expect(object.updatedAt).toBeDefined();
+ expect(object.getACL()).toBeDefined();
+
+ done();
+ });
+ });
+
+ describe('targeting authenticated', () => {
+ /**
+ * is **owner** of: _obj1_
+ *
+ * is **tester** of: [ _obj1, obj2_ ]
+ */
+ let user1;
+
+ /**
+ * is **owner** of: _obj2_
+ *
+ * is **tester** of: [ _obj1_ ]
+ */
+ let user2;
+
+ /**
+ * **owner**: _user1_
+ *
+ * **testers**: [ _user1,user2_ ]
+ */
+ let obj1;
+
+ /**
+ * **owner**: _user2_
+ *
+ * **testers**: [ _user1_ ]
+ */
+ let obj2;
+
+ async function initialize() {
+ await Config.get(Parse.applicationId).database.schemaCache.clear();
+
+ await Parse.User.logOut();
+
+ [user1, user2] = await Promise.all([
+ createUser('user1'),
+ createUser('user2'),
+ ]);
+
+ obj1 = new Parse.Object(className);
+ obj2 = new Parse.Object(className);
+
+ obj1.set('owner', user1);
+ obj1.set('testers', [user1, user2]);
+ obj1.set('test', 'test');
+
+ obj2.set('owner', user2);
+ obj2.set('testers', [user1]);
+ obj2.set('test', 'test');
+
+ await Parse.Object.saveAll([obj1, obj2], {
+ useMasterKey: true,
+ });
+ }
+
+ beforeEach(async () => {
+ await initialize();
+ });
+
+ it('should not hide any fields when set as empty array', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ authenticated: [],
+ },
+ });
+
+ // authenticated
+ await logIn(user1);
+
+ const object = await obj1.fetch();
+
+ expect(object.get('owner')).toBeDefined();
+ expect(object.get('testers')).toBeDefined();
+ expect(object.get('test')).toBeDefined();
+ expect(object.id).toBeDefined();
+ expect(object.createdAt).toBeDefined();
+ expect(object.updatedAt).toBeDefined();
+ expect(object.getACL()).toBeDefined();
+
+ done();
+ });
+
+ it('should hide fields for authenticated users only (* not set)', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ authenticated: ['test'],
+ },
+ });
+
+ // not authenticated
+ const objectNonAuth = await obj1.fetch();
+
+ expect(objectNonAuth.get('test')).toBeDefined();
+
+ // authenticated
+ await logIn(user1);
+ const object = await obj1.fetch();
+
+ expect(object.get('test')).toBe(undefined);
+
+ done();
+ });
+
+ it('should intersect public and auth for authenticated user', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['owner', 'testers'],
+ authenticated: ['testers'],
+ },
+ });
+
+ // authenticated
+ await logIn(user1);
+ const objectAuth = await obj1.fetch();
+
+ // ( {A,B} intersect {B} ) == {B}
+
+ expect(objectAuth.get('testers')).not.toBeDefined(
+ 'Should not be visible - protected for * and authenticated'
+ );
+ expect(objectAuth.get('test')).toBeDefined(
+ 'Should be visible - not protected for everyone (* and authenticated)'
+ );
+ expect(objectAuth.get('owner')).toBeDefined(
+ 'Should be visible - not protected for authenticated'
+ );
+
+ done();
+ });
+
+ it('should have higher prio than public for logged in users (intersect)', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['test'],
+ authenticated: [],
+ },
+ });
+ // authenticated, permitted
+ await logIn(user1);
+
+ const object = await obj1.fetch();
+ expect(object.get('test')).toBe('test');
+
+ done();
+ });
+
+ it('should have no effect on unauthenticated users (public not set)', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ authenticated: ['test'],
+ },
+ });
+
+ // unauthenticated, protected
+ const objectNonAuth = await obj1.fetch();
+ expect(objectNonAuth.get('test')).toBe('test');
+
+ done();
+ });
+
+ it('should protect multiple fields for authenticated users', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ authenticated: ['test', 'owner'],
+ },
+ });
+
+ // authenticated
+ await logIn(user1);
+ const object = await obj1.fetch();
+
+ expect(object.get('test')).toBe(undefined);
+ expect(object.get('owner')).toBe(undefined);
+
+ done();
+ });
+
+ it('should not be affected by rules not applicable to user (smoke)', async done => {
+ const role = await createRole({ users: user1 });
+ const roleName = role.get('name');
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ authenticated: ['owner', 'testers'],
+ [`role:${roleName}`]: ['test'],
+ 'userField:owner': [],
+ [user1.id]: [],
+ },
+ });
+
+ // authenticated, non-owner, no role
+ await logIn(user2);
+ const objectNotOwned = await obj1.fetch();
+
+ expect(objectNotOwned.get('owner')).toBe(undefined);
+ expect(objectNotOwned.get('testers')).toBe(undefined);
+ expect(objectNotOwned.get('test')).toBeDefined();
+
+ done();
+ });
+ });
+
+ describe('targeting roles', () => {
+ let user1, user2;
+
+ /**
+ * owner: user1
+ *
+ * testers: [user1,user2]
+ */
+ let obj1;
+
+ /**
+ * owner: user2
+ *
+ * testers: [user1]
+ */
+ let obj2;
+
+ async function initialize() {
+ await Config.get(Parse.applicationId).database.schemaCache.clear();
+
+ [user1, user2] = await Promise.all([
+ createUser('user1'),
+ createUser('user2'),
+ ]);
+
+ obj1 = new Parse.Object(className);
+ obj2 = new Parse.Object(className);
+
+ obj1.set('owner', user1);
+ obj1.set('testers', [user1, user2]);
+ obj1.set('test', 'test');
+
+ obj2.set('owner', user2);
+ obj2.set('testers', [user1]);
+ obj2.set('test', 'test');
+
+ await Parse.Object.saveAll([obj1, obj2], {
+ useMasterKey: true,
+ });
+ }
+
+ beforeEach(async () => {
+ await initialize();
+ });
+
+ it('should hide field when user belongs to a role', async done => {
+ const role = await createRole({ users: user1 });
+ const roleName = role.get('name');
+
+ await updateCLP({
+ protectedFields: {
+ [`role:${roleName}`]: ['test'],
+ },
+ get: { '*': true },
+ find: { '*': true },
+ });
+
+ // user has role
+ await logIn(user1);
+
+ const object = await obj1.fetch();
+ expect(object.get('test')).toBe(undefined); // field protected
+ expect(object.get('owner')).toBeDefined();
+ expect(object.get('testers')).toBeDefined();
+
+ done();
+ });
+
+ it('should not hide any fields when set as empty array', async done => {
+ const role = await createRole({ users: user1 });
+ const roleName = role.get('name');
+
+ await updateCLP({
+ protectedFields: {
+ [`role:${roleName}`]: [],
+ },
+ get: { '*': true },
+ find: { '*': true },
+ });
+
+ // user has role
+ await logIn(user1);
+
+ const object = await obj1.fetch();
+
+ expect(object.get('owner')).toBeDefined();
+ expect(object.get('testers')).toBeDefined();
+ expect(object.get('test')).toBeDefined();
+ expect(object.id).toBeDefined();
+ expect(object.createdAt).toBeDefined();
+ expect(object.updatedAt).toBeDefined();
+ expect(object.getACL()).toBeDefined();
+
+ done();
+ });
+
+ it('should hide multiple fields when user belongs to a role', async done => {
+ const role = await createRole({ users: user1 });
+ const roleName = role.get('name');
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ [`role:${roleName}`]: ['test', 'owner'],
+ },
+ });
+
+ // user has role
+ await logIn(user1);
+
+ const object = await obj1.fetch();
+
+ expect(object.get('test')).toBe(
+ undefined,
+ 'Field should not be visible - protected by role'
+ );
+ expect(object.get('owner')).toBe(
+ undefined,
+ 'Field should not be visible - protected by role'
+ );
+ expect(object.get('testers')).toBeDefined();
+
+ done();
+ });
+
+ it('should not protect when user does not belong to a role', async done => {
+ const role = await createRole({ users: user1 });
+ const roleName = role.get('name');
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ [`role:${roleName}`]: ['test', 'owner'],
+ },
+ });
+
+ // user doesn't have role
+ await logIn(user2);
+ const object = await obj1.fetch();
+
+ expect(object.get('test')).toBeDefined();
+ expect(object.get('owner')).toBeDefined();
+ expect(object.get('testers')).toBeDefined();
+
+ done();
+ });
+
+ it('should intersect protected fields when user belongs to multiple roles', async done => {
+ const role1 = await createRole({ users: user1 });
+ const role2 = await createRole({ users: user1 });
+
+ const role1name = role1.get('name');
+ const role2name = role2.get('name');
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ [`role:${role1name}`]: ['owner'],
+ [`role:${role2name}`]: ['test', 'owner'],
+ },
+ });
+
+ // user has both roles
+ await logIn(user1);
+ const object = await obj1.fetch();
+
+ // "owner" is a result of intersection
+ expect(object.get('owner')).toBe(
+ undefined,
+ 'Must not be visible - protected for all roles the user belongs to'
+ );
+ expect(object.get('test')).toBeDefined(
+ 'Has to be visible - is not protected for users with role1'
+ );
+ done();
+ });
+
+ it('should intersect protected fields when user belongs to multiple roles hierarchy', async done => {
+ const admin = await createRole({
+ users: user1,
+ roleName: 'admin',
+ });
+
+ const moder = await createRole({
+ users: [user1, user2],
+ roleName: 'moder',
+ });
+
+ const tester = await createRole({
+ roleName: 'tester',
+ });
+
+ // admin supersets moder role
+ moder.relation('roles').add(admin);
+ await moder.save(null, { useMasterKey: true });
+
+ tester.relation('roles').add(moder);
+ await tester.save(null, { useMasterKey: true });
+
+ const roleAdmin = `role:${admin.get('name')}`;
+ const roleModer = `role:${moder.get('name')}`;
+ const roleTester = `role:${tester.get('name')}`;
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ [roleAdmin]: [],
+ [roleModer]: ['owner'],
+ [roleTester]: ['test', 'owner'],
+ },
+ });
+
+ // user1 has admin & moder & tester roles, (moder includes tester).
+ await logIn(user1);
+ const object = await obj1.fetch();
+
+ // being admin makes all fields visible
+ expect(object.get('test')).toBeDefined(
+ 'Should be visible - admin role explicitly removes protection for all fields ( [] )'
+ );
+ expect(object.get('owner')).toBeDefined(
+ 'Should be visible - admin role explicitly removes protection for all fields ( [] )'
+ );
+
+ // user2 has moder & tester role, moder includes tester.
+ await logIn(user2);
+ const objectAgain = await obj1.fetch();
+
+ // being moder allows "test" field
+ expect(objectAgain.get('owner')).toBe(
+ undefined,
+ '"owner" should not be visible - protected for each role user belongs to'
+ );
+ expect(objectAgain.get('test')).toBeDefined(
+ 'Should be visible - moder role does not protect "test" field'
+ );
+
+ done();
+ });
+
+ it('should be able to clear protected fields for role (protected for authenticated)', async done => {
+ const role = await createRole({ users: user1 });
+ const roleName = role.get('name');
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ authenticated: ['test'],
+ [`role:${roleName}`]: [],
+ },
+ });
+
+ // user has role, test field visible
+ await logIn(user1);
+ const object = await obj1.fetch();
+ expect(object.get('test')).toBe('test');
+
+ done();
+ });
+
+ it('should determine protectedFields as intersection of field sets for public and role', async done => {
+ const role = await createRole({ users: user1 });
+ const roleName = role.get('name');
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['test', 'owner'],
+ [`role:${roleName}`]: ['owner', 'testers'],
+ },
+ });
+
+ // user has role
+ await logIn(user1);
+
+ const object = await obj1.fetch();
+ expect(object.get('test')).toBeDefined(
+ 'Should be visible - "test" is not protected for role user belongs to'
+ );
+ expect(object.get('testers')).toBeDefined(
+ 'Should be visible - "testers" is allowed for everyone (*)'
+ );
+ expect(object.get('owner')).toBe(
+ undefined,
+ 'Should not be visible - "test" is not allowed for both public(*) and role'
+ );
+ done();
+ });
+
+ it('should be determined as an intersection of protecedFields for authenticated and role', async done => {
+ const role = await createRole({ users: user1 });
+ const roleName = role.get('name');
+
+ // this is an example of misunderstood configuration.
+ // If you allow (== do not restrict) some field for broader audience
+ // (having a role implies user inheres to 'authenticated' group)
+ // it's not possible to narrow by protecting field for a role.
+ // You'd have to protect it for 'authenticated' as well.
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ authenticated: ['test'],
+ [`role:${roleName}`]: ['owner'],
+ },
+ });
+
+ // user has role
+ await logIn(user1);
+ const object = await obj1.fetch();
+
+ //
+ expect(object.get('test')).toBeDefined(
+ "Being both auhenticated and having a role leads to clearing protection on 'test' (by role rules)"
+ );
+ expect(object.get('owner')).toBeDefined(
+ 'All authenticated users allowed to see "owner"'
+ );
+ expect(object.get('testers')).toBeDefined();
+
+ done();
+ });
+
+ it('should not hide fields when user does not belong to a role protectedFields set for', async done => {
+ const role = await createRole({ users: user2 });
+ const roleName = role.get('name');
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ [`role:${roleName}`]: ['test'],
+ },
+ });
+
+ // relate user1 to some role, no protectedFields for it
+ await createRole({ users: user1 });
+
+ await logIn(user1);
+
+ const object = await obj1.fetch();
+ expect(object.get('test')).toBeDefined(
+ 'Field should be visible - user belongs to a role that has no protectedFields set'
+ );
+
+ done();
+ });
+ });
+
+ describe('using pointer-fields and queries with keys projection', () => {
+ /*
+ * Pointer variant ("userField:column") relies on User ids
+ * returned after query executed (hides fields before sending it to client)
+ * If such column is excluded/not included (not returned from db because of 'project')
+ * there will be no user ids to check against
+ * and protectedFields won't be applied correctly.
+ */
+
+ let user1;
+ /**
+ * owner: user1
+ *
+ * testers: [user1]
+ */
+ let obj;
+
+ let headers;
+
+ /**
+ * Clear cache, create user and object, login user and setup rest headers with token
+ */
+ async function initialize() {
+ await Config.get(Parse.applicationId).database.schemaCache.clear();
+
+ user1 = await createUser('user1');
+ user1 = await logIn(user1);
+
+ // await user1.fetch();
+ obj = new Parse.Object(className);
+
+ obj.set('owner', user1);
+ obj.set('field', 'field');
+ obj.set('test', 'test');
+
+ await Parse.Object.saveAll([obj], { useMasterKey: true });
+
+ headers = {
+ 'X-Parse-Application-Id': Parse.applicationId,
+ 'X-Parse-Rest-API-Key': 'rest',
+ 'Content-Type': 'application/json',
+ 'X-Parse-Session-Token': user1.getSessionToken(),
+ };
+ }
+
+ beforeEach(async () => {
+ await initialize();
+ });
+
+ it('should be enforced regardless of pointer-field being included in keys (select)', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['field', 'test'],
+ 'userField:owner': [],
+ },
+ });
+
+ const query = new Parse.Query('AnObject');
+ query.select('field', 'test');
+
+ const object = await query.get(obj.id);
+ expect(object.get('field')).toBe('field');
+ expect(object.get('test')).toBe('test');
+ done();
+ });
+
+ it('should protect fields for query where pointer field is not included via keys (REST GET)', async done => {
+ const obj = new Parse.Object(className);
+
+ obj.set('owner', user1);
+ obj.set('field', 'field');
+ obj.set('test', 'test');
+
+ await Parse.Object.saveAll([obj], { useMasterKey: true });
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['field', 'test'],
+ 'userField:owner': ['test'],
+ },
+ });
+
+ const { data: object } = await request({
+ url: `${Parse.serverURL}/classes/${className}/${obj.id}`,
+ qs: {
+ keys: 'field,test',
+ },
+ headers: headers,
+ });
+
+ expect(object.field).toBe(
+ 'field',
+ 'Should BE in response - not protected by "userField:owner"'
+ );
+ expect(object.test).toBe(
+ undefined,
+ 'Should NOT be in response - protected by "userField:owner"'
+ );
+ expect(object.owner).toBe(
+ undefined,
+ 'Should not be in response - not included in "keys"'
+ );
+ done();
+ });
+
+ it('should protect fields for query where pointer field is not included via keys (REST FIND)', async done => {
+ const obj = new Parse.Object(className);
+
+ obj.set('owner', user1);
+ obj.set('field', 'field');
+ obj.set('test', 'test');
+
+ await Parse.Object.saveAll([obj], { useMasterKey: true });
+
+ await obj.fetch();
+
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['field', 'test'],
+ 'userField:owner': ['test'],
+ },
+ });
+
+ const { data } = await request({
+ url: `${Parse.serverURL}/classes/${className}`,
+ qs: {
+ keys: 'field,test',
+ where: JSON.stringify({ objectId: obj.id }),
+ },
+ headers,
+ });
+
+ const object = data.results[0];
+
+ expect(object.field).toBe(
+ 'field',
+ 'Should be in response - not protected by "userField:owner"'
+ );
+ expect(object.test).toBe(
+ undefined,
+ 'Should not be in response - protected by "userField:owner"'
+ );
+ expect(object.owner).toBe(
+ undefined,
+ 'Should not be in response - not included in "keys"'
+ );
+ done();
+ });
+
+ it('should protect fields for query where pointer field is in excludeKeys (REST GET)', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['field', 'test'],
+ 'userField:owner': ['test'],
+ },
+ });
+
+ const { data: object } = await request({
+ qs: {
+ excludeKeys: 'owner',
+ },
+ headers,
+ url: `${Parse.serverURL}/classes/${className}/${obj.id}`,
+ });
+
+ expect(object.field).toBe(
+ 'field',
+ 'Should be in response - not protected by "userField:owner"'
+ );
+ expect(object['test']).toBe(
+ undefined,
+ 'Should not be in response - protected by "userField:owner"'
+ );
+ expect(object['owner']).toBe(
+ undefined,
+ 'Should not be in response - not included in "keys"'
+ );
+ done();
+ });
+
+ it('should protect fields for query where pointer field is in excludedKeys (REST FIND)', async done => {
+ await updateCLP({
+ protectedFields: {
+ '*': ['field', 'test'],
+ 'userField:owner': ['test'],
+ },
+ get: { '*': true },
+ find: { '*': true },
+ });
+
+ const { data } = await request({
+ qs: {
+ excludeKeys: 'owner',
+ where: JSON.stringify({ objectId: obj.id }),
+ },
+ headers,
+ url: `${Parse.serverURL}/classes/${className}`,
+ });
+
+ const object = data.results[0];
+
+ expect(object.field).toBe(
+ 'field',
+ 'Should be in response - not protected by "userField:owner"'
+ );
+ expect(object.test).toBe(
+ undefined,
+ 'Should not be in response - protected by "userField:owner"'
+ );
+ expect(object.owner).toBe(
+ undefined,
+ 'Should not be in response - not included in "keys"'
+ );
+ done();
+ });
+
+ xit('todo: should be enforced regardless of pointer-field being excluded', async done => {
+ await updateCLP({
+ get: { '*': true },
+ find: { '*': true },
+ protectedFields: {
+ '*': ['field', 'test'],
+ 'userField:owner': [],
+ },
+ });
+
+ const query = new Parse.Query('AnObject');
+
+ /* TODO: this has some caching problems on JS-SDK (2.11.) side */
+ // query.exclude('owner')
+
+ const object = await query.get(obj.id);
+ expect(object.get('field')).toBe('field');
+ expect(object.get('test')).toBe('test');
+ expect(object.get('owner')).toBe(undefined);
+ done();
});
});
});
diff --git a/spec/RegexVulnerabilities.spec.js b/spec/RegexVulnerabilities.spec.js
new file mode 100644
index 0000000000..58f5afac92
--- /dev/null
+++ b/spec/RegexVulnerabilities.spec.js
@@ -0,0 +1,198 @@
+const request = require('../lib/request');
+
+const serverURL = 'http://localhost:8378/1';
+const headers = {
+ 'Content-Type': 'application/json',
+};
+const keys = {
+ _ApplicationId: 'test',
+ _JavaScriptKey: 'test',
+};
+const emailAdapter = {
+ sendVerificationEmail: () => Promise.resolve(),
+ sendPasswordResetEmail: () => Promise.resolve(),
+ sendMail: () => {},
+};
+const appName = 'test';
+const publicServerURL = 'http://localhost:8378/1';
+
+describe('Regex Vulnerabilities', function() {
+ beforeEach(async function() {
+ await reconfigureServer({
+ verifyUserEmails: true,
+ emailAdapter,
+ appName,
+ publicServerURL,
+ });
+
+ const signUpResponse = await request({
+ url: `${serverURL}/users`,
+ method: 'POST',
+ headers,
+ body: JSON.stringify({
+ ...keys,
+ _method: 'POST',
+ username: 'someemail@somedomain.com',
+ password: 'somepassword',
+ email: 'someemail@somedomain.com',
+ }),
+ });
+ this.objectId = signUpResponse.data.objectId;
+ this.sessionToken = signUpResponse.data.sessionToken;
+ this.partialSessionToken = this.sessionToken.slice(0, 3);
+ });
+
+ describe('on session token', function() {
+ it('should not work with regex', async function() {
+ try {
+ await request({
+ url: `${serverURL}/users/me`,
+ method: 'POST',
+ headers,
+ body: JSON.stringify({
+ ...keys,
+ _SessionToken: {
+ $regex: this.partialSessionToken,
+ },
+ _method: 'GET',
+ }),
+ });
+ fail('should not work');
+ } catch (e) {
+ expect(e.data.code).toEqual(209);
+ expect(e.data.error).toEqual('Invalid session token');
+ }
+ });
+
+ it('should work with plain token', async function() {
+ const meResponse = await request({
+ url: `${serverURL}/users/me`,
+ method: 'POST',
+ headers,
+ body: JSON.stringify({
+ ...keys,
+ _SessionToken: this.sessionToken,
+ _method: 'GET',
+ }),
+ });
+ expect(meResponse.data.objectId).toEqual(this.objectId);
+ expect(meResponse.data.sessionToken).toEqual(this.sessionToken);
+ });
+ });
+
+ describe('on verify e-mail', function() {
+ beforeEach(async function() {
+ const userQuery = new Parse.Query(Parse.User);
+ this.user = await userQuery.get(this.objectId, { useMasterKey: true });
+ });
+
+ it('should not work with regex', async function() {
+ expect(this.user.get('emailVerified')).toEqual(false);
+ await request({
+ url: `${serverURL}/apps/test/verify_email?username=someemail@somedomain.com&token[$regex]=`,
+ method: 'GET',
+ });
+ await this.user.fetch({ useMasterKey: true });
+ expect(this.user.get('emailVerified')).toEqual(false);
+ });
+
+ it('should work with plain token', async function() {
+ expect(this.user.get('emailVerified')).toEqual(false);
+ // It should work
+ await request({
+ url: `${serverURL}/apps/test/verify_email?username=someemail@somedomain.com&token=${this.user.get(
+ '_email_verify_token'
+ )}`,
+ method: 'GET',
+ });
+ await this.user.fetch({ useMasterKey: true });
+ expect(this.user.get('emailVerified')).toEqual(true);
+ });
+ });
+
+ describe('on password reset', function() {
+ beforeEach(async function() {
+ this.user = await Parse.User.logIn(
+ 'someemail@somedomain.com',
+ 'somepassword'
+ );
+ });
+
+ it('should not work with regex', async function() {
+ expect(this.user.id).toEqual(this.objectId);
+ await request({
+ url: `${serverURL}/requestPasswordReset`,
+ method: 'POST',
+ headers,
+ body: JSON.stringify({
+ ...keys,
+ _method: 'POST',
+ email: 'someemail@somedomain.com',
+ }),
+ });
+ await this.user.fetch({ useMasterKey: true });
+ const passwordResetResponse = await request({
+ url: `${serverURL}/apps/test/request_password_reset?username=someemail@somedomain.com&token[$regex]=`,
+ method: 'GET',
+ });
+ expect(passwordResetResponse.status).toEqual(302);
+ expect(passwordResetResponse.headers.location).toMatch(
+ `\\/invalid\\_link\\.html`
+ );
+ await request({
+ url: `${serverURL}/apps/test/request_password_reset`,
+ method: 'POST',
+ body: {
+ token: { $regex: '' },
+ username: 'someemail@somedomain.com',
+ new_password: 'newpassword',
+ },
+ });
+ try {
+ await Parse.User.logIn('someemail@somedomain.com', 'newpassword');
+ fail('should not work');
+ } catch (e) {
+ expect(e.code).toEqual(101);
+ expect(e.message).toEqual('Invalid username/password.');
+ }
+ });
+
+ it('should work with plain token', async function() {
+ expect(this.user.id).toEqual(this.objectId);
+ await request({
+ url: `${serverURL}/requestPasswordReset`,
+ method: 'POST',
+ headers,
+ body: JSON.stringify({
+ ...keys,
+ _method: 'POST',
+ email: 'someemail@somedomain.com',
+ }),
+ });
+ await this.user.fetch({ useMasterKey: true });
+ const token = this.user.get('_perishable_token');
+ const passwordResetResponse = await request({
+ url: `${serverURL}/apps/test/request_password_reset?username=someemail@somedomain.com&token=${token}`,
+ method: 'GET',
+ });
+ expect(passwordResetResponse.status).toEqual(302);
+ expect(passwordResetResponse.headers.location).toMatch(
+ `\\/choose\\_password\\?token\\=${token}\\&`
+ );
+ await request({
+ url: `${serverURL}/apps/test/request_password_reset`,
+ method: 'POST',
+ body: {
+ token,
+ username: 'someemail@somedomain.com',
+ new_password: 'newpassword',
+ },
+ });
+ const userAgain = await Parse.User.logIn(
+ 'someemail@somedomain.com',
+ 'newpassword'
+ );
+ expect(userAgain.id).toEqual(this.objectId);
+ });
+ });
+});
diff --git a/spec/dev.js b/spec/dev.js
new file mode 100644
index 0000000000..31425dec40
--- /dev/null
+++ b/spec/dev.js
@@ -0,0 +1,98 @@
+const Config = require('../lib/Config');
+const Parse = require('parse/node');
+
+const className = 'AnObject';
+const defaultRoleName = 'tester';
+
+let schemaCache;
+
+module.exports = {
+ /* AnObject */
+ className,
+ schemaCache,
+
+ /**
+ * Creates and returns new user.
+ *
+ * This method helps to avoid 'User already exists' when re-running/debugging a single test.
+ * @param {string} username - username base, will be postfixed with current time in millis;
+ * @param {string} [password='password'] - optional, defaults to "password" if not set;
+ */
+ createUser: async (username, password = 'password') => {
+ const user = new Parse.User({
+ username: username + Date.now(),
+ password,
+ });
+ await user.save();
+ return user;
+ },
+
+ /**
+ * Logs the user in.
+ *
+ * If password not provided, default 'password' is used.
+ * @param {string} username - username base, will be postfixed with current time in millis;
+ * @param {string} [password='password'] - optional, defaults to "password" if not set;
+ */
+ logIn: async (userObject, password) => {
+ return await Parse.User.logIn(
+ userObject.getUsername(),
+ password || 'password'
+ );
+ },
+
+ /**
+ * Sets up Class-Level Permissions for 'AnObject' class.
+ * @param clp {ClassLevelPermissions}
+ */
+ updateCLP: async (clp, targetClass = className) => {
+ const config = Config.get(Parse.applicationId);
+ const schemaController = await config.database.loadSchema();
+
+ await schemaController.updateClass(targetClass, {}, clp);
+ },
+
+ /**
+ * Creates and returns role. Adds user(s) if provided.
+ *
+ * This method helps to avoid errors when re-running/debugging a single test.
+ *
+ * @param {Parse.User|Parse.User[]} [users] - user or array of users to be related with this role;
+ * @param {string?} [roleName] - uses this name for role if provided. Generates from datetime if not set;
+ * @param {string?} [exactName] - sets exact name (no generated part added);
+ * @param {Parse.Role[]} [roles] - uses this name for role if provided. Generates from datetime if not set;
+ * @param {boolean} [read] - value for role's acl public read. Defaults to true;
+ * @param {boolean} [write] - value for role's acl public write. Defaults to true;
+ */
+ createRole: async ({
+ users = null,
+ exactName = defaultRoleName + Date.now(),
+ roleName = null,
+ roles = null,
+ read = true,
+ write = true,
+ }) => {
+ const acl = new Parse.ACL();
+ acl.setPublicReadAccess(read);
+ acl.setPublicWriteAccess(write);
+
+ const role = new Parse.Object('_Role');
+ role.setACL(acl);
+
+ // generate name based on roleName or use exactName (if botth not provided name is generated)
+ const name = roleName ? roleName + Date.now() : exactName;
+ role.set('name', name);
+
+ if (roles) {
+ role.relation('roles').add(roles);
+ }
+
+ if (users) {
+ role.relation('users').add(users);
+ }
+
+ await role.save({ useMasterKey: true });
+
+ return role;
+ },
+};
diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js
index c547bf858a..661a19ee24 100644
--- a/spec/schemas.spec.js
+++ b/spec/schemas.spec.js
@@ -2861,6 +2861,35 @@ describe('schemas', () => {
done();
});
+ it('should be rejected if CLP pointerFields is not an array', async done => {
+ const config = Config.get(Parse.applicationId);
+ const schemaController = await config.database.loadSchema();
+
+ const operationKey = 'get';
+ const entity = 'pointerFields';
+ const value = {};
+
+ const schemaSetup = async () =>
+ await schemaController.addClassIfNotExists(
+ 'AnObject',
+ {},
+ {
+ [operationKey]: {
+ [entity]: value,
+ },
+ }
+ );
+
+ await expectAsync(schemaSetup()).toBeRejectedWith(
+ new Parse.Error(
+ Parse.Error.INVALID_JSON,
+ `'${value}' is not a valid value for ${operationKey}[${entity}] - expected an array.`
+ )
+ );
+
+ done();
+ });
+
describe('index management', () => {
beforeEach(() => require('../lib/TestUtils').destroyAllDataPermanently());
it('cannot create index if field does not exist', done => {
diff --git a/src/Config.js b/src/Config.js
index 8d31d3d9ea..2077626ff8 100644
--- a/src/Config.js
+++ b/src/Config.js
@@ -34,8 +34,7 @@ export class Config {
);
config.database = new DatabaseController(
cacheInfo.databaseController.adapter,
- schemaCache,
- cacheInfo.skipMongoDBServer13732Workaround
+ schemaCache
);
} else {
config[key] = cacheInfo[key];
diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js
index 4b69d05e29..5c3b8ab342 100644
--- a/src/Controllers/DatabaseController.js
+++ b/src/Controllers/DatabaseController.js
@@ -69,73 +69,14 @@ const isSpecialQueryKey = key => {
return specialQuerykeys.indexOf(key) >= 0;
};
-const validateQuery = (
- query: any,
- skipMongoDBServer13732Workaround: boolean
-): void => {
+const validateQuery = (query: any): void => {
if (query.ACL) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Cannot query on ACL.');
}
if (query.$or) {
if (query.$or instanceof Array) {
- query.$or.forEach(el =>
- validateQuery(el, skipMongoDBServer13732Workaround)
- );
-
- if (!skipMongoDBServer13732Workaround) {
- /* In MongoDB 3.2 & 3.4, $or queries which are not alone at the top
- * level of the query can not make efficient use of indexes due to a
- * long standing bug known as SERVER-13732.
- *
- * This bug was fixed in MongoDB version 3.6.
- *
- * For versions pre-3.6, the below logic produces a substantial
- * performance improvement inside the database by avoiding the bug.
- *
- * For versions 3.6 and above, there is no performance improvement and
- * the logic is unnecessary. Some query patterns are even slowed by
- * the below logic, due to the bug having been fixed and better
- * query plans being chosen.
- *
- * When versions before 3.4 are no longer supported by this project,
- * this logic, and the accompanying `skipMongoDBServer13732Workaround`
- * flag, can be removed.
- *
- * This block restructures queries in which $or is not the sole top
- * level element by moving all other top-level predicates inside every
- * subdocument of the $or predicate, allowing MongoDB's query planner
- * to make full use of the most relevant indexes.
- *
- * EG: {$or: [{a: 1}, {a: 2}], b: 2}
- * Becomes: {$or: [{a: 1, b: 2}, {a: 2, b: 2}]}
- *
- * The only exceptions are $near and $nearSphere operators, which are
- * constrained to only 1 operator per query. As a result, these ops
- * remain at the top level
- *
- * https://jira.mongodb.org/browse/SERVER-13732
- * https://github.com/parse-community/parse-server/issues/3767
- */
- Object.keys(query).forEach(key => {
- const noCollisions = !query.$or.some(subq =>
- Object.prototype.hasOwnProperty.call(subq, key)
- );
- let hasNears = false;
- if (query[key] != null && typeof query[key] == 'object') {
- hasNears = '$near' in query[key] || '$nearSphere' in query[key];
- }
- if (key != '$or' && noCollisions && !hasNears) {
- query.$or.forEach(subquery => {
- subquery[key] = query[key];
- });
- delete query[key];
- }
- });
- query.$or.forEach(el =>
- validateQuery(el, skipMongoDBServer13732Workaround)
- );
- }
+ query.$or.forEach(validateQuery);
} else {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
@@ -146,9 +87,7 @@ const validateQuery = (
if (query.$and) {
if (query.$and instanceof Array) {
- query.$and.forEach(el =>
- validateQuery(el, skipMongoDBServer13732Workaround)
- );
+ query.$and.forEach(validateQuery);
} else {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
@@ -159,9 +98,7 @@ const validateQuery = (
if (query.$nor) {
if (query.$nor instanceof Array && query.$nor.length > 0) {
- query.$nor.forEach(el =>
- validateQuery(el, skipMongoDBServer13732Workaround)
- );
+ query.$nor.forEach(validateQuery);
} else {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
@@ -217,7 +154,7 @@ const filterSensitiveData = (
return { key: key.substring(10), value: perms.protectedFields[key] };
});
- const newProtectedFields: Array = [];
+ const newProtectedFields: Array[] = [];
let overrideProtectedFields = false;
// check if the object grants the current user access based on the extracted fields
@@ -238,12 +175,28 @@ const filterSensitiveData = (
if (pointerPermIncludesUser) {
overrideProtectedFields = true;
- newProtectedFields.push(...pointerPerm.value);
+ newProtectedFields.push(pointerPerm.value);
}
});
- // if atleast one pointer-permission affected the current user override the protectedFields
- if (overrideProtectedFields) protectedFields = newProtectedFields;
+ // if at least one pointer-permission affected the current user
+ // intersect vs protectedFields from previous stage (@see addProtectedFields)
+ // Sets theory (intersections): A x (B x C) == (A x B) x C
+ if (overrideProtectedFields && protectedFields) {
+ newProtectedFields.push(protectedFields);
+ }
+ // intersect all sets of protectedFields
+ newProtectedFields.forEach(fields => {
+ if (fields) {
+ // if there're no protctedFields by other criteria ( id / role / auth)
+ // then we must intersect each set (per userField)
+ if (!protectedFields) {
+ protectedFields = fields;
+ } else {
+ protectedFields = protectedFields.filter(v => fields.includes(v));
+ }
+ }
+ });
}
}
@@ -251,9 +204,16 @@ const filterSensitiveData = (
/* special treat for the user class: don't filter protectedFields if currently loggedin user is
the retrieved user */
- if (!(isUserClass && userId && object.objectId === userId))
+ if (!(isUserClass && userId && object.objectId === userId)) {
protectedFields && protectedFields.forEach(k => delete object[k]);
+ // fields not requested by client (excluded),
+ //but were needed to apply protecttedFields
+ perms.protectedFields &&
+ perms.protectedFields.temporaryKeys &&
+ perms.protectedFields.temporaryKeys.forEach(k => delete object[k]);
+ }
+
if (!isUserClass) {
return object;
}
@@ -464,21 +424,15 @@ class DatabaseController {
adapter: StorageAdapter;
schemaCache: any;
schemaPromise: ?Promise;
- skipMongoDBServer13732Workaround: boolean;
_transactionalSession: ?any;
- constructor(
- adapter: StorageAdapter,
- schemaCache: any,
- skipMongoDBServer13732Workaround: boolean
- ) {
+ constructor(adapter: StorageAdapter, schemaCache: any) {
this.adapter = adapter;
this.schemaCache = schemaCache;
// We don't want a mutable this.schema, because then you could have
// one request that uses different schemas for different parts of
// it. Instead, use loadSchema to get a schema.
this.schemaPromise = null;
- this.skipMongoDBServer13732Workaround = skipMongoDBServer13732Workaround;
this._transactionalSession = null;
}
@@ -637,7 +591,7 @@ class DatabaseController {
if (acl) {
query = addWriteACL(query, acl);
}
- validateQuery(query, this.skipMongoDBServer13732Workaround);
+ validateQuery(query);
return schemaController
.getOneSchema(className, true)
.catch(error => {
@@ -916,7 +870,7 @@ class DatabaseController {
if (acl) {
query = addWriteACL(query, acl);
}
- validateQuery(query, this.skipMongoDBServer13732Workaround);
+ validateQuery(query);
return schemaController
.getOneSchema(className)
.catch(error => {
@@ -1416,7 +1370,8 @@ class DatabaseController {
className,
query,
aclGroup,
- auth
+ auth,
+ queryOptions
);
}
if (!query) {
@@ -1436,7 +1391,7 @@ class DatabaseController {
query = addReadACL(query, aclGroup);
}
}
- validateQuery(query, this.skipMongoDBServer13732Workaround);
+ validateQuery(query);
if (count) {
if (!classExists) {
return 0;
@@ -1638,7 +1593,8 @@ class DatabaseController {
className: string,
query: any = {},
aclGroup: any[] = [],
- auth: any = {}
+ auth: any = {},
+ queryOptions: FullQueryOptions = {}
): null | string[] {
const perms = schema.getClassLevelPermissions(className);
if (!perms) return null;
@@ -1648,14 +1604,85 @@ class DatabaseController {
if (aclGroup.indexOf(query.objectId) > -1) return null;
- // remove userField keys since they are filtered after querying
- let protectedKeys = Object.keys(protectedFields).reduce((acc, val) => {
- if (val.startsWith('userField:')) return acc;
- return acc.concat(protectedFields[val]);
+ // for queries where "keys" are set and do not include all 'userField':{field},
+ // we have to transparently include it, and then remove before returning to client
+ // Because if such key not projected the permission won't be enforced properly
+ // PS this is called when 'excludeKeys' already reduced to 'keys'
+ const preserveKeys = queryOptions.keys;
+
+ // these are keys that need to be included only
+ // to be able to apply protectedFields by pointer
+ // and then unset before returning to client (later in filterSensitiveFields)
+ const serverOnlyKeys = [];
+
+ const authenticated = auth.user;
+
+ // map to allow check without array search
+ const roles = (auth.userRoles || []).reduce((acc, r) => {
+ acc[r] = protectedFields[r];
+ return acc;
+ }, {});
+
+ // array of sets of protected fields. separate item for each applicable criteria
+ const protectedKeysSets = [];
+
+ for (const key in protectedFields) {
+ // skip userFields
+ if (key.startsWith('userField:')) {
+ if (preserveKeys) {
+ const fieldName = key.substring(10);
+ if (!preserveKeys.includes(fieldName)) {
+ // 1. put it there temporarily
+ queryOptions.keys && queryOptions.keys.push(fieldName);
+ // 2. preserve it delete later
+ serverOnlyKeys.push(fieldName);
+ }
+ }
+ continue;
+ }
+
+ // add public tier
+ if (key === '*') {
+ protectedKeysSets.push(protectedFields[key]);
+ continue;
+ }
+
+ if (authenticated) {
+ if (key === 'authenticated') {
+ // for logged in users
+ protectedKeysSets.push(protectedFields[key]);
+ continue;
+ }
+
+ if (roles[key] && key.startsWith('role:')) {
+ // add applicable roles
+ protectedKeysSets.push(roles[key]);
+ }
+ }
+ }
+
+ // check if there's a rule for current user's id
+ if (authenticated) {
+ const userId = auth.user.id;
+ if (perms.protectedFields[userId]) {
+ protectedKeysSets.push(perms.protectedFields[userId]);
+ }
+ }
+
+ // preserve fields to be removed before sending response to client
+ if (serverOnlyKeys.length > 0) {
+ perms.protectedFields.temporaryKeys = serverOnlyKeys;
+ }
+
+ let protectedKeys = protectedKeysSets.reduce((acc, next) => {
+ if (next) {
+ acc.push(...next);
+ }
+ return acc;
}, []);
- [...(auth.userRoles || [])].forEach(role => {
- const fields = protectedFields[role];
+ // intersect all sets of protectedFields
+ protectedKeysSets.forEach(fields => {
if (fields) {
protectedKeys = protectedKeys.filter(v => fields.includes(v));
}
@@ -1797,7 +1824,7 @@ class DatabaseController {
]);
}
- static _validateQuery: (any, boolean) => void;
+ static _validateQuery: any => void;
}
module.exports = DatabaseController;
diff --git a/src/Controllers/SchemaController.js b/src/Controllers/SchemaController.js
index 8c799d0533..435a4b5570 100644
--- a/src/Controllers/SchemaController.js
+++ b/src/Controllers/SchemaController.js
@@ -175,32 +175,62 @@ const volatileClasses = Object.freeze([
// Anything that start with role
const roleRegex = /^role:.*/;
-// Anything that starts with userField
-const pointerPermissionRegex = /^userField:.*/;
+// Anything that starts with userField (allowed for protected fields only)
+const protectedFieldsPointerRegex = /^userField:.*/;
// * permission
const publicRegex = /^\*$/;
-const requireAuthenticationRegex = /^requiresAuthentication$/;
+const authenticatedRegex = /^authenticated$/;
-const pointerFieldsRegex = /^pointerFields$/;
+const requiresAuthenticationRegex = /^requiresAuthentication$/;
-const permissionKeyRegex = Object.freeze([
+const clpPointerRegex = /^pointerFields$/;
+
+// regex for validating entities in protectedFields object
+const protectedFieldsRegex = Object.freeze([
+ protectedFieldsPointerRegex,
+ publicRegex,
+ authenticatedRegex,
roleRegex,
- pointerPermissionRegex,
+]);
+
+// clp regex
+const clpFieldsRegex = Object.freeze([
+ clpPointerRegex,
publicRegex,
- requireAuthenticationRegex,
- pointerFieldsRegex,
+ requiresAuthenticationRegex,
+ roleRegex,
]);
function validatePermissionKey(key, userIdRegExp) {
let matchesSome = false;
- for (const regEx of permissionKeyRegex) {
+ for (const regEx of clpFieldsRegex) {
if (key.match(regEx) !== null) {
matchesSome = true;
break;
}
}
+ // userId depends on startup options so it's dynamic
+ const valid = matchesSome || key.match(userIdRegExp) !== null;
+ if (!valid) {
+ throw new Parse.Error(
+ Parse.Error.INVALID_JSON,
+ `'${key}' is not a valid key for class level permissions`
+ );
+ }
+}
+
+function validateProtectedFieldsKey(key, userIdRegExp) {
+ let matchesSome = false;
+ for (const regEx of protectedFieldsRegex) {
+ if (key.match(regEx) !== null) {
+ matchesSome = true;
+ break;
+ }
+ }
+
+ // userId regex depends on launch options so it's dynamic
const valid = matchesSome || key.match(userIdRegExp) !== null;
if (!valid) {
throw new Parse.Error(
@@ -264,7 +294,7 @@ function validateCLP(
if (operationKey === 'protectedFields') {
for (const entity in operation) {
// throws on unexpected key
- validatePermissionKey(entity, userIdRegExp);
+ validateProtectedFieldsKey(entity, userIdRegExp);
const protectedFields = operation[entity];
@@ -277,6 +307,13 @@ function validateCLP(
// if the field is in form of array
for (const field of protectedFields) {
+ // do not alloow to protect default fields
+ if (defaultColumns._Default[field]) {
+ throw new Parse.Error(
+ Parse.Error.INVALID_JSON,
+ `Default field '${field}' can not be protected`
+ );
+ }
// field should exist on collection
if (!Object.prototype.hasOwnProperty.call(fields, field)) {
throw new Parse.Error(
@@ -301,6 +338,8 @@ function validateCLP(
// throws on unexpected key
validatePermissionKey(entity, userIdRegExp);
+ // entity can be either:
+ // "pointerFields": string[]
if (entity === 'pointerFields') {
const pointerFields = operation[entity];
@@ -311,13 +350,14 @@ function validateCLP(
} else {
throw new Parse.Error(
Parse.Error.INVALID_JSON,
- `'${pointerFields}' is not a valid value for protectedFields[${entity}] - expected an array.`
+ `'${pointerFields}' is not a valid value for ${operationKey}[${entity}] - expected an array.`
);
}
// proceed with next entity key
continue;
}
+ // or [entity]: boolean
const permit = operation[entity];
if (permit !== true) {
diff --git a/src/Controllers/index.js b/src/Controllers/index.js
index 8ef767933f..d10ad8001c 100644
--- a/src/Controllers/index.js
+++ b/src/Controllers/index.js
@@ -171,7 +171,6 @@ export function getDatabaseController(
const {
databaseURI,
databaseOptions,
- skipMongoDBServer13732Workaround,
collectionPrefix,
schemaCacheTTL,
enableSingleSchemaCache,
@@ -195,8 +194,7 @@ export function getDatabaseController(
}
return new DatabaseController(
databaseAdapter,
- new SchemaCache(cacheController, schemaCacheTTL, enableSingleSchemaCache),
- skipMongoDBServer13732Workaround
+ new SchemaCache(cacheController, schemaCacheTTL, enableSingleSchemaCache)
);
}
diff --git a/src/GraphQL/ParseGraphQLSchema.js b/src/GraphQL/ParseGraphQLSchema.js
index d01f5eee0a..d596eae5b7 100644
--- a/src/GraphQL/ParseGraphQLSchema.js
+++ b/src/GraphQL/ParseGraphQLSchema.js
@@ -197,19 +197,60 @@ class ParseGraphQLSchema {
if (this.graphQLCustomTypeDefs) {
schemaDirectives.load(this);
- this.graphQLSchema = mergeSchemas({
- schemas: [
- this.graphQLSchemaDirectivesDefinitions,
- this.graphQLAutoSchema,
- this.graphQLCustomTypeDefs,
- ],
- mergeDirectives: true,
- });
+ if (typeof this.graphQLCustomTypeDefs.getTypeMap === 'function') {
+ const customGraphQLSchemaTypeMap = this.graphQLCustomTypeDefs.getTypeMap();
+ Object.values(customGraphQLSchemaTypeMap).forEach(
+ customGraphQLSchemaType => {
+ if (
+ !customGraphQLSchemaType ||
+ !customGraphQLSchemaType.name ||
+ customGraphQLSchemaType.name.startsWith('__')
+ ) {
+ return;
+ }
+ const autoGraphQLSchemaType = this.graphQLAutoSchema.getType(
+ customGraphQLSchemaType.name
+ );
+ if (autoGraphQLSchemaType) {
+ autoGraphQLSchemaType._fields = {
+ ...autoGraphQLSchemaType._fields,
+ ...customGraphQLSchemaType._fields,
+ };
+ }
+ }
+ );
+ this.graphQLSchema = mergeSchemas({
+ schemas: [
+ this.graphQLSchemaDirectivesDefinitions,
+ this.graphQLCustomTypeDefs,
+ this.graphQLAutoSchema,
+ ],
+ mergeDirectives: true,
+ });
+ } else if (typeof this.graphQLCustomTypeDefs === 'function') {
+ this.graphQLSchema = await this.graphQLCustomTypeDefs({
+ directivesDefinitionsSchema: this.graphQLSchemaDirectivesDefinitions,
+ autoSchema: this.graphQLAutoSchema,
+ mergeSchemas,
+ });
+ } else {
+ this.graphQLSchema = mergeSchemas({
+ schemas: [
+ this.graphQLSchemaDirectivesDefinitions,
+ this.graphQLAutoSchema,
+ this.graphQLCustomTypeDefs,
+ ],
+ mergeDirectives: true,
+ });
+ }
const graphQLSchemaTypeMap = this.graphQLSchema.getTypeMap();
Object.keys(graphQLSchemaTypeMap).forEach(graphQLSchemaTypeName => {
const graphQLSchemaType = graphQLSchemaTypeMap[graphQLSchemaTypeName];
- if (typeof graphQLSchemaType.getFields === 'function') {
+ if (
+ typeof graphQLSchemaType.getFields === 'function' &&
+ this.graphQLCustomTypeDefs.definitions
+ ) {
const graphQLCustomTypeDef = this.graphQLCustomTypeDefs.definitions.find(
definition => definition.name.value === graphQLSchemaTypeName
);
diff --git a/src/GraphQL/helpers/objectsQueries.js b/src/GraphQL/helpers/objectsQueries.js
index 3e918c98b6..c1237deaea 100644
--- a/src/GraphQL/helpers/objectsQueries.js
+++ b/src/GraphQL/helpers/objectsQueries.js
@@ -3,6 +3,11 @@ import { offsetToCursor, cursorToOffset } from 'graphql-relay';
import rest from '../../rest';
import { transformQueryInputToParse } from '../transformers/query';
+const needToGetAllKeys = (fields, keys) =>
+ keys
+ ? !!keys.split(',').find(keyName => !fields[keyName.split('.')[0]])
+ : true;
+
const getObject = async (
className,
objectId,
@@ -12,10 +17,11 @@ const getObject = async (
includeReadPreference,
config,
auth,
- info
+ info,
+ parseClass
) => {
const options = {};
- if (keys) {
+ if (!needToGetAllKeys(parseClass.fields, keys)) {
options.keys = keys;
}
if (include) {
@@ -133,7 +139,14 @@ const findObjects = async (
// Silently replace the limit on the query with the max configured
options.limit = config.maxLimit;
}
- if (keys) {
+ if (
+ !needToGetAllKeys(
+ parseClasses.find(
+ ({ className: parseClassName }) => className === parseClassName
+ ).fields,
+ keys
+ )
+ ) {
options.keys = keys;
}
if (includeAll === true) {
@@ -313,4 +326,4 @@ const calculateSkipAndLimit = (
};
};
-export { getObject, findObjects, calculateSkipAndLimit };
+export { getObject, findObjects, calculateSkipAndLimit, needToGetAllKeys };
diff --git a/src/GraphQL/loaders/defaultRelaySchema.js b/src/GraphQL/loaders/defaultRelaySchema.js
index 3837bd5b9f..c3c2d9ccce 100644
--- a/src/GraphQL/loaders/defaultRelaySchema.js
+++ b/src/GraphQL/loaders/defaultRelaySchema.js
@@ -30,7 +30,10 @@ const load = parseGraphQLSchema => {
undefined,
config,
auth,
- info
+ info,
+ parseGraphQLSchema.parseClasses.find(
+ ({ className }) => type === className
+ )
)),
};
} catch (e) {
diff --git a/src/GraphQL/loaders/parseClassMutations.js b/src/GraphQL/loaders/parseClassMutations.js
index 3ca333a0db..f41cccf5ed 100644
--- a/src/GraphQL/loaders/parseClassMutations.js
+++ b/src/GraphQL/loaders/parseClassMutations.js
@@ -112,8 +112,12 @@ const load = function(
include,
['id', 'objectId', 'createdAt', 'updatedAt']
);
+ const needToGetAllKeys = objectsQueries.needToGetAllKeys(
+ parseClass.fields,
+ keys
+ );
let optimizedObject = {};
- if (needGet) {
+ if (needGet && !needToGetAllKeys) {
optimizedObject = await objectsQueries.getObject(
className,
createdObject.objectId,
@@ -123,7 +127,21 @@ const load = function(
undefined,
config,
auth,
- info
+ info,
+ parseClass
+ );
+ } else if (needToGetAllKeys) {
+ optimizedObject = await objectsQueries.getObject(
+ className,
+ createdObject.objectId,
+ undefined,
+ include,
+ undefined,
+ undefined,
+ config,
+ auth,
+ info,
+ parseClass
);
}
return {
@@ -212,9 +230,12 @@ const load = function(
include,
['id', 'objectId', 'updatedAt']
);
-
+ const needToGetAllKeys = objectsQueries.needToGetAllKeys(
+ parseClass.fields,
+ keys
+ );
let optimizedObject = {};
- if (needGet) {
+ if (needGet && !needToGetAllKeys) {
optimizedObject = await objectsQueries.getObject(
className,
id,
@@ -224,7 +245,21 @@ const load = function(
undefined,
config,
auth,
- info
+ info,
+ parseClass
+ );
+ } else if (needToGetAllKeys) {
+ optimizedObject = await objectsQueries.getObject(
+ className,
+ id,
+ undefined,
+ include,
+ undefined,
+ undefined,
+ config,
+ auth,
+ info,
+ parseClass
);
}
return {
@@ -301,7 +336,8 @@ const load = function(
undefined,
config,
auth,
- info
+ info,
+ parseClass
);
}
await objectsMutations.deleteObject(
diff --git a/src/GraphQL/loaders/parseClassQueries.js b/src/GraphQL/loaders/parseClassQueries.js
index 80667836d0..cd1e71d922 100644
--- a/src/GraphQL/loaders/parseClassQueries.js
+++ b/src/GraphQL/loaders/parseClassQueries.js
@@ -14,7 +14,7 @@ const getParseClassQueryConfig = function(
return (parseClassConfig && parseClassConfig.query) || {};
};
-const getQuery = async (className, _source, args, context, queryInfo) => {
+const getQuery = async (parseClass, _source, args, context, queryInfo) => {
let { id } = args;
const { options } = args;
const { readPreference, includeReadPreference } = options || {};
@@ -23,14 +23,14 @@ const getQuery = async (className, _source, args, context, queryInfo) => {
const globalIdObject = fromGlobalId(id);
- if (globalIdObject.type === className) {
+ if (globalIdObject.type === parseClass.className) {
id = globalIdObject.id;
}
const { keys, include } = extractKeysAndInclude(selectedFields);
return await objectsQueries.getObject(
- className,
+ parseClass.className,
id,
keys,
include,
@@ -38,7 +38,8 @@ const getQuery = async (className, _source, args, context, queryInfo) => {
includeReadPreference,
config,
auth,
- info
+ info,
+ parseClass
);
};
@@ -79,7 +80,7 @@ const load = function(
),
async resolve(_source, args, context, queryInfo) {
try {
- return await getQuery(className, _source, args, context, queryInfo);
+ return await getQuery(parseClass, _source, args, context, queryInfo);
} catch (e) {
parseGraphQLSchema.handleError(e);
}
diff --git a/src/GraphQL/loaders/parseClassTypes.js b/src/GraphQL/loaders/parseClassTypes.js
index 4909c3e992..b0272c4de6 100644
--- a/src/GraphQL/loaders/parseClassTypes.js
+++ b/src/GraphQL/loaders/parseClassTypes.js
@@ -436,7 +436,7 @@ const load = (
);
const parseOrder = order && order.join(',');
- return await objectsQueries.findObjects(
+ return objectsQueries.findObjects(
source[field].className,
{
$relatedTo: {
diff --git a/src/GraphQL/transformers/mutation.js b/src/GraphQL/transformers/mutation.js
index 6d5dbc601d..d2f3c8b7b5 100644
--- a/src/GraphQL/transformers/mutation.js
+++ b/src/GraphQL/transformers/mutation.js
@@ -122,7 +122,7 @@ const transformers = {
parseGraphQLSchema,
{ config, auth, info }
) => {
- if (Object.keys(value) === 0)
+ if (Object.keys(value).length === 0)
throw new Parse.Error(
Parse.Error.INVALID_POINTER,
`You need to provide at least one operation on the relation mutation of field ${field}`
@@ -203,7 +203,7 @@ const transformers = {
parseGraphQLSchema,
{ config, auth, info }
) => {
- if (Object.keys(value) > 1 || Object.keys(value) === 0)
+ if (Object.keys(value).length > 1 || Object.keys(value).length === 0)
throw new Parse.Error(
Parse.Error.INVALID_POINTER,
`You need to provide link OR createLink on the pointer mutation of field ${field}`
diff --git a/src/LiveQuery/ParseWebSocketServer.js b/src/LiveQuery/ParseWebSocketServer.js
index 9e0d18d2a1..606056fc2e 100644
--- a/src/LiveQuery/ParseWebSocketServer.js
+++ b/src/LiveQuery/ParseWebSocketServer.js
@@ -13,6 +13,10 @@ export class ParseWebSocketServer {
logger.info('Parse LiveQuery Server starts running');
};
wss.onConnection = ws => {
+ ws.on('error', error => {
+ logger.error(error.message);
+ logger.error(JSON.stringify(ws));
+ });
onConnect(new ParseWebSocket(ws));
// Send ping to client periodically
const pingIntervalId = setInterval(() => {
diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js
index 4bded75ab9..725002034c 100644
--- a/src/Options/Definitions.js
+++ b/src/Options/Definitions.js
@@ -371,12 +371,6 @@ module.exports.ParseServerOptions = {
help: 'Disables console output',
action: parsers.booleanParser,
},
- skipMongoDBServer13732Workaround: {
- env: 'PARSE_SKIP_MONGODB_SERVER_13732_WORKAROUND',
- help: 'Circumvent Parse workaround for historical MongoDB bug SERVER-13732',
- action: parsers.booleanParser,
- default: false,
- },
startLiveQueryServer: {
env: 'PARSE_SERVER_START_LIVE_QUERY_SERVER',
help: 'Starts the liveQuery server',
diff --git a/src/Options/docs.js b/src/Options/docs.js
index 098fefb563..9e5b6ac8f8 100644
--- a/src/Options/docs.js
+++ b/src/Options/docs.js
@@ -67,7 +67,6 @@
* @property {String} serverURL URL to your parse server with http:// or https://.
* @property {Number} sessionLength Session duration, in seconds, defaults to 1 year
* @property {Boolean} silent Disables console output
- * @property {Boolean} skipMongoDBServer13732Workaround Circumvent Parse workaround for historical MongoDB bug SERVER-13732
* @property {Boolean} startLiveQueryServer Starts the liveQuery server
* @property {String[]} userSensitiveFields Personally identifiable information fields in the user table the should be removed for non-authorized users. Deprecated @see protectedFields
* @property {Boolean} verbose Set the logging to verbose
diff --git a/src/Options/index.js b/src/Options/index.js
index 43922ed36c..3aa5b7fc6b 100644
--- a/src/Options/index.js
+++ b/src/Options/index.js
@@ -64,10 +64,6 @@ export interface ParseServerOptions {
databaseOptions: ?any;
/* Adapter module for the database */
databaseAdapter: ?Adapter;
- /* Circumvent Parse workaround for historical MongoDB bug SERVER-13732
- :ENV: PARSE_SKIP_MONGODB_SERVER_13732_WORKAROUND
- :DEFAULT: false */
- skipMongoDBServer13732Workaround: ?boolean;
/* Full path to your cloud code main.js */
cloud: ?string;
/* A collection prefix for the classes
diff --git a/src/ParseServer.js b/src/ParseServer.js
index 5611c50491..1fd279b4c7 100644
--- a/src/ParseServer.js
+++ b/src/ParseServer.js
@@ -262,10 +262,12 @@ class ParseServer {
if (options.mountGraphQL === true || options.mountPlayground === true) {
let graphQLCustomTypeDefs = undefined;
- if (options.graphQLSchema) {
+ if (typeof options.graphQLSchema === 'string') {
graphQLCustomTypeDefs = parse(
fs.readFileSync(options.graphQLSchema, 'utf8')
);
+ } else if (typeof options.graphQLSchema === 'object') {
+ graphQLCustomTypeDefs = options.graphQLSchema;
}
const parseGraphQLServer = new ParseGraphQLServer(this, {
diff --git a/src/Routers/ClassesRouter.js b/src/Routers/ClassesRouter.js
index 0cbc8d215d..d85101af6e 100644
--- a/src/Routers/ClassesRouter.js
+++ b/src/Routers/ClassesRouter.js
@@ -6,6 +6,7 @@ import Parse from 'parse/node';
const ALLOWED_GET_QUERY_KEYS = [
'keys',
'include',
+ 'excludeKeys',
'readPreference',
'includeReadPreference',
'subqueryReadPreference',
@@ -69,6 +70,9 @@ export class ClassesRouter extends PromiseRouter {
if (body.include) {
options.include = String(body.include);
}
+ if (typeof body.excludeKeys == 'string') {
+ options.excludeKeys = body.excludeKeys;
+ }
if (typeof body.readPreference === 'string') {
options.readPreference = body.readPreference;
}
diff --git a/src/Routers/PublicAPIRouter.js b/src/Routers/PublicAPIRouter.js
index f30c52892f..7453835473 100644
--- a/src/Routers/PublicAPIRouter.js
+++ b/src/Routers/PublicAPIRouter.js
@@ -11,7 +11,10 @@ const views = path.resolve(__dirname, '../../views');
export class PublicAPIRouter extends PromiseRouter {
verifyEmail(req) {
- const { token, username } = req.query;
+ const { username, token: rawToken } = req.query;
+ const token =
+ rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
+
const appId = req.params.appId;
const config = Config.get(appId);
@@ -122,7 +125,9 @@ export class PublicAPIRouter extends PromiseRouter {
return this.missingPublicServerURL();
}
- const { username, token } = req.query;
+ const { username, token: rawToken } = req.query;
+ const token =
+ rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
if (!username || !token) {
return this.invalidLink(req);
@@ -158,7 +163,9 @@ export class PublicAPIRouter extends PromiseRouter {
return this.missingPublicServerURL();
}
- const { username, token, new_password } = req.body;
+ const { username, new_password, token: rawToken } = req.body;
+ const token =
+ rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
if ((!username || !token || !new_password) && req.xhr === false) {
return this.invalidLink(req);
diff --git a/src/middlewares.js b/src/middlewares.js
index 75923e713f..f60cb53df6 100644
--- a/src/middlewares.js
+++ b/src/middlewares.js
@@ -105,6 +105,10 @@ export function handleParseHeaders(req, res, next) {
}
}
+ if (info.sessionToken && typeof info.sessionToken !== 'string') {
+ info.sessionToken = info.sessionToken.toString();
+ }
+
if (info.clientVersion) {
info.clientSDK = ClientSDK.fromString(info.clientVersion);
}