diff --git a/README.md b/README.md index 0b6a2ea132..1d90073d47 100644 --- a/README.md +++ b/README.md @@ -251,6 +251,7 @@ The client keys used with Parse are no longer necessary with Parse Server. If yo * `masterKeyIps` - The array of ip addresses where masterKey usage will be restricted to only these ips. (Default to [] which means allow all ips). If you're using this feature and have `useMasterKey: true` in cloudcode, make sure that you put your own ip in this list. * `readOnlyMasterKey` - A masterKey that has full read access to the data, but no write access. This key should be treated the same way as your masterKey, keeping it private. * `objectIdSize` - The string length of the newly generated object's ids. +* `fileCreationPolicy` - Permissions for file creation. Set `readonly` to prevent anyone including master to create new files. Set `master` to only allow master to create files. Set `user` to allow master and users to create files. By default, anonymous users are allowed to create new files. ##### Logging diff --git a/spec/ParseFile.spec.js b/spec/ParseFile.spec.js index 9f19a87119..c1895b1add 100644 --- a/spec/ParseFile.spec.js +++ b/spec/ParseFile.spec.js @@ -363,6 +363,95 @@ describe('Parse.File testing', () => { }); }); + it("save file with 'readonly' file creation policy", async () => { + await reconfigureServer({ + fileCreationPolicy: 'readonly' + }) + + const file = new Parse.File("hello.txt", data, "text/plain"); + try { + await file.save(); + fail('file.save() should not be allowed') + } catch (error) { + expect(error.code).toBe(Parse.Error.OPERATION_FORBIDDEN); + } + }); + + it("save file with 'master' file creation policy", async done => { + await reconfigureServer({ + fileCreationPolicy: 'master' + }) + + // Save as anonymous + let file = new Parse.File("hello.txt", data, "text/plain"); + try { + await file.save(); + fail('file.save() should not be allowed for anonymous users') + } catch (error) { + expect(error.code).toBe(Parse.Error.OPERATION_FORBIDDEN); + } + + // Save as master + file = new Parse.File("hello.txt", data, "text/plain"); + await file.save({ useMasterKey: true }); + + // Save as user + let user = new Parse.User(); + user.set('username', `user${Math.floor(Math.random() * 10000)}`); + user.set('password', 'p@ssw0rd'); + user = await user.signUp(); + request.post({ + headers: { + 'Content-Type': 'text/html', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest' + }, + url: 'http://localhost:8378/1/files/file', + body: 'fee fi fo', + }, (error, response, body) => { + const b = JSON.parse(body); + expect(b.code).toEqual(Parse.Error.OPERATION_FORBIDDEN); + done(); + }); + }); + + it("save file with 'user' file creation policy", async done => { + await reconfigureServer({ + fileCreationPolicy: 'user' + }) + + // Save as anonymous + let file = new Parse.File("hello.txt", data, "text/plain"); + try { + await file.save(); + fail('file.save() should not be allowed for anonymous users') + } catch (error) { + expect(error.code).toBe(Parse.Error.OPERATION_FORBIDDEN); + } + + // Save as master + file = new Parse.File("hello.txt", data, "text/plain"); + await file.save({ useMasterKey: true }); + + // Save as user + let user = new Parse.User(); + user.set('username', `user${Math.floor(Math.random() * 10000)}`); + user.set('password', 'p@ssw0rd'); + user = await user.signUp(); + request.post({ + headers: { + 'Content-Type': 'text/html', + 'X-Parse-Application-Id': 'test', + 'X-Parse-REST-API-Key': 'rest' + }, + url: 'http://localhost:8378/1/files/file', + body: 'fee fi fo', + }, (error) => { + expect(error).toBe(null); + done(); + }); + }); + it("file toJSON testing", done => { const file = new Parse.File("hello.txt", data, "text/plain"); ok(!file.url()); diff --git a/src/Routers/FilesRouter.js b/src/Routers/FilesRouter.js index 8312526f30..ff5bf0004e 100644 --- a/src/Routers/FilesRouter.js +++ b/src/Routers/FilesRouter.js @@ -61,6 +61,21 @@ export class FilesRouter { } createHandler(req, res, next) { + if (req.config.fileCreationPolicy === 'readonly') { + next(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'read-only masterKey isn\'t allowed to perform the file create operation.')); + return; + } + + if (req.config.fileCreationPolicy === 'master' && !req.auth.isMaster) { + next(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Only master is allowed to create new files.')); + return; + } + + if (req.config.fileCreationPolicy === 'user' && !req.auth.isMaster && !req.auth.user) { + next(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Only master and registred users aire allowed to create new files.')); + return; + } + if (!req.body || !req.body.length) { next(new Parse.Error(Parse.Error.FILE_SAVE_ERROR, 'Invalid file upload.'));