-
-
Notifications
You must be signed in to change notification settings - Fork 10.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5566 from halfdan/refactor-middleware
Middleware Refactor
- Loading branch information
Showing
19 changed files
with
1,461 additions
and
1,001 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,70 @@ | ||
var passport = require('passport'), | ||
BearerStrategy = require('passport-http-bearer').Strategy, | ||
ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy, | ||
models = require('../models'); | ||
var BearerStrategy = require('passport-http-bearer').Strategy, | ||
ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy, | ||
models = require('../models'), | ||
strategies; | ||
|
||
/** | ||
* ClientPasswordStrategy | ||
* | ||
* This strategy is used to authenticate registered OAuth clients. It is | ||
* employed to protect the `token` endpoint, which consumers use to obtain | ||
* access tokens. The OAuth 2.0 specification suggests that clients use the | ||
* HTTP Basic scheme to authenticate (not implemented yet). | ||
strategies = { | ||
|
||
* Use of the client password strategy is implemented to support ember-simple-auth. | ||
*/ | ||
passport.use(new ClientPasswordStrategy( | ||
function strategy(clientId, clientSecret, done) { | ||
models.Client.forge({slug: clientId}) | ||
.fetch() | ||
.then(function then(model) { | ||
if (model) { | ||
var client = model.toJSON(); | ||
if (client.secret === clientSecret) { | ||
return done(null, client); | ||
/** | ||
* ClientPasswordStrategy | ||
* | ||
* This strategy is used to authenticate registered OAuth clients. It is | ||
* employed to protect the `token` endpoint, which consumers use to obtain | ||
* access tokens. The OAuth 2.0 specification suggests that clients use the | ||
* HTTP Basic scheme to authenticate (not implemented yet). | ||
* Use of the client password strategy is implemented to support ember-simple-auth. | ||
*/ | ||
clientPasswordStrategy: new ClientPasswordStrategy( | ||
function strategy(clientId, clientSecret, done) { | ||
models.Client.forge({slug: clientId}) | ||
.fetch() | ||
.then(function then(model) { | ||
if (model) { | ||
var client = model.toJSON(); | ||
if (client.secret === clientSecret) { | ||
return done(null, client); | ||
} | ||
} | ||
} | ||
return done(null, false); | ||
}); | ||
} | ||
)); | ||
return done(null, false); | ||
}); | ||
} | ||
), | ||
|
||
/** | ||
* BearerStrategy | ||
* | ||
* This strategy is used to authenticate users based on an access token (aka a | ||
* bearer token). The user must have previously authorized a client | ||
* application, which is issued an access token to make requests on behalf of | ||
* the authorizing user. | ||
*/ | ||
passport.use(new BearerStrategy( | ||
function strategy(accessToken, done) { | ||
models.Accesstoken.forge({token: accessToken}) | ||
.fetch() | ||
.then(function then(model) { | ||
if (model) { | ||
var token = model.toJSON(); | ||
if (token.expires > Date.now()) { | ||
models.User.forge({id: token.user_id}) | ||
.fetch() | ||
.then(function then(model) { | ||
if (model) { | ||
var user = model.toJSON(), | ||
info = {scope: '*'}; | ||
return done(null, {id: user.id}, info); | ||
} | ||
/** | ||
* BearerStrategy | ||
* | ||
* This strategy is used to authenticate users based on an access token (aka a | ||
* bearer token). The user must have previously authorized a client | ||
* application, which is issued an access token to make requests on behalf of | ||
* the authorizing user. | ||
*/ | ||
bearerStrategy: new BearerStrategy( | ||
function strategy(accessToken, done) { | ||
models.Accesstoken.forge({token: accessToken}) | ||
.fetch() | ||
.then(function then(model) { | ||
if (model) { | ||
var token = model.toJSON(); | ||
if (token.expires > Date.now()) { | ||
models.User.forge({id: token.user_id}) | ||
.fetch() | ||
.then(function then(model) { | ||
if (model) { | ||
var user = model.toJSON(), | ||
info = {scope: '*'}; | ||
return done(null, {id: user.id}, info); | ||
} | ||
return done(null, false); | ||
}); | ||
} else { | ||
return done(null, false); | ||
}); | ||
} | ||
} else { | ||
return done(null, false); | ||
} | ||
} else { | ||
return done(null, false); | ||
} | ||
}); | ||
} | ||
)); | ||
}); | ||
} | ||
) | ||
}; | ||
|
||
module.exports = strategies; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
var passport = require('passport'), | ||
apiErrorHandlers = require('./api-error-handlers'); | ||
|
||
// ### Authenticate Middleware | ||
// authentication has to be done for /ghost/* routes with | ||
// exceptions for signin, signout, signup, forgotten, reset only | ||
// api and frontend use different authentication mechanisms atm | ||
function authenticate(req, res, next) { | ||
var path, | ||
subPath; | ||
|
||
// SubPath is the url path starting after any default subdirectories | ||
// it is stripped of anything after the two levels `/ghost/.*?/` as the reset link has an argument | ||
path = req.path; | ||
/*jslint regexp:true, unparam:true*/ | ||
subPath = path.replace(/^(\/.*?\/.*?\/)(.*)?/, function replace(match, a) { | ||
return a; | ||
}); | ||
|
||
if (subPath.indexOf('/ghost/api/') === 0 | ||
&& (path.indexOf('/ghost/api/v0.1/authentication/') !== 0 | ||
|| (path.indexOf('/ghost/api/v0.1/authentication/setup/') === 0 && req.method === 'PUT'))) { | ||
return passport.authenticate('bearer', {session: false, failWithError: true}, | ||
function authenticate(err, user, info) { | ||
if (err) { | ||
return next(err); // will generate a 500 error | ||
} | ||
// Generate a JSON response reflecting authentication status | ||
if (!user) { | ||
var error = { | ||
code: 401, | ||
errorType: 'NoPermissionError', | ||
message: 'Please Sign In' | ||
}; | ||
|
||
return apiErrorHandlers.errorHandler(error, req, res, next); | ||
} | ||
// TODO: figure out, why user & authInfo is lost | ||
req.authInfo = info; | ||
req.user = user; | ||
return next(null, user, info); | ||
} | ||
)(req, res, next); | ||
} | ||
next(); | ||
} | ||
|
||
module.exports = authenticate; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
var config = require('../config'), | ||
url = require('url'); | ||
|
||
function isSSLrequired(isAdmin, configUrl, forceAdminSSL) { | ||
var forceSSL = url.parse(configUrl).protocol === 'https:' ? true : false; | ||
if (forceSSL || (isAdmin && forceAdminSSL)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
// The guts of checkSSL. Indicate forbidden or redirect according to configuration. | ||
// Required args: forceAdminSSL, url and urlSSL should be passed from config. reqURL from req.url | ||
function sslForbiddenOrRedirect(opt) { | ||
var forceAdminSSL = opt.forceAdminSSL, | ||
reqUrl = opt.reqUrl, // expected to be relative-to-root | ||
baseUrl = url.parse(opt.configUrlSSL || opt.configUrl), | ||
response = { | ||
// Check if forceAdminSSL: { redirect: false } is set, which means | ||
// we should just deny non-SSL access rather than redirect | ||
isForbidden: (forceAdminSSL && forceAdminSSL.redirect !== undefined && !forceAdminSSL.redirect), | ||
|
||
// Append the request path to the base configuration path, trimming out a double "//" | ||
redirectPathname: function redirectPathname() { | ||
var pathname = baseUrl.path; | ||
if (reqUrl[0] === '/' && pathname[pathname.length - 1] === '/') { | ||
pathname += reqUrl.slice(1); | ||
} else { | ||
pathname += reqUrl; | ||
} | ||
return pathname; | ||
}, | ||
redirectUrl: function redirectUrl(query) { | ||
return url.format({ | ||
protocol: 'https:', | ||
hostname: baseUrl.hostname, | ||
port: baseUrl.port, | ||
pathname: this.redirectPathname(), | ||
query: query | ||
}); | ||
} | ||
}; | ||
|
||
return response; | ||
} | ||
|
||
// Check to see if we should use SSL | ||
// and redirect if needed | ||
function checkSSL(req, res, next) { | ||
if (isSSLrequired(res.isAdmin, config.url, config.forceAdminSSL)) { | ||
if (!req.secure) { | ||
var response = sslForbiddenOrRedirect({ | ||
forceAdminSSL: config.forceAdminSSL, | ||
configUrlSSL: config.urlSSL, | ||
configUrl: config.url, | ||
reqUrl: req.url | ||
}); | ||
|
||
if (response.isForbidden) { | ||
return res.sendStatus(403); | ||
} else { | ||
return res.redirect(301, response.redirectUrl(req.query)); | ||
} | ||
} | ||
} | ||
next(); | ||
} | ||
|
||
module.exports = checkSSL; | ||
// SSL helper functions are exported primarily for unit testing. | ||
module.exports.isSSLrequired = isSSLrequired; | ||
module.exports.sslForbiddenOrRedirect = sslForbiddenOrRedirect; |
Oops, something went wrong.