diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 9a410eed..312bd070 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -872,4 +872,70 @@ describe('schemas', () => { }); }); }); + + it('should set/get schema permissions', done => { + + let object = new Parse.Object('AClass'); + object.save().then(() => { + request.put({ + url: 'http://localhost:8378/1/schemas/AClass/permissions', + headers: masterKeyHeaders, + json: true, + body: { + find: { + '*': true + }, + create: { + 'role:admin': true + } + } + }, (error, response, body) => { + expect(error).toEqual(null); + request.get({ + url: 'http://localhost:8378/1/schemas/AClass/permissions', + headers: masterKeyHeaders, + json: true, + }, (error, response, body) => { + expect(response.statusCode).toEqual(200); + expect(response.body).toEqual({ + find: { + '*': true + }, + create: { + 'role:admin': true + } + }); + done(); + }); + }); + }); + }); + + it('should fail setting schema permissions with invalid key', done => { + + let object = new Parse.Object('AClass'); + object.save().then(() => { + request.put({ + url: 'http://localhost:8378/1/schemas/AClass/permissions', + headers: masterKeyHeaders, + json: true, + body: { + find: { + '*': true + }, + create: { + 'role:admin': true + }, + dummy: { + 'some': true + } + } + }, (error, response, body) => { + expect(error).toEqual(null); + expect(body.code).toEqual(107); + expect(body.error).toEqual('dummy is not a valid operation for class level permissions'); + done(); + }); + }); + }); }); diff --git a/src/Routers/SchemasRouter.js b/src/Routers/SchemasRouter.js index a0a90ef2..4f8acbe6 100644 --- a/src/Routers/SchemasRouter.js +++ b/src/Routers/SchemasRouter.js @@ -106,6 +106,24 @@ function modifySchema(req) { }); } +function setSchemaPermissions(req) { + var className = req.params.className; + return req.config.database.loadSchema() + .then(schema => { + return schema.setPermissions(className, req.body); + }).then((res) => { + return Promise.resolve({response: {}}); + }); +} + +function getSchemaPermissions(req) { + var className = req.params.className; + return req.config.database.loadSchema() + .then(schema => { + return Promise.resolve({response: schema.perms[className]}); + }); +} + // A helper function that removes all join tables for a schema. Returns a promise. var removeJoinTables = (database, mongoSchema) => { return Promise.all(Object.keys(mongoSchema) @@ -171,6 +189,8 @@ export class SchemasRouter extends PromiseRouter { this.route('POST', '/schemas', middleware.promiseEnforceMasterKeyAccess, createSchema); this.route('POST', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, createSchema); this.route('PUT', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, modifySchema); + this.route('GET', '/schemas/:className/permissions', middleware.promiseEnforceMasterKeyAccess, getSchemaPermissions); + this.route('PUT', '/schemas/:className/permissions', middleware.promiseEnforceMasterKeyAccess, setSchemaPermissions); this.route('DELETE', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, deleteSchema); } } diff --git a/src/Schema.js b/src/Schema.js index 2a048a54..496c0b2e 100644 --- a/src/Schema.js +++ b/src/Schema.js @@ -76,6 +76,14 @@ var requiredColumns = { _Role: ["name", "ACL"] } +let CLPValidKeys = ['find', 'get', 'create', 'update', 'delete']; +function validateCLP(perms) { + Object.keys(perms).forEach((key) => { + if (CLPValidKeys.indexOf(key) == -1) { + throw new Parse.Error(Parse.Error.INVALID_JSON, `${key} is not a valid operation for class level permissions`); + } + }); +} // Valid classes must: // Be one of _User, _Installation, _Role, _Session OR // Be a join table OR @@ -288,6 +296,7 @@ class Schema { // Sets the Class-level permissions for a given className, which must exist. setPermissions(className, perms) { + validateCLP(perms); var update = { _metadata: { class_permissions: perms