Skip to content

Commit 23bffc8

Browse files
copterchrisflovilmart
authored andcommitted
Add maxLimit server configuration (#4048)
* Add maxLimit server configuration * Fix maxlimit validation logic to correctly handle maxLimit:0 case
1 parent 976da4d commit 23bffc8

File tree

7 files changed

+63
-0
lines changed

7 files changed

+63
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo
218218
* `loggerAdapter` - The default behavior/transport (File) can be changed by creating an adapter class (see [`LoggerAdapter.js`](https://github.com/parse-community/parse-server/blob/master/src/Adapters/Logger/LoggerAdapter.js)).
219219
* `logLevel` - Set the specific level you want to log. Defaults to `info`. The default logger uses the npm log levels as defined by the underlying winston logger. Check [Winston logging levels](https://github.com/winstonjs/winston#logging-levels) for details on values to specify.
220220
* `sessionLength` - The length of time in seconds that a session should be valid for. Defaults to 31536000 seconds (1 year).
221+
* `maxLimit` - The maximum value supported for the limit option on queries. Defaults to unlimited.
221222
* `revokeSessionOnPasswordReset` - When a user changes their password, either through the reset password email or while logged in, all sessions are revoked if this is true. Set to false if you don't want to revoke sessions.
222223
* `accountLockout` - Lock account when a malicious user is attempting to determine an account password by trial and error.
223224
* `passwordPolicy` - Optional password policy rules to enforce.

spec/ParseQuery.spec.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,38 @@ describe('Parse.Query testing', () => {
239239
});
240240
});
241241

242+
it("query with limit equal to maxlimit", function(done) {
243+
var baz = new TestObject({ foo: 'baz' });
244+
var qux = new TestObject({ foo: 'qux' });
245+
reconfigureServer({ maxLimit: 1 })
246+
Parse.Object.saveAll([baz, qux], function() {
247+
var query = new Parse.Query(TestObject);
248+
query.limit(1);
249+
query.find({
250+
success: function(results) {
251+
equal(results.length, 1);
252+
done();
253+
}
254+
});
255+
});
256+
});
257+
258+
it("query with limit exceeding maxlimit", function(done) {
259+
var baz = new TestObject({ foo: 'baz' });
260+
var qux = new TestObject({ foo: 'qux' });
261+
reconfigureServer({ maxLimit: 1 })
262+
Parse.Object.saveAll([baz, qux], function() {
263+
var query = new Parse.Query(TestObject);
264+
query.limit(2);
265+
query.find({
266+
success: function(results) {
267+
equal(results.length, 1);
268+
done();
269+
}
270+
});
271+
});
272+
});
273+
242274
it("containedIn object array queries", function(done) {
243275
var messageList = [];
244276
for (var i = 0; i < 4; ++i) {

spec/index.spec.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,14 @@ describe('server', () => {
415415
.then(done);
416416
})
417417

418+
it('fails if maxLimit is negative', (done) => {
419+
reconfigureServer({ maxLimit: -100 })
420+
.catch(error => {
421+
expect(error).toEqual('Max limit must be a value greater than 0.');
422+
done();
423+
});
424+
});
425+
418426
it('fails if you try to set revokeSessionOnPasswordReset to non-boolean', done => {
419427
reconfigureServer({ revokeSessionOnPasswordReset: 'non-bool' })
420428
.catch(done);

src/Config.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export class Config {
7171
this.mount = removeTrailingSlash(mount);
7272
this.liveQueryController = cacheInfo.liveQueryController;
7373
this.sessionLength = cacheInfo.sessionLength;
74+
this.maxLimit = cacheInfo.maxLimit;
7475
this.expireInactiveSessions = cacheInfo.expireInactiveSessions;
7576
this.generateSessionExpiresAt = this.generateSessionExpiresAt.bind(this);
7677
this.generateEmailVerifyTokenExpiresAt = this.generateEmailVerifyTokenExpiresAt.bind(this);
@@ -86,6 +87,7 @@ export class Config {
8687
revokeSessionOnPasswordReset,
8788
expireInactiveSessions,
8889
sessionLength,
90+
maxLimit,
8991
emailVerifyTokenValidityDuration,
9092
accountLockout,
9193
passwordPolicy,
@@ -113,6 +115,8 @@ export class Config {
113115
this.validateSessionConfiguration(sessionLength, expireInactiveSessions);
114116

115117
this.validateMasterKeyIps(masterKeyIps);
118+
119+
this.validateMaxLimit(maxLimit);
116120
}
117121

118122
static validateAccountLockoutPolicy(accountLockout) {
@@ -220,6 +224,12 @@ export class Config {
220224
}
221225
}
222226

227+
static validateMaxLimit(maxLimit) {
228+
if (maxLimit <= 0) {
229+
throw 'Max limit must be a value greater than 0.'
230+
}
231+
}
232+
223233
generateEmailVerifyTokenExpiresAt() {
224234
if (!this.verifyUserEmails || !this.emailVerifyTokenValidityDuration) {
225235
return undefined;

src/ParseServer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ addParseCloud();
8686
// "javascriptKey": optional key from Parse dashboard
8787
// "push": optional key from configure push
8888
// "sessionLength": optional length in seconds for how long Sessions should be valid for
89+
// "maxLimit": optional upper bound for what can be specified for the 'limit' parameter on queries
8990

9091
class ParseServer {
9192

@@ -138,6 +139,7 @@ class ParseServer {
138139
},
139140
liveQuery = {},
140141
sessionLength = defaults.sessionLength, // 1 Year in seconds
142+
maxLimit,
141143
expireInactiveSessions = defaults.expireInactiveSessions,
142144
revokeSessionOnPasswordReset = defaults.revokeSessionOnPasswordReset,
143145
schemaCacheTTL = defaults.schemaCacheTTL, // cache for 5s
@@ -264,6 +266,7 @@ class ParseServer {
264266
maxUploadSize: maxUploadSize,
265267
liveQueryController: liveQueryController,
266268
sessionLength: Number(sessionLength),
269+
maxLimit: Number(maxLimit),
267270
expireInactiveSessions: expireInactiveSessions,
268271
jsonLogs,
269272
revokeSessionOnPasswordReset,

src/Routers/ClassesRouter.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ export class ClassesRouter extends PromiseRouter {
1515
handleFind(req) {
1616
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
1717
const options = ClassesRouter.optionsFromBody(body);
18+
if (req.config.maxLimit && (body.limit > req.config.maxLimit)) {
19+
// Silently replace the limit on the query with the max configured
20+
options.limit = Number(req.config.maxLimit);
21+
}
1822
if (body.redirectClassNameForKey) {
1923
options.redirectClassNameForKey = String(body.redirectClassNameForKey);
2024
}

src/cli/definitions/parse-server.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@ export default {
194194
help: "Session duration, defaults to 1 year",
195195
action: numberParser("sessionLength")
196196
},
197+
"maxLimit": {
198+
env: "PARSE_SERVER_MAX_LIMIT",
199+
help: "Max value for limit option on queries, defaults to unlimited",
200+
action: numberParser("maxLimit")
201+
},
197202
"verbose": {
198203
env: "VERBOSE",
199204
help: "Set the logging to verbose"

0 commit comments

Comments
 (0)