Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Remove authentication for public endpoints #4251

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 21 additions & 19 deletions core/server/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@
// Ghost's JSON API is integral to the workings of Ghost, regardless of whether you want to access data internally,
// from a theme, an app, or from an external app, you'll use the Ghost JSON API to do so.

var _ = require('lodash'),
Promise = require('bluebird'),
config = require('../config'),
var _ = require('lodash'),
Promise = require('bluebird'),
config = require('../config'),
// Include Endpoints
configuration = require('./configuration'),
db = require('./db'),
mail = require('./mail'),
notifications = require('./notifications'),
posts = require('./posts'),
roles = require('./roles'),
settings = require('./settings'),
tags = require('./tags'),
themes = require('./themes'),
users = require('./users'),
slugs = require('./slugs'),
authentication = require('./authentication'),
uploads = require('./upload'),
dataExport = require('../data/export'),
errors = require('../errors'),
configuration = require('./configuration'),
db = require('./db'),
mail = require('./mail'),
notifications = require('./notifications'),
posts = require('./posts'),
roles = require('./roles'),
settings = require('./settings'),
tags = require('./tags'),
themes = require('./themes'),
users = require('./users'),
slugs = require('./slugs'),
authentication = require('./authentication'),
uploads = require('./upload'),
dataExport = require('../data/export'),
publicEndpoints = require('./publicEndpoints'),
errors = require('../errors'),

http,
formatHttpErrors,
Expand Down Expand Up @@ -289,7 +290,8 @@ module.exports = {
users: users,
slugs: slugs,
authentication: authentication,
uploads: uploads
uploads: uploads,
publicEndpoints: publicEndpoints
};

/**
Expand Down
123 changes: 123 additions & 0 deletions core/server/api/publicEndpoints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@

var endpoints = [
{
// /ghost/api/v0.1/posts/?status=published
pathPattern: /^\/ghost\/api\/v0.1\/posts(\/)?$/,
// white listed allowed verbs
verbs: ['GET'],
// white listed allowed parameters
opts: [
{status: 'published'}
]
},
{
// /ghost/api/v0.1/posts/:id
pathPattern: /^\/ghost\/api\/v0.1\/posts\/[0-9](\/)?$/,
// white listed allowed verbs
verbs: ['GET'],
// white listed allowed parameters
opts: [
{status: 'published'}
]
},
{
// /ghost/api/v0.1/posts/slug/:slug
pathPattern: /^\/ghost\/api\/v0.1\/posts\/slug\/[a-zA-Z0-9\-]{1,}(\/)?$/,
// white listed allowed verbs
verbs: ['GET'],
opts: [
{status: 'published'}
]
},
{
// /ghost/api/v0.1/users/:id/?status=active
pathPattern: /^\/ghost\/api\/v0.1\/users\/[0-9](\/)?$/,
// white listed allowed verbs
verbs: ['GET'],
// white listed allowed parameters
opts: [
{status: 'active'}
]
},
{
// /ghost/api/v0.1/users/email/:email/
// TODO: Use proper email regex here
pathPattern: /^\/ghost\/api\/v0.1\/users\/email\/.{1,}(\/)?$/,
// white listed allowed verbs
verbs: ['GET']
},
{
// /ghost/api/v0.1/users/slug/:slug
pathPattern: /^\/ghost\/api\/v0.1\/users\/slug\/[a-zA-Z0-9\-]{1,}(\/)?$/,
// white listed allowed verbs
verbs: ['GET']
},
{
// /ghost/api/v0.1/tags/
pathPattern: /^\/ghost\/api\/v0.1\/tags(\/)?$/,
// white listed allowed verbs
verbs: ['GET']
},
{
// /ghost/api/v0.1/settings/?type=blog
pathPattern: /^\/ghost\/api\/v0.1\/settings(\/)?$/,
// white listed allowed verbs
verbs: ['GET'],
// white listed allowed parameters
opts: [
{type: 'blog'}
]
},
{
// /ghost/api/v0.1/settings/:key/?type=blog
pathPattern: /^\/ghost\/api\/v0.1\/settings\/[0-9a-zA-Z]{1,}(\/)?$/,
// white listed allowed verbs
verbs: ['GET'],
// white listed allowed parameters
opts: [
{type: 'blog'}
]
}
];

function isUsingPublicParams(endpoint, req) {
var param,
i,
allowedParam;

for (param in req.query) {
if (req.query.hasOwnProperty(param)) {
for (i = 0; i < endpoint.opts.length; i += 1) {
allowedParam = endpoint.opts[ i ];
if (allowedParam[param] && req.query[param] === allowedParam[param]) {
return true;
}
}
}
}
return false;
}

function hasPublicVerb(endpoint, req) {
return (endpoint.verbs.indexOf(req.method) > -1);
}

function isPathPublic(path) {
var i,
endpoint;

for (i = 0; i < endpoints.length; i += 1) {
endpoint = endpoints[i];
if (endpoint.pathPattern.test(path)) {
return endpoint;
}
}
}

exports.isPublic = function (req, path) {
var endpoint = isPathPublic(path);
if (endpoint && hasPublicVerb(endpoint, req) && isUsingPublicParams(endpoint, req)) {
return true;
}
return false;
};
44 changes: 24 additions & 20 deletions core/server/middleware/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,31 @@ middleware = {

if (subPath.indexOf('/ghost/api/') === 0
&& path.indexOf('/ghost/api/v0.1/authentication/') !== 0) {
return passport.authenticate('bearer', {session: false, failWithError: true},
function (err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting authentication status
if (!user) {
var msg = {
type: 'error',
message: 'Please Sign In',
status: 'passive'
};
res.status(401);
return res.send(msg);
if (api.publicEndpoints.isPublic(req, path)) {
return next(null, {}, {});
} else {
return passport.authenticate('bearer', {session: false, failWithError: true},
function (err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting authentication status
if (!user) {
var msg = {
type: 'error',
message: 'Please Sign In',
status: 'passive'
};
res.status(401);
return res.send(msg);
}
// TODO: figure out, why user & authInfo is lost
req.authInfo = info;
req.user = user;
return next(null, user, info);
}
// TODO: figure out, why user & authInfo is lost
req.authInfo = info;
req.user = user;
return next(null, user, info);
}
)(req, res, next);
)(req, res, next);
}
}
next();
},
Expand Down