diff --git a/spec/LogsRouter.spec.js b/spec/LogsRouter.spec.js index e8907a39b6..a8ef8b25d1 100644 --- a/spec/LogsRouter.spec.js +++ b/spec/LogsRouter.spec.js @@ -1,6 +1,3 @@ -'use strict'; - -const request = require('request'); var LogsRouter = require('../src/Routers/LogsRouter').LogsRouter; var LoggerController = require('../src/Controllers/LoggerController').LoggerController; var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter; @@ -23,7 +20,7 @@ describe('LogsRouter', () => { var router = new LogsRouter(); expect(() => { - router.validateRequest(request); + router.handleGET(request); }).not.toThrow(); done(); }); @@ -43,23 +40,28 @@ describe('LogsRouter', () => { var router = new LogsRouter(); expect(() => { - router.validateRequest(request); + router.handleGET(request); }).toThrow(); done(); }); - it('can check invalid master key of request', done => { - request.get({ - url: 'http://localhost:8378/1/scriptlog', - json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest' + it('can check invalid master key of request', (done) => { + // Make mock request + var request = { + auth: { + isMaster: false + }, + query: {}, + config: { + loggerController: loggerController } - }, (error, response, body) => { - expect(response.statusCode).toEqual(403); - expect(body.error).toEqual('unauthorized: master key is required'); - done(); - }); + }; + + var router = new LogsRouter(); + + expect(() => { + router.handleGET(request); + }).toThrow(); + done(); }); }); diff --git a/spec/ParseGlobalConfig.spec.js b/spec/ParseGlobalConfig.spec.js index 399c9ee624..8b739a785a 100644 --- a/spec/ParseGlobalConfig.spec.js +++ b/spec/ParseGlobalConfig.spec.js @@ -53,8 +53,8 @@ describe('a GlobalConfig', () => { 'X-Parse-REST-API-Key': 'rest' }, }, (error, response, body) => { - expect(response.statusCode).toEqual(403); - expect(body.error).toEqual('unauthorized: master key is required'); + expect(response.statusCode).toEqual(401); + expect(body.error).toEqual('unauthorized'); done(); }); }); diff --git a/spec/ParseRole.spec.js b/spec/ParseRole.spec.js index 02166ddded..83a5a59e30 100644 --- a/spec/ParseRole.spec.js +++ b/spec/ParseRole.spec.js @@ -59,16 +59,17 @@ describe('Parse Role testing', () => { }); }); - + it("should recursively load roles", (done) => { - + var rolesNames = ["FooRole", "BarRole", "BazRole"]; - + var createRole = function(name, parent, user) { - var role = new Parse.Role(name, new Parse.ACL()); + var role = new Parse.Object("_Role") + role.set("name", name); if (user) { var users = role.relation('users'); - users.add(user); + users.add(user); } if (parent) { role.relation('roles').add(parent); @@ -77,7 +78,7 @@ describe('Parse Role testing', () => { } var roleIds = {}; createTestUser().then( (user) => { - + return createRole(rolesNames[0], null, null).then( (aRole) => { roleIds[aRole.get("name")] = aRole.id; return createRole(rolesNames[1], aRole, null); @@ -101,23 +102,5 @@ describe('Parse Role testing', () => { }); }); - it("_Role object should not save without name.", (done) => { - var role = new Parse.Role(); - role.save(null,{useMasterKey:true}) - .then((r) => { - fail("_Role object should not save without name."); - }, (error) => { - expect(error.code).toEqual(111); - role.set('name','testRole'); - role.save(null,{useMasterKey:true}) - .then((r2)=>{ - fail("_Role object should not save without ACL."); - }, (error2) =>{ - expect(error2.code).toEqual(111); - done(); - }); - }); - }); - }); diff --git a/spec/PromiseRouter.spec.js b/spec/PromiseRouter.spec.js deleted file mode 100644 index 999325ac4e..0000000000 --- a/spec/PromiseRouter.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -var PromiseRouter = require("../src/PromiseRouter").default; - -describe("PromiseRouter", () => { - - it("should properly handle rejects", (done) => { - var router = new PromiseRouter(); - router.route("GET", "/dummy", (req)=> { - return Promise.reject({ - error: "an error", - code: -1 - }) - }, (req) => { - fail("this should not be called"); - }); - - router.routes[0].handler({}).then((result) => { - console.error(result); - fail("this should not be called"); - done(); - }, (error)=> { - expect(error.error).toEqual("an error"); - expect(error.code).toEqual(-1); - done(); - }); - }); -}) \ No newline at end of file diff --git a/spec/features.spec.js b/spec/features.spec.js deleted file mode 100644 index 3ddd7a6055..0000000000 --- a/spec/features.spec.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -var features = require('../src/features'); -const request = require("request"); - -describe('features', () => { - it('set and get features', (done) => { - features.setFeature('users', { - testOption1: true, - testOption2: false - }); - - var _features = features.getFeatures(); - - var expected = { - testOption1: true, - testOption2: false - }; - - expect(_features.users).toEqual(expected); - done(); - }); - - it('get features that does not exist', (done) => { - var _features = features.getFeatures(); - expect(_features.test).toBeUndefined(); - done(); - }); - - it('requires the master key to get all schemas', done => { - request.get({ - url: 'http://localhost:8378/1/features', - json: true, - headers: { - 'X-Parse-Application-Id': 'test', - 'X-Parse-REST-API-Key': 'rest' - } - }, (error, response, body) => { - expect(response.statusCode).toEqual(403); - expect(body.error).toEqual('unauthorized: master key is required'); - done(); - }); - }); -}); diff --git a/spec/schemas.spec.js b/spec/schemas.spec.js index 24589a3865..145b2134c8 100644 --- a/spec/schemas.spec.js +++ b/spec/schemas.spec.js @@ -98,8 +98,8 @@ describe('schemas', () => { json: true, headers: restKeyHeaders, }, (error, response, body) => { - expect(response.statusCode).toEqual(403); - expect(body.error).toEqual('unauthorized: master key is required'); + expect(response.statusCode).toEqual(401); + expect(body.error).toEqual('master key not specified'); done(); }); }); @@ -110,8 +110,8 @@ describe('schemas', () => { json: true, headers: restKeyHeaders, }, (error, response, body) => { - expect(response.statusCode).toEqual(403); - expect(body.error).toEqual('unauthorized: master key is required'); + expect(response.statusCode).toEqual(401); + expect(body.error).toEqual('master key not specified'); done(); }); }); @@ -206,8 +206,8 @@ describe('schemas', () => { className: 'MyClass', }, }, (error, response, body) => { - expect(response.statusCode).toEqual(403); - expect(body.error).toEqual('unauthorized: master key is required'); + expect(response.statusCode).toEqual(401); + expect(body.error).toEqual('master key not specified'); done(); }); }); diff --git a/src/Adapters/Files/FilesAdapter.js b/src/Adapters/Files/FilesAdapter.js index d0dda00481..1ddfbabbdd 100644 --- a/src/Adapters/Files/FilesAdapter.js +++ b/src/Adapters/Files/FilesAdapter.js @@ -12,18 +12,6 @@ // database adapter. export class FilesAdapter { - /* this method is responsible to store the file in order to be retrived later by it's file name - * - * - * @param config the current config - * @param filename the filename to save - * @param data the buffer of data from the file - * @param contentType the supposed contentType - * @discussion the contentType can be undefined if the controller was not able to determine it - * - * @return a promise that should fail if the storage didn't succeed - * - */ createFile(config, filename: string, data, contentType: string) { } deleteFile(config, filename) { } diff --git a/src/Adapters/Push/ParsePushAdapter.js b/src/Adapters/Push/ParsePushAdapter.js index c953d15763..3f554054ed 100644 --- a/src/Adapters/Push/ParsePushAdapter.js +++ b/src/Adapters/Push/ParsePushAdapter.js @@ -14,10 +14,6 @@ export class ParsePushAdapter extends PushAdapter { super(pushConfig); this.validPushTypes = ['ios', 'android']; this.senderMap = {}; - // used in PushController for Dashboard Features - this.feature = { - immediatePush: true - }; let pushTypes = Object.keys(pushConfig); for (let pushType of pushTypes) { diff --git a/src/Adapters/Storage/Mongo/MongoCollection.js b/src/Adapters/Storage/Mongo/MongoCollection.js deleted file mode 100644 index cb721db00e..0000000000 --- a/src/Adapters/Storage/Mongo/MongoCollection.js +++ /dev/null @@ -1,53 +0,0 @@ - -let mongodb = require('mongodb'); -let Collection = mongodb.Collection; - -export default class MongoCollection { - _mongoCollection:Collection; - - constructor(mongoCollection:Collection) { - this._mongoCollection = mongoCollection; - } - - // Does a find with "smart indexing". - // Currently this just means, if it needs a geoindex and there is - // none, then build the geoindex. - // This could be improved a lot but it's not clear if that's a good - // idea. Or even if this behavior is a good idea. - find(query, { skip, limit, sort } = {}) { - return this._rawFind(query, { skip, limit, sort }) - .catch(error => { - // Check for "no geoindex" error - if (error.code != 17007 || - !error.message.match(/unable to find index for .geoNear/)) { - throw error; - } - // Figure out what key needs an index - let key = error.message.match(/field=([A-Za-z_0-9]+) /)[1]; - if (!key) { - throw error; - } - - var index = {}; - index[key] = '2d'; - //TODO: condiser moving index creation logic into Schema.js - return this._mongoCollection.createIndex(index) - // Retry, but just once. - .then(() => this._rawFind(query, { skip, limit, sort })); - }); - } - - _rawFind(query, { skip, limit, sort } = {}) { - return this._mongoCollection - .find(query, { skip, limit, sort }) - .toArray(); - } - - count(query, { skip, limit, sort } = {}) { - return this._mongoCollection.count(query, { skip, limit, sort }); - } - - drop() { - return this._mongoCollection.drop(); - } -} diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index 201388b2ff..742420c5b3 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -1,6 +1,4 @@ -import MongoCollection from './MongoCollection'; - let mongodb = require('mongodb'); let MongoClient = mongodb.MongoClient; @@ -32,12 +30,6 @@ export class MongoStorageAdapter { }); } - adaptiveCollection(name: string) { - return this.connect() - .then(() => this.database.collection(name)) - .then(rawCollection => new MongoCollection(rawCollection)); - } - collectionExists(name: string) { return this.connect().then(() => { return this.database.listCollections({ name: name }).toArray(); diff --git a/src/Controllers/AdaptableController.js b/src/Controllers/AdaptableController.js index ab7d715667..902a6eb349 100644 --- a/src/Controllers/AdaptableController.js +++ b/src/Controllers/AdaptableController.js @@ -18,12 +18,8 @@ export class AdaptableController { this.options = options; this.appId = appId; this.adapter = adapter; - this.setFeature(); } - // sets features for Dashboard to consume from features router - setFeature() {} - set adapter(adapter) { this.validateAdapter(adapter); this[_adapter] = adapter; @@ -71,4 +67,4 @@ export class AdaptableController { } } -export default AdaptableController; +export default AdaptableController; \ No newline at end of file diff --git a/src/Controllers/DatabaseController.js b/src/Controllers/DatabaseController.js index a7d26245a7..5fc2f6ef3c 100644 --- a/src/Controllers/DatabaseController.js +++ b/src/Controllers/DatabaseController.js @@ -507,6 +507,7 @@ DatabaseController.prototype.find = function(className, query, options = {}) { mongoWhere = {'$and': [mongoWhere, {'$or': orParts}]}; } if (options.count) { + delete mongoOptions.limit; return collection.count(mongoWhere, mongoOptions); } else { return collection.find(mongoWhere, mongoOptions) diff --git a/src/Controllers/PushController.js b/src/Controllers/PushController.js index 2e2134a6c4..22d9fe1135 100644 --- a/src/Controllers/PushController.js +++ b/src/Controllers/PushController.js @@ -3,16 +3,9 @@ import PromiseRouter from '../PromiseRouter'; import rest from '../rest'; import AdaptableController from './AdaptableController'; import { PushAdapter } from '../Adapters/Push/PushAdapter'; -import features from '../features'; - -const FEATURE_NAME = 'push'; export class PushController extends AdaptableController { - setFeature() { - features.setFeature(FEATURE_NAME, this.adapter.feature || {}); - } - /** * Check whether the deviceType parameter in qury condition is valid or not. * @param {Object} where A query condition diff --git a/src/PromiseRouter.js b/src/PromiseRouter.js index 308e4ded06..4070f70619 100644 --- a/src/PromiseRouter.js +++ b/src/PromiseRouter.js @@ -49,11 +49,17 @@ export default class PromiseRouter { if (handlers.length > 1) { const length = handlers.length; handler = function(req) { - return handlers.reduce((promise, handler) => { - return promise.then((result) => { - return handler(req); - }); - }, Promise.resolve()); + var next = function(i, req, res) { + if (i == length) { + return res; + } + let result = handlers[i](req); + if (!result || typeof result.then !== "function") { + result = Promise.resolve(result); + } + return result.then((res) => (next(i+1, req, res))); + } + return next(0, req); } } diff --git a/src/Routers/FeaturesRouter.js b/src/Routers/FeaturesRouter.js deleted file mode 100644 index 05ccad5b5b..0000000000 --- a/src/Routers/FeaturesRouter.js +++ /dev/null @@ -1,13 +0,0 @@ -import PromiseRouter from '../PromiseRouter'; -import * as middleware from "../middlewares"; -import { getFeatures } from '../features'; - -export class FeaturesRouter extends PromiseRouter { - mountRoutes() { - this.route('GET','/features', middleware.promiseEnforceMasterKeyAccess, () => { - return { response: { - results: [getFeatures()] - } }; - }); - } -} diff --git a/src/Routers/GlobalConfigRouter.js b/src/Routers/GlobalConfigRouter.js index 53abdac50e..1fbde2d531 100644 --- a/src/Routers/GlobalConfigRouter.js +++ b/src/Routers/GlobalConfigRouter.js @@ -3,7 +3,6 @@ var Parse = require('parse/node').Parse; import PromiseRouter from '../PromiseRouter'; -import * as middleware from "../middlewares"; export class GlobalConfigRouter extends PromiseRouter { getGlobalConfig(req) { @@ -19,6 +18,13 @@ export class GlobalConfigRouter extends PromiseRouter { })); } updateGlobalConfig(req) { + if (!req.auth.isMaster) { + return Promise.resolve({ + status: 401, + response: {error: 'unauthorized'}, + }); + } + return req.config.database.rawCollection('_GlobalConfig') .then(coll => coll.findOneAndUpdate({ _id: 1 }, { $set: req.body })) .then(response => { @@ -35,7 +41,7 @@ export class GlobalConfigRouter extends PromiseRouter { mountRoutes() { this.route('GET', '/config', req => { return this.getGlobalConfig(req) }); - this.route('PUT', '/config', middleware.promiseEnforceMasterKeyAccess, req => { return this.updateGlobalConfig(req) }); + this.route('PUT', '/config', req => { return this.updateGlobalConfig(req) }); } } diff --git a/src/Routers/HooksRouter.js b/src/Routers/HooksRouter.js index f214e5a6b9..ed34cdc4c0 100644 --- a/src/Routers/HooksRouter.js +++ b/src/Routers/HooksRouter.js @@ -1,9 +1,15 @@ import { Parse } from 'parse/node'; import PromiseRouter from '../PromiseRouter'; import { HooksController } from '../Controllers/HooksController'; -import * as middleware from "../middlewares"; + +function enforceMasterKeyAccess(req) { + if (!req.auth.isMaster) { + throw new Parse.Error(403, "unauthorized: master key is required"); + } +} export class HooksRouter extends PromiseRouter { + createHook(aHook, config) { return config.hooksController.createHook(aHook).then( (hook) => ({response: hook})); }; @@ -87,14 +93,14 @@ export class HooksRouter extends PromiseRouter { } mountRoutes() { - this.route('GET', '/hooks/functions', middleware.promiseEnforceMasterKeyAccess, this.handleGetFunctions.bind(this)); - this.route('GET', '/hooks/triggers', middleware.promiseEnforceMasterKeyAccess, this.handleGetTriggers.bind(this)); - this.route('GET', '/hooks/functions/:functionName', middleware.promiseEnforceMasterKeyAccess, this.handleGetFunctions.bind(this)); - this.route('GET', '/hooks/triggers/:className/:triggerName', middleware.promiseEnforceMasterKeyAccess, this.handleGetTriggers.bind(this)); - this.route('POST', '/hooks/functions', middleware.promiseEnforceMasterKeyAccess, this.handlePost.bind(this)); - this.route('POST', '/hooks/triggers', middleware.promiseEnforceMasterKeyAccess, this.handlePost.bind(this)); - this.route('PUT', '/hooks/functions/:functionName', middleware.promiseEnforceMasterKeyAccess, this.handlePut.bind(this)); - this.route('PUT', '/hooks/triggers/:className/:triggerName', middleware.promiseEnforceMasterKeyAccess, this.handlePut.bind(this)); + this.route('GET', '/hooks/functions', enforceMasterKeyAccess, this.handleGetFunctions.bind(this)); + this.route('GET', '/hooks/triggers', enforceMasterKeyAccess, this.handleGetTriggers.bind(this)); + this.route('GET', '/hooks/functions/:functionName', enforceMasterKeyAccess, this.handleGetFunctions.bind(this)); + this.route('GET', '/hooks/triggers/:className/:triggerName', enforceMasterKeyAccess, this.handleGetTriggers.bind(this)); + this.route('POST', '/hooks/functions', enforceMasterKeyAccess, this.handlePost.bind(this)); + this.route('POST', '/hooks/triggers', enforceMasterKeyAccess, this.handlePost.bind(this)); + this.route('PUT', '/hooks/functions/:functionName', enforceMasterKeyAccess, this.handlePut.bind(this)); + this.route('PUT', '/hooks/triggers/:className/:triggerName', enforceMasterKeyAccess, this.handlePut.bind(this)); } } diff --git a/src/Routers/LogsRouter.js b/src/Routers/LogsRouter.js index fbc8ec99d4..abd57944da 100644 --- a/src/Routers/LogsRouter.js +++ b/src/Routers/LogsRouter.js @@ -1,22 +1,25 @@ import { Parse } from 'parse/node'; import PromiseRouter from '../PromiseRouter'; -import * as middleware from "../middlewares"; + +// only allow request with master key +let enforceSecurity = (auth) => { + if (!auth || !auth.isMaster) { + throw new Parse.Error( + Parse.Error.OPERATION_FORBIDDEN, + 'Clients aren\'t allowed to perform the ' + + 'get' + ' operation on logs.' + ); + } +} export class LogsRouter extends PromiseRouter { mountRoutes() { - this.route('GET','/scriptlog', middleware.promiseEnforceMasterKeyAccess, this.validateRequest, (req) => { + this.route('GET','/logs', (req) => { return this.handleGET(req); }); } - validateRequest(req) { - if (!req.config || !req.config.loggerController) { - throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, - 'Logger adapter is not availabe'); - } - } - // Returns a promise for a {response} object. // query params: // level (optional) Level of logging you want to query for (info || error) @@ -24,25 +27,28 @@ export class LogsRouter extends PromiseRouter { // until (optional) End time for the search. Defaults to current time. // order (optional) Direction of results returned, either “asc” or “desc”. Defaults to “desc”. // size (optional) Number of rows returned by search. Defaults to 10 - // n same as size, overrides size if set handleGET(req) { - const from = req.query.from; - const until = req.query.until; - let size = req.query.size; - if (req.query.n) { - size = req.query.n; + if (!req.config || !req.config.loggerController) { + throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, + 'Logger adapter is not availabe'); } + + let promise = new Parse.Promise(); + let from = req.query.from; + let until = req.query.until; + let size = req.query.size; + let order = req.query.order + let level = req.query.level; + enforceSecurity(req.auth); - const order = req.query.order - const level = req.query.level; const options = { from, until, size, order, - level - }; - + level, + } + return req.config.loggerController.getLogs(options).then((result) => { return Promise.resolve({ response: result diff --git a/src/Routers/SchemasRouter.js b/src/Routers/SchemasRouter.js index 007625f3c4..352b1caf44 100644 --- a/src/Routers/SchemasRouter.js +++ b/src/Routers/SchemasRouter.js @@ -5,7 +5,14 @@ var express = require('express'), Schema = require('../Schema'); import PromiseRouter from '../PromiseRouter'; -import * as middleware from "../middlewares"; + +// TODO: refactor in a SchemaController at one point... +function masterKeyRequiredResponse() { + return Promise.resolve({ + status: 401, + response: {error: 'master key not specified'}, + }) +} function classNameMismatchResponse(bodyClass, pathClass) { return Promise.resolve({ @@ -38,13 +45,20 @@ function mongoSchemaToSchemaAPIResponse(schema) { } function getAllSchemas(req) { - return req.config.database.adaptiveCollection('_SCHEMA') - .then(collection => collection.find({})) - .then(schemas => schemas.map(mongoSchemaToSchemaAPIResponse)) - .then(schemas => ({ response: { results: schemas }})); + if (!req.auth.isMaster) { + return masterKeyRequiredResponse(); + } + return req.config.database.collection('_SCHEMA') + .then(coll => coll.find({}).toArray()) + .then(schemas => ({response: { + results: schemas.map(mongoSchemaToSchemaAPIResponse) + }})); } function getOneSchema(req) { + if (!req.auth.isMaster) { + return masterKeyRequiredResponse(); + } return req.config.database.collection('_SCHEMA') .then(coll => coll.findOne({'_id': req.params.className})) .then(schema => ({response: mongoSchemaToSchemaAPIResponse(schema)})) @@ -58,6 +72,9 @@ function getOneSchema(req) { } function createSchema(req) { + if (!req.auth.isMaster) { + return masterKeyRequiredResponse(); + } if (req.params.className && req.body.className) { if (req.params.className != req.body.className) { return classNameMismatchResponse(req.body.className, req.params.className); @@ -83,6 +100,10 @@ function createSchema(req) { } function modifySchema(req) { + if (!req.auth.isMaster) { + return masterKeyRequiredResponse(); + } + if (req.body.className && req.body.className != req.params.className) { return classNameMismatchResponse(req.body.className, req.params.className); } @@ -147,11 +168,15 @@ var removeJoinTables = (database, mongoSchema) => { }; function deleteSchema(req) { + if (!req.auth.isMaster) { + return masterKeyRequiredResponse(); + } + if (!Schema.classNameIsValid(req.params.className)) { throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, Schema.invalidClassNameMessage(req.params.className)); } - return req.config.database.adaptiveCollection(req.params.className) + return req.config.database.collection(req.params.className) .then(collection => { return collection.count() .then(count => { @@ -160,19 +185,19 @@ function deleteSchema(req) { } return collection.drop(); }) - }) - .then(() => { - // We've dropped the collection now, so delete the item from _SCHEMA - // and clear the _Join collections - return req.config.database.collection('_SCHEMA') - .then(coll => coll.findAndRemove({_id: req.params.className}, [])) - .then(doc => { - if (doc.value === null) { - //tried to delete non-existent class - return Promise.resolve(); - } - return removeJoinTables(req.config.database, doc.value); - }); + .then(() => { + // We've dropped the collection now, so delete the item from _SCHEMA + // and clear the _Join collections + return req.config.database.collection('_SCHEMA') + .then(coll => coll.findAndRemove({_id: req.params.className}, [])) + .then(doc => { + if (doc.value === null) { + //tried to delete non-existent class + return Promise.resolve(); + } + return removeJoinTables(req.config.database, doc.value); + }); + }) }) .then(() => { // Success @@ -189,11 +214,11 @@ function deleteSchema(req) { export class SchemasRouter extends PromiseRouter { mountRoutes() { - this.route('GET', '/schemas', middleware.promiseEnforceMasterKeyAccess, getAllSchemas); - this.route('GET', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, getOneSchema); - 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('DELETE', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, deleteSchema); + this.route('GET', '/schemas', getAllSchemas); + this.route('GET', '/schemas/:className', getOneSchema); + this.route('POST', '/schemas', createSchema); + this.route('POST', '/schemas/:className', createSchema); + this.route('PUT', '/schemas/:className', modifySchema); + this.route('DELETE', '/schemas/:className', deleteSchema); } } diff --git a/src/Schema.js b/src/Schema.js index b16dfd188e..5c8a94d183 100644 --- a/src/Schema.js +++ b/src/Schema.js @@ -73,8 +73,7 @@ var defaultColumns = { var requiredColumns = { - _Product: ["productIdentifier", "icon", "order", "title", "subtitle"], - _Role: ["name", "ACL"] + _Product: ["productIdentifier", "icon", "order", "title", "subtitle"] } // Valid classes must: diff --git a/src/cli/parse-server.js b/src/cli/parse-server.js index c5945b6c15..627964f981 100755 --- a/src/cli/parse-server.js +++ b/src/cli/parse-server.js @@ -42,6 +42,14 @@ if (program.args.length > 0 ) { console.log(`Configuation loaded from ${jsonPath}`) } +if (!program.appId || !program.masterKey || !program.serverURL) { + program.outputHelp(); + console.error(""); + console.error(colors.red("ERROR: appId, masterKey and serverURL are required")); + console.error(""); + process.exit(1); +} + options = Object.keys(definitions).reduce(function (options, key) { if (program[key]) { options[key] = program[key]; @@ -53,14 +61,6 @@ if (!options.serverURL) { options.serverURL = `http://localhost:${options.port}${options.mountPath}`; } -if (!options.appId || !options.masterKey || !options.serverURL) { - program.outputHelp(); - console.error(""); - console.error(colors.red("ERROR: appId, masterKey and serverURL are required")); - console.error(""); - process.exit(1); -} - const app = express(); const api = new ParseServer(options); app.use(options.mountPath, api); diff --git a/src/features.js b/src/features.js deleted file mode 100644 index 1048b91cab..0000000000 --- a/src/features.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * features.js - * Feature config file that holds information on the features that are currently - * available on Parse Server. This is primarily created to work with an UI interface - * like the web dashboard. The list of features will change depending on the your - * app, choice of adapter as well as Parse Server version. This approach will enable - * the dashboard to be built independently and still support these use cases. - * - * - * Default features and feature options are listed in the features object. - * - * featureSwitch is a convenient way to turn on/off features without changing the config - * - * Features that use Adapters should specify the feature options through - * the setFeature method in your controller and feature - * Reference PushController and ParsePushAdapter as an example. - * - * NOTE: When adding new endpoints be sure to update this list both (features, featureSwitch) - * if you are planning to have a UI consume it. - */ - -// default features -let features = { - analytics: { - slowQueries: false, - performanceAnalysis: false, - retentionAnalysis: false, - }, - classes: {}, - files: {}, - functions: {}, - globalConfig: { - create: true, - read: true, - update: true, - delete: true, - }, - hooks: { - create: false, - read: false, - update: false, - delete: false, - }, - iapValidation: {}, - installations: {}, - logs: { - info: true, - error: true, - }, - publicAPI: {}, - push: {}, - roles: {}, - schemas: { - addField: true, - removeField: true, - addClass: true, - removeClass: true, - clearAllDataFromClass: false, - exportClass: false, - }, - sessions: {}, - users: {}, -}; - -// master switch for features -let featuresSwitch = { - analytics: true, - classes: true, - files: true, - functions: true, - globalConfig: true, - hooks: true, - iapValidation: true, - installations: true, - logs: true, - publicAPI: true, - push: true, - roles: true, - schemas: true, - sessions: true, - users: true, -}; - -/** - * set feature config options - */ -function setFeature(key, value) { - features[key] = value; -} - -/** - * get feature config options - */ -function getFeatures() { - let result = {}; - Object.keys(features).forEach((key) => { - if (featuresSwitch[key]) { - result[key] = features[key]; - } - }); - return result; -} - -module.exports = { - getFeatures, - setFeature, -}; diff --git a/src/index.js b/src/index.js index b521a26f03..13af8463fe 100644 --- a/src/index.js +++ b/src/index.js @@ -18,7 +18,6 @@ import ParsePushAdapter from './Adapters/Push/ParsePushAdapter'; import PromiseRouter from './PromiseRouter'; import { AnalyticsRouter } from './Routers/AnalyticsRouter'; import { ClassesRouter } from './Routers/ClassesRouter'; -import { FeaturesRouter } from './Routers/FeaturesRouter'; import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter'; import { FilesController } from './Controllers/FilesController'; import { FilesRouter } from './Routers/FilesRouter'; @@ -208,8 +207,7 @@ function ParseServer({ new SchemasRouter(), new PushRouter(), new LogsRouter(), - new IAPValidationRouter(), - new FeaturesRouter(), + new IAPValidationRouter() ]; if (process.env.PARSE_EXPERIMENTAL_CONFIG_ENABLED || process.env.TESTING) { diff --git a/src/middlewares.js b/src/middlewares.js index 56ebdc1dac..8489cda02d 100644 --- a/src/middlewares.js +++ b/src/middlewares.js @@ -194,16 +194,6 @@ function enforceMasterKeyAccess(req, res, next) { next(); } -function promiseEnforceMasterKeyAccess(request) { - if (!request.auth.isMaster) { - let error = new Error(); - error.status = 403; - error.message = "unauthorized: master key is required"; - throw error; - } - return Promise.resolve(); -} - function invalidRequest(req, res) { res.status(403); res.end('{"error":"unauthorized"}'); @@ -214,6 +204,5 @@ module.exports = { allowMethodOverride: allowMethodOverride, handleParseErrors: handleParseErrors, handleParseHeaders: handleParseHeaders, - enforceMasterKeyAccess: enforceMasterKeyAccess, - promiseEnforceMasterKeyAccess + enforceMasterKeyAccess: enforceMasterKeyAccess };