Merge pull request #747 from ParsePlatform/nlutsenko.schema.middleware

Add promise-based master-key enforce middleware and move SchemasRouter, FeaturesRouter to use it.
This commit is contained in:
Nikita Lutsenko
2016-03-01 16:23:19 -08:00
5 changed files with 49 additions and 63 deletions

View File

@@ -1,4 +1,7 @@
var features = require('../src/features') 'use strict';
var features = require('../src/features');
const request = require("request");
describe('features', () => { describe('features', () => {
it('set and get features', (done) => { it('set and get features', (done) => {
@@ -23,4 +26,19 @@ describe('features', () => {
expect(_features.test).toBeUndefined(); expect(_features.test).toBeUndefined();
done(); 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();
});
});
}); });

View File

@@ -98,8 +98,8 @@ describe('schemas', () => {
json: true, json: true,
headers: restKeyHeaders, headers: restKeyHeaders,
}, (error, response, body) => { }, (error, response, body) => {
expect(response.statusCode).toEqual(401); expect(response.statusCode).toEqual(403);
expect(body.error).toEqual('master key not specified'); expect(body.error).toEqual('unauthorized: master key is required');
done(); done();
}); });
}); });
@@ -110,8 +110,8 @@ describe('schemas', () => {
json: true, json: true,
headers: restKeyHeaders, headers: restKeyHeaders,
}, (error, response, body) => { }, (error, response, body) => {
expect(response.statusCode).toEqual(401); expect(response.statusCode).toEqual(403);
expect(body.error).toEqual('master key not specified'); expect(body.error).toEqual('unauthorized: master key is required');
done(); done();
}); });
}); });
@@ -206,8 +206,8 @@ describe('schemas', () => {
className: 'MyClass', className: 'MyClass',
}, },
}, (error, response, body) => { }, (error, response, body) => {
expect(response.statusCode).toEqual(401); expect(response.statusCode).toEqual(403);
expect(body.error).toEqual('master key not specified'); expect(body.error).toEqual('unauthorized: master key is required');
done(); done();
}); });
}); });

View File

@@ -1,32 +1,13 @@
import PromiseRouter from '../PromiseRouter'; import PromiseRouter from '../PromiseRouter';
import {getFeatures} from '../features'; import * as middleware from "../middlewares";
import { getFeatures } from '../features';
let masterKeyRequiredResponse = () => {
return Promise.resolve({
status: 401,
response: {error: 'master key not specified'},
})
}
export class FeaturesRouter extends PromiseRouter { export class FeaturesRouter extends PromiseRouter {
mountRoutes() { mountRoutes() {
this.route('GET','/features', (req) => { this.route('GET','/features', middleware.promiseEnforceMasterKeyAccess, () => {
return this.handleGET(req); return { response: {
});
}
handleGET(req) {
if (!req.auth.isMaster) {
return masterKeyRequiredResponse();
}
return Promise.resolve({
response: {
results: [getFeatures()] results: [getFeatures()]
} } };
}); });
} }
} }
export default FeaturesRouter;

View File

@@ -5,14 +5,7 @@ var express = require('express'),
Schema = require('../Schema'); Schema = require('../Schema');
import PromiseRouter from '../PromiseRouter'; 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) { function classNameMismatchResponse(bodyClass, pathClass) {
return Promise.resolve({ return Promise.resolve({
@@ -45,9 +38,6 @@ function mongoSchemaToSchemaAPIResponse(schema) {
} }
function getAllSchemas(req) { function getAllSchemas(req) {
if (!req.auth.isMaster) {
return masterKeyRequiredResponse();
}
return req.config.database.collection('_SCHEMA') return req.config.database.collection('_SCHEMA')
.then(coll => coll.find({}).toArray()) .then(coll => coll.find({}).toArray())
.then(schemas => ({response: { .then(schemas => ({response: {
@@ -56,9 +46,6 @@ function getAllSchemas(req) {
} }
function getOneSchema(req) { function getOneSchema(req) {
if (!req.auth.isMaster) {
return masterKeyRequiredResponse();
}
return req.config.database.collection('_SCHEMA') return req.config.database.collection('_SCHEMA')
.then(coll => coll.findOne({'_id': req.params.className})) .then(coll => coll.findOne({'_id': req.params.className}))
.then(schema => ({response: mongoSchemaToSchemaAPIResponse(schema)})) .then(schema => ({response: mongoSchemaToSchemaAPIResponse(schema)}))
@@ -72,9 +59,6 @@ function getOneSchema(req) {
} }
function createSchema(req) { function createSchema(req) {
if (!req.auth.isMaster) {
return masterKeyRequiredResponse();
}
if (req.params.className && req.body.className) { if (req.params.className && req.body.className) {
if (req.params.className != req.body.className) { if (req.params.className != req.body.className) {
return classNameMismatchResponse(req.body.className, req.params.className); return classNameMismatchResponse(req.body.className, req.params.className);
@@ -100,10 +84,6 @@ function createSchema(req) {
} }
function modifySchema(req) { function modifySchema(req) {
if (!req.auth.isMaster) {
return masterKeyRequiredResponse();
}
if (req.body.className && req.body.className != req.params.className) { if (req.body.className && req.body.className != req.params.className) {
return classNameMismatchResponse(req.body.className, req.params.className); return classNameMismatchResponse(req.body.className, req.params.className);
} }
@@ -168,10 +148,6 @@ var removeJoinTables = (database, mongoSchema) => {
}; };
function deleteSchema(req) { function deleteSchema(req) {
if (!req.auth.isMaster) {
return masterKeyRequiredResponse();
}
if (!Schema.classNameIsValid(req.params.className)) { if (!Schema.classNameIsValid(req.params.className)) {
throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, Schema.invalidClassNameMessage(req.params.className)); throw new Parse.Error(Parse.Error.INVALID_CLASS_NAME, Schema.invalidClassNameMessage(req.params.className));
} }
@@ -214,11 +190,11 @@ function deleteSchema(req) {
export class SchemasRouter extends PromiseRouter { export class SchemasRouter extends PromiseRouter {
mountRoutes() { mountRoutes() {
this.route('GET', '/schemas', getAllSchemas); this.route('GET', '/schemas', middleware.promiseEnforceMasterKeyAccess, getAllSchemas);
this.route('GET', '/schemas/:className', getOneSchema); this.route('GET', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, getOneSchema);
this.route('POST', '/schemas', createSchema); this.route('POST', '/schemas', middleware.promiseEnforceMasterKeyAccess, createSchema);
this.route('POST', '/schemas/:className', createSchema); this.route('POST', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, createSchema);
this.route('PUT', '/schemas/:className', modifySchema); this.route('PUT', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, modifySchema);
this.route('DELETE', '/schemas/:className', deleteSchema); this.route('DELETE', '/schemas/:className', middleware.promiseEnforceMasterKeyAccess, deleteSchema);
} }
} }

View File

@@ -194,6 +194,16 @@ function enforceMasterKeyAccess(req, res, next) {
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) { function invalidRequest(req, res) {
res.status(403); res.status(403);
res.end('{"error":"unauthorized"}'); res.end('{"error":"unauthorized"}');
@@ -204,5 +214,6 @@ module.exports = {
allowMethodOverride: allowMethodOverride, allowMethodOverride: allowMethodOverride,
handleParseErrors: handleParseErrors, handleParseErrors: handleParseErrors,
handleParseHeaders: handleParseHeaders, handleParseHeaders: handleParseHeaders,
enforceMasterKeyAccess: enforceMasterKeyAccess enforceMasterKeyAccess: enforceMasterKeyAccess,
promiseEnforceMasterKeyAccess
}; };