Skip to content

Add maxLimit server configuration #4048

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo
* `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)).
* `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.
* `sessionLength` - The length of time in seconds that a session should be valid for. Defaults to 31536000 seconds (1 year).
* `maxLimit` - The maximum value supported for the limit option on queries. Defaults to unlimited.
* `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.
* `accountLockout` - Lock account when a malicious user is attempting to determine an account password by trial and error.
* `passwordPolicy` - Optional password policy rules to enforce.
Expand Down
32 changes: 32 additions & 0 deletions spec/ParseQuery.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,38 @@ describe('Parse.Query testing', () => {
});
});

it("query with limit equal to maxlimit", function(done) {
var baz = new TestObject({ foo: 'baz' });
var qux = new TestObject({ foo: 'qux' });
reconfigureServer({ maxLimit: 1 })
Parse.Object.saveAll([baz, qux], function() {
var query = new Parse.Query(TestObject);
query.limit(1);
query.find({
success: function(results) {
equal(results.length, 1);
done();
}
});
});
});

it("query with limit exceeding maxlimit", function(done) {
var baz = new TestObject({ foo: 'baz' });
var qux = new TestObject({ foo: 'qux' });
reconfigureServer({ maxLimit: 1 })
Parse.Object.saveAll([baz, qux], function() {
var query = new Parse.Query(TestObject);
query.limit(2);
query.find({
success: function(results) {
equal(results.length, 1);
done();
}
});
});
});

it("containedIn object array queries", function(done) {
var messageList = [];
for (var i = 0; i < 4; ++i) {
Expand Down
8 changes: 8 additions & 0 deletions spec/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,14 @@ describe('server', () => {
.then(done);
})

it('fails if maxLimit is negative', (done) => {
reconfigureServer({ maxLimit: -100 })
.catch(error => {
expect(error).toEqual('Max limit must be a value greater than 0.');
done();
});
});

it('fails if you try to set revokeSessionOnPasswordReset to non-boolean', done => {
reconfigureServer({ revokeSessionOnPasswordReset: 'non-bool' })
.catch(done);
Expand Down
10 changes: 10 additions & 0 deletions src/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class Config {
this.mount = removeTrailingSlash(mount);
this.liveQueryController = cacheInfo.liveQueryController;
this.sessionLength = cacheInfo.sessionLength;
this.maxLimit = cacheInfo.maxLimit;
this.expireInactiveSessions = cacheInfo.expireInactiveSessions;
this.generateSessionExpiresAt = this.generateSessionExpiresAt.bind(this);
this.generateEmailVerifyTokenExpiresAt = this.generateEmailVerifyTokenExpiresAt.bind(this);
Expand All @@ -86,6 +87,7 @@ export class Config {
revokeSessionOnPasswordReset,
expireInactiveSessions,
sessionLength,
maxLimit,
emailVerifyTokenValidityDuration,
accountLockout,
passwordPolicy,
Expand Down Expand Up @@ -113,6 +115,8 @@ export class Config {
this.validateSessionConfiguration(sessionLength, expireInactiveSessions);

this.validateMasterKeyIps(masterKeyIps);

this.validateMaxLimit(maxLimit);
}

static validateAccountLockoutPolicy(accountLockout) {
Expand Down Expand Up @@ -220,6 +224,12 @@ export class Config {
}
}

static validateMaxLimit(maxLimit) {
if (maxLimit <= 0) {
throw 'Max limit must be a value greater than 0.'
}
}

generateEmailVerifyTokenExpiresAt() {
if (!this.verifyUserEmails || !this.emailVerifyTokenValidityDuration) {
return undefined;
Expand Down
3 changes: 3 additions & 0 deletions src/ParseServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ addParseCloud();
// "javascriptKey": optional key from Parse dashboard
// "push": optional key from configure push
// "sessionLength": optional length in seconds for how long Sessions should be valid for
// "maxLimit": optional upper bound for what can be specified for the 'limit' parameter on queries

class ParseServer {

Expand Down Expand Up @@ -138,6 +139,7 @@ class ParseServer {
},
liveQuery = {},
sessionLength = defaults.sessionLength, // 1 Year in seconds
maxLimit,
expireInactiveSessions = defaults.expireInactiveSessions,
revokeSessionOnPasswordReset = defaults.revokeSessionOnPasswordReset,
schemaCacheTTL = defaults.schemaCacheTTL, // cache for 5s
Expand Down Expand Up @@ -264,6 +266,7 @@ class ParseServer {
maxUploadSize: maxUploadSize,
liveQueryController: liveQueryController,
sessionLength: Number(sessionLength),
maxLimit: Number(maxLimit),
expireInactiveSessions: expireInactiveSessions,
jsonLogs,
revokeSessionOnPasswordReset,
Expand Down
4 changes: 4 additions & 0 deletions src/Routers/ClassesRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export class ClassesRouter extends PromiseRouter {
handleFind(req) {
const body = Object.assign(req.body, ClassesRouter.JSONFromQuery(req.query));
const options = ClassesRouter.optionsFromBody(body);
if (req.config.maxLimit && (body.limit > req.config.maxLimit)) {
// Silently replace the limit on the query with the max configured
options.limit = Number(req.config.maxLimit);
}
if (body.redirectClassNameForKey) {
options.redirectClassNameForKey = String(body.redirectClassNameForKey);
}
Expand Down
5 changes: 5 additions & 0 deletions src/cli/definitions/parse-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ export default {
help: "Session duration, defaults to 1 year",
action: numberParser("sessionLength")
},
"maxLimit": {
env: "PARSE_SERVER_MAX_LIMIT",
help: "Max value for limit option on queries, defaults to unlimited",
action: numberParser("maxLimit")
},
"verbose": {
env: "VERBOSE",
help: "Set the logging to verbose"
Expand Down