Fix user-literal rewrite for anonymous requests
Currently any `currentUserLiteral` routes when accessed with a bad token throw a 500 due to a SQL error that is raised because `Model.findById` is invoked with `id={currentUserLiteral}` (`id=me` in our case) when the url rewrite fails. This commit changes the token middleware to return 401 Not Authorized when the client is requesting a currentUserLiteral route without a valid access token.
This commit is contained in:
parent
0499d09d6b
commit
fbf818b2dc
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
'use strict';
|
||||
var g = require('../../lib/globalize');
|
||||
var loopback = require('../../lib/loopback');
|
||||
var assert = require('assert');
|
||||
var debug = require('debug')('loopback:middleware:token');
|
||||
|
@ -21,18 +22,33 @@ module.exports = token;
|
|||
/*
|
||||
* Rewrite the url to replace current user literal with the logged in user id
|
||||
*/
|
||||
function rewriteUserLiteral(req, currentUserLiteral) {
|
||||
if (req.accessToken && req.accessToken.userId && currentUserLiteral) {
|
||||
function rewriteUserLiteral(req, currentUserLiteral, next) {
|
||||
if (!currentUserLiteral) return next();
|
||||
const literalRegExp = new RegExp('/' + currentUserLiteral + '(/|$|\\?)', 'g');
|
||||
|
||||
if (req.accessToken && req.accessToken.userId) {
|
||||
// Replace /me/ with /current-user-id/
|
||||
var urlBeforeRewrite = req.url;
|
||||
req.url = req.url.replace(
|
||||
new RegExp('/' + currentUserLiteral + '(/|$|\\?)', 'g'),
|
||||
req.url = req.url.replace(literalRegExp,
|
||||
'/' + req.accessToken.userId + '$1');
|
||||
|
||||
if (req.url !== urlBeforeRewrite) {
|
||||
debug('req.url has been rewritten from %s to %s', urlBeforeRewrite,
|
||||
req.url);
|
||||
}
|
||||
} else if (!req.accessToken && literalRegExp.test(req.url)) {
|
||||
debug(
|
||||
'URL %s matches current-user literal %s,' +
|
||||
' but no (valid) access token was provided.',
|
||||
req.url, currentUserLiteral);
|
||||
|
||||
var e = new Error(g.f('Authorization Required'));
|
||||
e.status = e.statusCode = 401;
|
||||
e.code = 'AUTHORIZATION_REQUIRED';
|
||||
return next(e);
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
function escapeRegExp(str) {
|
||||
|
@ -105,23 +121,24 @@ function token(options) {
|
|||
if (!enableDoublecheck) {
|
||||
// req.accessToken is defined already (might also be "null" or "false") and enableDoublecheck
|
||||
// has not been set --> skip searching for credentials
|
||||
rewriteUserLiteral(req, currentUserLiteral);
|
||||
return next();
|
||||
return rewriteUserLiteral(req, currentUserLiteral, next);
|
||||
}
|
||||
if (req.accessToken && req.accessToken.id && !overwriteExistingToken) {
|
||||
// req.accessToken.id is defined, which means that some other middleware has identified a valid user.
|
||||
// when overwriteExistingToken is not set to a truthy value, skip searching for credentials.
|
||||
rewriteUserLiteral(req, currentUserLiteral);
|
||||
return next();
|
||||
return rewriteUserLiteral(req, currentUserLiteral, next);
|
||||
}
|
||||
// continue normal operation (as if req.accessToken was undefined)
|
||||
}
|
||||
|
||||
TokenModel.findForRequest(req, options, function(err, token) {
|
||||
req.accessToken = token || null;
|
||||
rewriteUserLiteral(req, currentUserLiteral);
|
||||
|
||||
var ctx = req.loopbackContext;
|
||||
if (ctx && ctx.active) ctx.set('accessToken', token);
|
||||
next(err);
|
||||
|
||||
if (err) return next(err);
|
||||
rewriteUserLiteral(req, currentUserLiteral, next);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -289,6 +289,26 @@ describe('loopback.token(options)', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should generate a 401 on a current user literal route without an authToken',
|
||||
function(done) {
|
||||
var app = createTestApp(null, done);
|
||||
request(app)
|
||||
.get('/users/me')
|
||||
.set('authorization', null)
|
||||
.expect(401)
|
||||
.end(done);
|
||||
});
|
||||
|
||||
it('should generate a 401 on a current user literal route with invalid authToken',
|
||||
function(done) {
|
||||
var app = createTestApp(this.token, done);
|
||||
request(app)
|
||||
.get('/users/me')
|
||||
.set('Authorization', 'invald-token-id')
|
||||
.expect(401)
|
||||
.end(done);
|
||||
});
|
||||
|
||||
it('should skip when req.token is already present', function(done) {
|
||||
var tokenStub = {id: 'stub id'};
|
||||
app.use(function(req, res, next) {
|
||||
|
|
Loading…
Reference in New Issue