From 768fe3b6ce0a335e7a732570cbef3d12a4ca89be Mon Sep 17 00:00:00 2001 From: Wes Thomas Date: Fri, 21 Jul 2023 22:22:48 -0700 Subject: [PATCH 01/12] feat: allow setting createdAt during object creation --- spec/rest.spec.js | 14 ++++++++++++++ src/RestWrite.js | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/spec/rest.spec.js b/spec/rest.spec.js index 02d2f5960b..c51817433a 100644 --- a/spec/rest.spec.js +++ b/spec/rest.spec.js @@ -98,6 +98,20 @@ describe('rest create', () => { expect(objectId).toBeDefined(); }); + it('should allow createdAt to be specified', async () => { + const obj = { + createdAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, + }; + + const { + status, + response: { createdAt }, + } = await rest.create(config, auth.nobody(config), 'MyClass', obj); + + expect(status).toEqual(201); + expect(createdAt).toEqual(obj.createdAt.iso); + }); + it('is backwards compatible when _id size changes', done => { rest .create(config, auth.nobody(config), 'Foo', { size: 10 }) diff --git a/src/RestWrite.js b/src/RestWrite.js index d35e52d6b5..c27c236426 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -370,7 +370,12 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { // Add default fields this.data.updatedAt = this.updatedAt; if (!this.query) { - this.data.createdAt = this.updatedAt; + // optionally support specifying createdAt during object creation + if (this.data.createdAt && this.data.createdAt.__type === 'Date') { + this.data.createdAt = this.data.updatedAt = this.data.createdAt.iso; + } else { + this.data.createdAt = this.updatedAt; + } // Only assign new objectId if we are creating new object if (!this.data.objectId) { From d14c505755b0ce1ed385b4db93a55e0dbc2aad68 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Sun, 23 Jul 2023 01:08:04 +0200 Subject: [PATCH 02/12] Update spec/rest.spec.js Signed-off-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- spec/rest.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/rest.spec.js b/spec/rest.spec.js index c51817433a..08001e55ca 100644 --- a/spec/rest.spec.js +++ b/spec/rest.spec.js @@ -98,7 +98,7 @@ describe('rest create', () => { expect(objectId).toBeDefined(); }); - it('should allow createdAt to be specified', async () => { + it('allows createdAt to be set', async () => { const obj = { createdAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, }; From b1dae902f9a1f999c39f0d90a4851aa05364ac05 Mon Sep 17 00:00:00 2001 From: Wes Thomas Date: Sat, 5 Aug 2023 10:28:05 -0700 Subject: [PATCH 03/12] allow setting updated at optionally with createdAt --- src/RestWrite.js | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/RestWrite.js b/src/RestWrite.js index c27c236426..b3e7b33dad 100644 --- a/src/RestWrite.js +++ b/src/RestWrite.js @@ -368,12 +368,34 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { }; // Add default fields - this.data.updatedAt = this.updatedAt; if (!this.query) { - // optionally support specifying createdAt during object creation - if (this.data.createdAt && this.data.createdAt.__type === 'Date') { - this.data.createdAt = this.data.updatedAt = this.data.createdAt.iso; + // allow customizing createdAt and updatedAt when using maintenance key + if ( + this.auth.isMaintenance && + this.data.createdAt && + this.data.createdAt.__type === 'Date' + ) { + this.data.createdAt = this.data.createdAt.iso; + + if (this.data.updatedAt && this.data.updatedAt.__type === 'Date') { + const createdAt = new Date(this.data.createdAt); + const updatedAt = new Date(this.data.updatedAt.iso); + + if (updatedAt < createdAt) { + throw new Parse.Error( + Parse.Error.VALIDATION_ERROR, + 'updatedAt cannot occur before createdAt' + ); + } + + this.data.updatedAt = this.data.updatedAt.iso; + } + // if no updatedAt is provided, set it to createdAt to match default behavior + else { + this.data.updatedAt = this.data.createdAt; + } } else { + this.data.updatedAt = this.updatedAt; this.data.createdAt = this.updatedAt; } @@ -387,6 +409,8 @@ RestWrite.prototype.setRequiredFieldsIfNeeded = function () { }); } } else if (schema) { + this.data.updatedAt = this.updatedAt; + Object.keys(this.data).forEach(fieldName => { setRequiredFieldIfNeeded(fieldName, false); }); From 186581dd9db6901bcd4a44d8667210dffba40024 Mon Sep 17 00:00:00 2001 From: Wes Thomas Date: Sat, 5 Aug 2023 10:28:40 -0700 Subject: [PATCH 04/12] add tests for editing createdAt / updatedAt with maintenance key --- spec/rest.spec.js | 51 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/spec/rest.spec.js b/spec/rest.spec.js index 08001e55ca..c315f388e4 100644 --- a/spec/rest.spec.js +++ b/spec/rest.spec.js @@ -98,18 +98,55 @@ describe('rest create', () => { expect(objectId).toBeDefined(); }); - it('allows createdAt to be set', async () => { + it('allows createdAt and updatedAt to be set with maintenance key', async () => { + let obj = { + createdAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, + }; + + const res = await rest.create(config, auth.maintenance(config), 'MyClass', obj); + + expect(res.status).toEqual(201); + expect(res.response.createdAt).toEqual(obj.createdAt.iso); + + obj = { + createdAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, + updatedAt: { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }, + }; + + const res2 = await rest.create(config, auth.maintenance(config), 'MyClass', obj); + + expect(res2.status).toEqual(201); + expect(res2.response.createdAt).toEqual(obj.createdAt.iso); + + const res3 = await rest.get(config, auth.nobody(config), 'MyClass', res2.response.objectId); + expect(res3.results[0].updatedAt).toEqual(obj.updatedAt.iso); + }); + + it('cannot set updatedAt dated before createdAt with maintenance key', async () => { const obj = { createdAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, + updatedAt: { __type: 'Date', iso: '2001-01-01T00:00:00.000Z' }, }; - const { - status, - response: { createdAt }, - } = await rest.create(config, auth.nobody(config), 'MyClass', obj); + try { + await rest.create(config, auth.maintenance(config), 'MyClass', obj); + fail(); + } catch (err) { + expect(err.code).toEqual(Parse.Error.VALIDATION_ERROR); + } + }); - expect(status).toEqual(201); - expect(createdAt).toEqual(obj.createdAt.iso); + it('cannot set updatedAt without createdAt with maintenance key', async () => { + const obj = { + updatedAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, + }; + + const res = await rest.create(config, auth.maintenance(config), 'MyClass', obj); + const obj_id = res.response.objectId; + + const res2 = await rest.get(config, auth.nobody(config), 'MyClass', obj_id); + + expect(res2.results[0].updatedAt).not.toEqual(obj.updatedAt.iso); }); it('is backwards compatible when _id size changes', done => { From d25102a7a8fc261a85a3adade091ab504544b860 Mon Sep 17 00:00:00 2001 From: Wes Thomas Date: Sat, 5 Aug 2023 10:40:16 -0700 Subject: [PATCH 05/12] add Object Timestamps access scope classification to README --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0288d5f4ba..8ce3cdda38 100644 --- a/README.md +++ b/README.md @@ -358,12 +358,12 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo ## Access Scopes -| Scope | Internal data | Custom data | Restricted by CLP, ACL | Key | -|----------------|---------------|-------------|------------------------|---------------------| -| Internal | r/w | r/w | no | `maintenanceKey` | -| Master | -/- | r/w | no | `masterKey` | -| ReadOnlyMaster | -/- | r/- | no | `readOnlyMasterKey` | -| Session | -/- | r/w | yes | `sessionToken` | +| Scope | Internal data | Object timestamps | Custom data | Restricted by CLP, ACL | Key | +| -------------- | ------------- | ----------------- | ----------- | ---------------------- | ------------------- | +| Internal | r/w | r/w | r/w | no | `maintenanceKey` | +| Master | -/- | r/- | r/w | no | `masterKey` | +| ReadOnlyMaster | -/- | r/- | r/- | no | `readOnlyMasterKey` | +| Session | -/- | r/- | r/w | yes | `sessionToken` | ## Email Verification and Password Reset From 47022999a3a112b793dfa79ccbdb7149618d7a7c Mon Sep 17 00:00:00 2001 From: Wes Thomas Date: Sat, 5 Aug 2023 13:43:56 -0700 Subject: [PATCH 06/12] update docs to clarify additional `maintenanceKey` functionality --- src/Options/Definitions.js | 2 +- src/Options/docs.js | 2 +- src/Options/index.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index 7a1e56bad0..f434fdead8 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -317,7 +317,7 @@ module.exports.ParseServerOptions = { maintenanceKey: { env: 'PARSE_SERVER_MAINTENANCE_KEY', help: - '(Optional) The maintenance key is used for modifying internal fields of Parse Server.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.', + '(Optional) The maintenance key is used for modifying internal fields of Parse Server, including object timestamps.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.', required: true, }, maintenanceKeyIps: { diff --git a/src/Options/docs.js b/src/Options/docs.js index 09e6f5b3b4..a383be65ba 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -60,7 +60,7 @@ * @property {String} logLevel Sets the level for logs * @property {LogLevels} logLevels (Optional) Overrides the log levels used internally by Parse Server to log events. * @property {String} logsFolder Folder for the logs (defaults to './logs'); set to null to disable file based logging - * @property {String} maintenanceKey (Optional) The maintenance key is used for modifying internal fields of Parse Server.

⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. + * @property {String} maintenanceKey (Optional) The maintenance key is used for modifying internal fields of Parse Server, including object timestamps.

⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. * @property {String[]} maintenanceKeyIps (Optional) Restricts the use of maintenance key permissions to a list of IP addresses.

This option accepts a list of single IP addresses, for example:
`['10.0.0.1', '10.0.0.2']`

You can also use CIDR notation to specify an IP address range, for example:
`['10.0.1.0/24']`

Special cases:
- Setting an empty array `[]` means that `maintenanceKey` cannot be used even in Parse Server Cloud Code.
- Setting `['0.0.0.0/0']` means disabling the filter and the maintenance key can be used from any IP address.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the maintenance key. * @property {String} masterKey Your Parse Master Key * @property {String[]} masterKeyIps (Optional) Restricts the use of master key permissions to a list of IP addresses.

This option accepts a list of single IP addresses, for example:
`['10.0.0.1', '10.0.0.2']`

You can also use CIDR notation to specify an IP address range, for example:
`['10.0.1.0/24']`

Special cases:
- Setting an empty array `[]` means that `masterKey` cannot be used even in Parse Server Cloud Code.
- Setting `['0.0.0.0/0']` means disabling the filter and the master key can be used from any IP address.

To connect Parse Dashboard from a different server requires to add the IP address of the server that hosts Parse Dashboard because Parse Dashboard uses the master key.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the master key. diff --git a/src/Options/index.js b/src/Options/index.js index d501b996dd..af6f225099 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -47,7 +47,7 @@ export interface ParseServerOptions { appId: string; /* Your Parse Master Key */ masterKey: string; - /* (Optional) The maintenance key is used for modifying internal fields of Parse Server.

⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. */ + /* (Optional) The maintenance key is used for modifying internal fields of Parse Server, including object timestamps.

⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. */ maintenanceKey: string; /* URL to your parse server with http:// or https://. :ENV: PARSE_SERVER_URL */ From 26b2aaead6fd9094d87cc301c100daecb7c5a349 Mon Sep 17 00:00:00 2001 From: Wes Thomas Date: Sun, 6 Aug 2023 11:04:41 -0700 Subject: [PATCH 07/12] clarify access scope title --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8ce3cdda38..373ce3e34d 100644 --- a/README.md +++ b/README.md @@ -358,12 +358,12 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo ## Access Scopes -| Scope | Internal data | Object timestamps | Custom data | Restricted by CLP, ACL | Key | -| -------------- | ------------- | ----------------- | ----------- | ---------------------- | ------------------- | -| Internal | r/w | r/w | r/w | no | `maintenanceKey` | -| Master | -/- | r/- | r/w | no | `masterKey` | -| ReadOnlyMaster | -/- | r/- | r/- | no | `readOnlyMasterKey` | -| Session | -/- | r/- | r/w | yes | `sessionToken` | +| Scope | Internal data | Object timestamps | Custom data | Restricted by CLP, ACL | Key | +| ----------------------------- | ------------- | ----------------- | ----------- | ---------------------- | ------------------- | +| Internal and read-only fields | r/w | r/w | r/w | no | `maintenanceKey` | +| Master | -/- | r/- | r/w | no | `masterKey` | +| ReadOnlyMaster | -/- | r/- | r/- | no | `readOnlyMasterKey` | +| Session | -/- | r/- | r/w | yes | `sessionToken` | ## Email Verification and Password Reset From 201f0438a5145aff8362ab5586c4171762510965 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Sun, 6 Aug 2023 22:33:30 +0200 Subject: [PATCH 08/12] Update README.md Signed-off-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 373ce3e34d..d061e8c200 100644 --- a/README.md +++ b/README.md @@ -358,12 +358,14 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo ## Access Scopes -| Scope | Internal data | Object timestamps | Custom data | Restricted by CLP, ACL | Key | -| ----------------------------- | ------------- | ----------------- | ----------- | ---------------------- | ------------------- | -| Internal and read-only fields | r/w | r/w | r/w | no | `maintenanceKey` | -| Master | -/- | r/- | r/w | no | `masterKey` | -| ReadOnlyMaster | -/- | r/- | r/- | no | `readOnlyMasterKey` | -| Session | -/- | r/- | r/w | yes | `sessionToken` | +| Scope | Internal data | Read-only data (1) | Custom data | Restricted by CLP, ACL | Key | +|----------------|---------------|-------------------------------|-------------|------------------------|---------------------| +| Internal | r/w | r/w | r/w | no | `maintenanceKey` | +| Master | -/- | r/- | r/w | no | `masterKey` | +| ReadOnlyMaster | -/- | r/- | r/- | no | `readOnlyMasterKey` | +| Session | -/- | r/- | r/w | yes | `sessionToken` | + +(1) `Parse.Object.createdAt`, `Parse.Object.updatedAt`. ## Email Verification and Password Reset From 4eefa5213bca3acc5d0f7fc843ebeede56b0cfea Mon Sep 17 00:00:00 2001 From: Wes Thomas Date: Mon, 7 Aug 2023 22:55:08 -0700 Subject: [PATCH 09/12] expand rest maintenance spec --- spec/helper.js | 5 +- spec/rest.spec.js | 164 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 116 insertions(+), 53 deletions(-) diff --git a/spec/helper.js b/spec/helper.js index 445de26509..128a0401c5 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -103,6 +103,7 @@ const defaultConfiguration = { restAPIKey: 'rest', webhookKey: 'hook', masterKey: 'test', + maintenanceKey: 'testing', readOnlyMasterKey: 'read-only-test', fileKey: 'test', directAccess: true, @@ -250,8 +251,8 @@ afterEach(function (done) { }) .then(() => Parse.User.logOut()) .then( - () => {}, - () => {} + () => { }, + () => { } ) // swallow errors .then(() => { // Connection close events are not immediate on node 10+... wait a bit diff --git a/spec/rest.spec.js b/spec/rest.spec.js index c315f388e4..9d3c2ef843 100644 --- a/spec/rest.spec.js +++ b/spec/rest.spec.js @@ -98,57 +98,6 @@ describe('rest create', () => { expect(objectId).toBeDefined(); }); - it('allows createdAt and updatedAt to be set with maintenance key', async () => { - let obj = { - createdAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, - }; - - const res = await rest.create(config, auth.maintenance(config), 'MyClass', obj); - - expect(res.status).toEqual(201); - expect(res.response.createdAt).toEqual(obj.createdAt.iso); - - obj = { - createdAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, - updatedAt: { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }, - }; - - const res2 = await rest.create(config, auth.maintenance(config), 'MyClass', obj); - - expect(res2.status).toEqual(201); - expect(res2.response.createdAt).toEqual(obj.createdAt.iso); - - const res3 = await rest.get(config, auth.nobody(config), 'MyClass', res2.response.objectId); - expect(res3.results[0].updatedAt).toEqual(obj.updatedAt.iso); - }); - - it('cannot set updatedAt dated before createdAt with maintenance key', async () => { - const obj = { - createdAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, - updatedAt: { __type: 'Date', iso: '2001-01-01T00:00:00.000Z' }, - }; - - try { - await rest.create(config, auth.maintenance(config), 'MyClass', obj); - fail(); - } catch (err) { - expect(err.code).toEqual(Parse.Error.VALIDATION_ERROR); - } - }); - - it('cannot set updatedAt without createdAt with maintenance key', async () => { - const obj = { - updatedAt: { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }, - }; - - const res = await rest.create(config, auth.maintenance(config), 'MyClass', obj); - const obj_id = res.response.objectId; - - const res2 = await rest.get(config, auth.nobody(config), 'MyClass', obj_id); - - expect(res2.results[0].updatedAt).not.toEqual(obj.updatedAt.iso); - }); - it('is backwards compatible when _id size changes', done => { rest .create(config, auth.nobody(config), 'Foo', { size: 10 }) @@ -187,6 +136,119 @@ describe('rest create', () => { }); }); + describe('with maintenance key', () => { + let req; + + async function getObject(id) { + const res = await request({ + headers: { + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest' + }, + method: 'GET', + url: `http://localhost:8378/1/classes/TestObject/${id}` + }); + + return res.data; + } + + beforeEach(() => { + req = { + headers: { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest', + 'X-Parse-Maintenance-Key': 'testing' + }, + method: 'POST', + url: 'http://localhost:8378/1/classes/TestObject' + }; + }); + + it('allows createdAt', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + req.body = { createdAt }; + + const res = await request(req); + expect(res.data.createdAt).toEqual(createdAt.iso); + }); + + it('allows createdAt and updatedAt', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }; + req.body = { createdAt, updatedAt }; + + const res = await request(req); + + const obj = await getObject(res.data.objectId); + expect(obj.createdAt).toEqual(createdAt.iso); + expect(obj.updatedAt).toEqual(updatedAt.iso); + }); + + it('allows createdAt, updatedAt, and additional field', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }; + req.body = { createdAt, updatedAt, testing: 123 }; + + const res = await request(req); + + const obj = await getObject(res.data.objectId); + expect(obj.createdAt).toEqual(createdAt.iso); + expect(obj.updatedAt).toEqual(updatedAt.iso); + expect(obj.testing).toEqual(123); + }); + + it('cannot set updatedAt dated before createdAt', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + const updatedAt = { __type: 'Date', iso: '2018-12-01T00:00:00.000Z' }; + req.body = { createdAt, updatedAt }; + + try { + await request(req); + fail(); + } + catch (err) { + expect(err.data.code).toEqual(Parse.Error.VALIDATION_ERROR); + } + }); + + it('cannot set updatedAt without createdAt', async () => { + const updatedAt = { __type: 'Date', iso: '2018-12-01T00:00:00.000Z' }; + req.body = { updatedAt }; + + const res = await request(req); + + const obj = await getObject(res.data.objectId); + expect(obj.updatedAt).not.toEqual(updatedAt.iso); + }); + + it('handles bad types for createdAt and updatedAt', async () => { + const createdAt = 12345; + const updatedAt = true; + req.body = { createdAt, updatedAt }; + + try { + await request(req); + fail(); + } + catch (err) { + expect(err.data.code).toEqual(Parse.Error.INCORRECT_TYPE); + } + }); + + it('cannot set createdAt or updatedAt without maintenance key', async () => { + const createdAt = { __type: 'Date', iso: '2019-01-01T00:00:00.000Z' }; + const updatedAt = { __type: 'Date', iso: '2019-02-01T00:00:00.000Z' }; + req.body = { createdAt, updatedAt }; + delete req.headers['X-Parse-Maintenance-Key']; + + const res = await request(req); + + expect(res.data.createdAt).not.toEqual(createdAt.iso); + expect(res.data.updatedAt).not.toEqual(updatedAt.iso); + }); + }); + it('handles array, object, date', done => { const now = new Date(); const obj = { From 7c843af5b3234eccf5cc64e4ea96fa545ef8dd39 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:27:15 +0200 Subject: [PATCH 10/12] Update src/Options/Definitions.js Signed-off-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Options/Definitions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Options/Definitions.js b/src/Options/Definitions.js index f434fdead8..f35300b287 100644 --- a/src/Options/Definitions.js +++ b/src/Options/Definitions.js @@ -317,7 +317,7 @@ module.exports.ParseServerOptions = { maintenanceKey: { env: 'PARSE_SERVER_MAINTENANCE_KEY', help: - '(Optional) The maintenance key is used for modifying internal fields of Parse Server, including object timestamps.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.', + '(Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.

\u26A0\uFE0F This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server.', required: true, }, maintenanceKeyIps: { From 0b760adc00cf9637f27be6213d223fae0209a461 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:27:22 +0200 Subject: [PATCH 11/12] Update src/Options/docs.js Signed-off-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Options/docs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Options/docs.js b/src/Options/docs.js index a383be65ba..cf672cc6cb 100644 --- a/src/Options/docs.js +++ b/src/Options/docs.js @@ -60,7 +60,7 @@ * @property {String} logLevel Sets the level for logs * @property {LogLevels} logLevels (Optional) Overrides the log levels used internally by Parse Server to log events. * @property {String} logsFolder Folder for the logs (defaults to './logs'); set to null to disable file based logging - * @property {String} maintenanceKey (Optional) The maintenance key is used for modifying internal fields of Parse Server, including object timestamps.

⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. + * @property {String} maintenanceKey (Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.

⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. * @property {String[]} maintenanceKeyIps (Optional) Restricts the use of maintenance key permissions to a list of IP addresses.

This option accepts a list of single IP addresses, for example:
`['10.0.0.1', '10.0.0.2']`

You can also use CIDR notation to specify an IP address range, for example:
`['10.0.1.0/24']`

Special cases:
- Setting an empty array `[]` means that `maintenanceKey` cannot be used even in Parse Server Cloud Code.
- Setting `['0.0.0.0/0']` means disabling the filter and the maintenance key can be used from any IP address.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the maintenance key. * @property {String} masterKey Your Parse Master Key * @property {String[]} masterKeyIps (Optional) Restricts the use of master key permissions to a list of IP addresses.

This option accepts a list of single IP addresses, for example:
`['10.0.0.1', '10.0.0.2']`

You can also use CIDR notation to specify an IP address range, for example:
`['10.0.1.0/24']`

Special cases:
- Setting an empty array `[]` means that `masterKey` cannot be used even in Parse Server Cloud Code.
- Setting `['0.0.0.0/0']` means disabling the filter and the master key can be used from any IP address.

To connect Parse Dashboard from a different server requires to add the IP address of the server that hosts Parse Dashboard because Parse Dashboard uses the master key.

Defaults to `['127.0.0.1', '::1']` which means that only `localhost`, the server itself, is allowed to use the master key. From 6162ed4c11b062beaf1c5bf5818e76a46764c0d6 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:27:27 +0200 Subject: [PATCH 12/12] Update src/Options/index.js Signed-off-by: Manuel <5673677+mtrezza@users.noreply.github.com> --- src/Options/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Options/index.js b/src/Options/index.js index af6f225099..996512e36e 100644 --- a/src/Options/index.js +++ b/src/Options/index.js @@ -47,7 +47,7 @@ export interface ParseServerOptions { appId: string; /* Your Parse Master Key */ masterKey: string; - /* (Optional) The maintenance key is used for modifying internal fields of Parse Server, including object timestamps.

⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. */ + /* (Optional) The maintenance key is used for modifying internal and read-only fields of Parse Server.

⚠️ This key is not intended to be used as part of a regular operation of Parse Server. This key is intended to conduct out-of-band changes such as one-time migrations or data correction tasks. Internal fields are not officially documented and may change at any time without publication in release changelogs. We strongly advice not to rely on internal fields as part of your regular operation and to investigate the implications of any planned changes *directly in the source code* of your current version of Parse Server. */ maintenanceKey: string; /* URL to your parse server with http:// or https://. :ENV: PARSE_SERVER_URL */