Skip to content

Commit 691cf59

Browse files
authored
Adds count class level permission (#3814)
* Adds count class level permission * fixup! Adds count class level permission * Adds missing count property on beforeFind request object * nits
1 parent d71683a commit 691cf59

File tree

5 files changed

+75
-5
lines changed

5 files changed

+75
-5
lines changed

spec/CloudCode.spec.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,4 +1470,36 @@ describe('afterFind hooks', () => {
14701470
});
14711471
});
14721472
});
1473+
1474+
it('should set count to true on beforeFind hooks if query is count', (done) => {
1475+
const hook = {
1476+
method: function(req) {
1477+
expect(req.count).toBe(true);
1478+
return Promise.resolve();
1479+
}
1480+
};
1481+
spyOn(hook, 'method').and.callThrough();
1482+
Parse.Cloud.beforeFind('Stuff', hook.method);
1483+
new Parse.Query('Stuff').count().then((count) => {
1484+
expect(count).toBe(0);
1485+
expect(hook.method).toHaveBeenCalled();
1486+
done();
1487+
});
1488+
});
1489+
1490+
it('should set count to false on beforeFind hooks if query is not count', (done) => {
1491+
const hook = {
1492+
method: function(req) {
1493+
expect(req.count).toBe(false);
1494+
return Promise.resolve();
1495+
}
1496+
};
1497+
spyOn(hook, 'method').and.callThrough();
1498+
Parse.Cloud.beforeFind('Stuff', hook.method);
1499+
new Parse.Query('Stuff').find().then((res) => {
1500+
expect(res.length).toBe(0);
1501+
expect(hook.method).toHaveBeenCalled();
1502+
done();
1503+
});
1504+
});
14731505
});

spec/Schema.spec.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,38 @@ describe('SchemaController', () => {
178178
});
179179
});
180180

181+
it('class-level permissions test count', (done) => {
182+
var obj;
183+
return config.database.loadSchema()
184+
// Create a valid class
185+
.then(schema => schema.validateObject('Stuff', {foo: 'bar'}))
186+
.then(schema => {
187+
var count = {};
188+
return schema.setPermissions('Stuff', {
189+
'create': {'*': true},
190+
'find': {'*': true},
191+
'count': count
192+
})
193+
}).then(() => {
194+
obj = new Parse.Object('Stuff');
195+
obj.set('foo', 'bar');
196+
return obj.save();
197+
}).then((o) => {
198+
obj = o;
199+
var query = new Parse.Query('Stuff');
200+
return query.find();
201+
}).then((results) => {
202+
expect(results.length).toBe(1);
203+
var query = new Parse.Query('Stuff');
204+
return query.count();
205+
}).then(() => {
206+
fail('Class permissions should have rejected this query.');
207+
}, (err) => {
208+
expect(err.message).toEqual('Permission denied for action count on class Stuff.');
209+
done();
210+
});
211+
});
212+
181213
it('can add classes without needing an object', done => {
182214
config.database.loadSchema()
183215
.then(schema => schema.addClassIfNotExists('NewClass', {

src/Controllers/DatabaseController.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,9 @@ DatabaseController.prototype.find = function(className, query, {
744744
const isMaster = acl === undefined;
745745
const aclGroup = acl || [];
746746
op = op || (typeof query.objectId == 'string' && Object.keys(query).length === 1 ? 'get' : 'find');
747+
// Count operation if counting
748+
op = (count === true ? 'count' : op);
749+
747750
let classExists = true;
748751
return this.loadSchema()
749752
.then(schemaController => {

src/Controllers/SchemaController.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ function verifyPermissionKey(key) {
137137
}
138138
}
139139

140-
const CLPValidKeys = Object.freeze(['find', 'get', 'create', 'update', 'delete', 'addField', 'readUserFields', 'writeUserFields']);
140+
const CLPValidKeys = Object.freeze(['find', 'count', 'get', 'create', 'update', 'delete', 'addField', 'readUserFields', 'writeUserFields']);
141141
function validateCLP(perms, fields) {
142142
if (!perms) {
143143
return;
@@ -820,7 +820,7 @@ export default class SchemaController {
820820

821821
// No matching CLP, let's check the Pointer permissions
822822
// And handle those later
823-
const permissionField = ['get', 'find'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
823+
const permissionField = ['get', 'find', 'count'].indexOf(operation) > -1 ? 'readUserFields' : 'writeUserFields';
824824

825825
// Reject create when write lockdown
826826
if (permissionField == 'writeUserFields' && operation == 'create') {

src/triggers.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,12 @@ export function getRequestObject(triggerType, auth, parseObject, originalParseOb
155155
return request;
156156
}
157157

158-
export function getRequestQueryObject(triggerType, auth, query, config) {
158+
export function getRequestQueryObject(triggerType, auth, query, count, config) {
159159
var request = {
160160
triggerName: triggerType,
161-
query: query,
161+
query,
162162
master: false,
163+
count,
163164
log: config.loggerController
164165
};
165166

@@ -298,6 +299,7 @@ export function maybeRunQueryTrigger(triggerType, className, restWhere, restOpti
298299
if (restWhere) {
299300
parseQuery._where = restWhere;
300301
}
302+
let count = false;
301303
if (restOptions) {
302304
if (restOptions.include && restOptions.include.length > 0) {
303305
parseQuery._include = restOptions.include.split(',');
@@ -308,8 +310,9 @@ export function maybeRunQueryTrigger(triggerType, className, restWhere, restOpti
308310
if (restOptions.limit) {
309311
parseQuery._limit = restOptions.limit;
310312
}
313+
count = !!restOptions.count;
311314
}
312-
const requestObject = getRequestQueryObject(triggerType, auth, parseQuery, config);
315+
const requestObject = getRequestQueryObject(triggerType, auth, parseQuery, count, config);
313316
return Promise.resolve().then(() => {
314317
return trigger(requestObject);
315318
}).then((result) => {

0 commit comments

Comments
 (0)