diff --git a/CHANGELOG.md b/CHANGELOG.md index b04fa45be0..2d3fcafa71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,7 @@ ___ - Added Deprecation Policy to govern the introduction of breaking changes in a phased pattern that is more predictable for developers (Manuel Trezza) [#7199](https://github.com/parse-community/parse-server/pull/7199) - Add REST API endpoint `/loginAs` to create session of any user with master key; allows to impersonate another user. (GormanFletcher) [#7406](https://github.com/parse-community/parse-server/pull/7406) - Add official support for MongoDB 5.0 (Manuel Trezza) [#7469](https://github.com/parse-community/parse-server/pull/7469) +- Deprecated ``explain` queries run by non-master users (Kartal Kaan Bozdogan) [#7519](https://github.com/parse-community/parse-server/issues/7519) - Add issue bot (Manuel Trezza) [#7523](https://github.com/parse-community/parse-server/pull/7523) ### Other Changes diff --git a/DEPRECATIONS.md b/DEPRECATIONS.md index cf220a0054..7be8a285eb 100644 --- a/DEPRECATIONS.md +++ b/DEPRECATIONS.md @@ -6,6 +6,7 @@ The following is a list of deprecations, according to the [Deprecation Policy](h |-------------------------------------------------|----------------------------------------------------------------------|---------------------------------|---------------------------------|-----------------------|-------| | Native MongoDB syntax in aggregation pipeline | [#7338](https://github.com/parse-community/parse-server/issues/7338) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - | | Config option `directAccess` defaults to `true` | [#6636](https://github.com/parse-community/parse-server/pull/6636) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - | +| `explain` queries used by non-master users | [#7519](https://github.com/parse-community/parse-server/issues/7519) | 5.0.0 (2022) | 6.0.0 (2023) | deprecated | - | [i_deprecation]: ## "The version and date of the deprecation." [i_removal]: ## "The version and date of the planned removal." diff --git a/spec/ParseQuery.spec.js b/spec/ParseQuery.spec.js index e196280a5c..cc77bab7a9 100644 --- a/spec/ParseQuery.spec.js +++ b/spec/ParseQuery.spec.js @@ -5218,4 +5218,36 @@ describe('Parse.Query testing', () => { // Validate expect(result.executionStats).not.toBeUndefined(); }); + + xit('users cannot use explain queries', async () => { + // Create an object + const obj = new TestObject({ foo: 'baz', hello: 'world' }); + await obj.save(); + // Query TestObject with explain. + const query = new Parse.Query('TestObject'); + query.equalTo('objectId', obj.id); + query.explain(); + try { + await query.find(); + fail('even non-master users can use explain'); + } catch (e) { + equal(e.code, Parse.Error.OPERATION_FORBIDDEN); + equal(e.message, 'Cannot explain'); + } + try { + await new Parse.Query('TestObject').explain().get(obj.id); + fail('even non-master users can use explain'); + } catch (e) { + equal(e.code, Parse.Error.OPERATION_FORBIDDEN); + equal(e.message, 'Cannot explain'); + } + }).pend('Disabled until non-master explains are disabled'); + it('the master key can use explain queries', async () => { + const obj = new TestObject({ foo: 'baz', hello: 'world' }); + await obj.save(); + const query = new Parse.Query('TestObject'); + query.equalTo('objectId', obj.id); + query.explain(); + await query.find({ useMasterKey: true }); // Must not throw + }); }); diff --git a/src/rest.js b/src/rest.js index fca3497a5d..4f7dbbdebc 100644 --- a/src/rest.js +++ b/src/rest.js @@ -12,6 +12,7 @@ var Parse = require('parse/node').Parse; var RestQuery = require('./RestQuery'); var RestWrite = require('./RestWrite'); var triggers = require('./triggers'); +import Deprecator from './Deprecator/Deprecator'; function checkTriggers(className, config, types) { return types.some(triggerType => { @@ -26,6 +27,12 @@ function checkLiveQuery(className, config) { // Returns a promise for an object with optional keys 'results' and 'count'. function find(config, auth, className, restWhere, restOptions, clientSDK, context) { enforceRoleSecurity('find', className, auth); + if (restOptions && restOptions.explain && !auth.isMaster) { + //throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Cannot explain'); + Deprecator.logRuntimeDeprecation({ + usage: 'The use of explain queries by non-master users', + }); + } return triggers .maybeRunQueryTrigger( triggers.Types.beforeFind, @@ -57,6 +64,12 @@ function find(config, auth, className, restWhere, restOptions, clientSDK, contex const get = (config, auth, className, objectId, restOptions, clientSDK, context) => { var restWhere = { objectId }; enforceRoleSecurity('get', className, auth); + if (restOptions && restOptions.explain && !auth.isMaster) { + //throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Cannot explain'); + Deprecator.logRuntimeDeprecation({ + usage: 'The use of explain queries by non-master users', + }); + } return triggers .maybeRunQueryTrigger( triggers.Types.beforeFind,