Merge pull request #1169 from strongloop/feature/allow-current-user-literal

Allow 'me' literal to represent the current user in url
This commit is contained in:
Raymond Feng 2015-03-12 10:20:39 -07:00
commit 131633f50d
2 changed files with 99 additions and 5 deletions

View File

@ -4,6 +4,7 @@
var loopback = require('../../lib/loopback');
var assert = require('assert');
var debug = require('debug')('loopback:middleware:token');
/*!
* Export the middleware.
@ -11,6 +12,27 @@ var assert = require('assert');
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) {
// Replace /me/ with /current-user-id/
var urlBeforeRewrite = req.url;
req.url = req.url.replace(
new RegExp('/' + currentUserLiteral + '(/|$|\\?)', 'g'),
'/' + req.accessToken.userId + '$1');
if (req.url !== urlBeforeRewrite) {
debug('req.url has been rewritten from %s to %s', urlBeforeRewrite,
req.url);
}
}
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/**
* Check for an access token in cookies, headers, and query string parameters.
* This function always checks for the following:
@ -39,19 +61,37 @@ module.exports = token;
* @property {Array} [cookies] Array of cookie names.
* @property {Array} [headers] Array of header names.
* @property {Array} [params] Array of param names.
* @property {Array} [model] An AccessToken object to use.
* @property {Function|String} [model] AccessToken model name or class to use.
* @property {String} [currentUserLiteral] String literal for the current user.
* @header loopback.token([options])
*/
function token(options) {
options = options || {};
var TokenModel = options.model || loopback.AccessToken;
assert(TokenModel, 'loopback.token() middleware requires a AccessToken model');
if (typeof TokenModel === 'string') {
// Make it possible to configure the model in middleware.json
TokenModel = loopback.getModel(TokenModel);
}
var currentUserLiteral = options.currentUserLiteral;
if (currentUserLiteral && (typeof currentUserLiteral !== 'string')) {
debug('Set currentUserLiteral to \'me\' as the value is not a string.');
currentUserLiteral = 'me';
}
if (typeof currentUserLiteral === 'string') {
currentUserLiteral = escapeRegExp(currentUserLiteral);
}
assert(typeof TokenModel === 'function',
'loopback.token() middleware requires a AccessToken model');
return function(req, res, next) {
if (req.accessToken !== undefined) return next();
if (req.accessToken !== undefined) {
rewriteUserLiteral(req, currentUserLiteral);
return next();
}
TokenModel.findForRequest(req, options, function(err, token) {
req.accessToken = token || null;
rewriteUserLiteral(req, currentUserLiteral);
var ctx = loopback.getCurrentContext();
if (ctx) ctx.set('accessToken', token);
next(err);

View File

@ -108,6 +108,51 @@ describe('loopback.token(options)', function() {
});
});
it('should rewrite url for the current user literal at the end without query',
function(done) {
var app = createTestApp(this.token, done);
var id = this.token.id;
var userId = this.token.userId;
request(app)
.get('/users/me')
.set('authorization', id)
.end(function(err, res) {
assert(!err);
assert.deepEqual(res.body, {userId: userId});
done();
});
});
it('should rewrite url for the current user literal at the end with query',
function(done) {
var app = createTestApp(this.token, done);
var id = this.token.id;
var userId = this.token.userId;
request(app)
.get('/users/me?state=1')
.set('authorization', id)
.end(function(err, res) {
assert(!err);
assert.deepEqual(res.body, {userId: userId, state: 1});
done();
});
});
it('should rewrite url for the current user literal in the middle',
function(done) {
var app = createTestApp(this.token, done);
var id = this.token.id;
var userId = this.token.userId;
request(app)
.get('/users/me/1')
.set('authorization', id)
.end(function(err, res) {
assert(!err);
assert.deepEqual(res.body, {userId: userId, state: 1});
done();
});
});
it('should skip when req.token is already present', function(done) {
var tokenStub = { id: 'stub id' };
app.use(function(req, res, next) {
@ -284,7 +329,7 @@ describe('app.enableAuth()', function() {
function createTestingToken(done) {
var test = this;
Token.create({}, function(err, token) {
Token.create({userId: '123'}, function(err, token) {
if (err) return done(err);
test.token = token;
done();
@ -307,7 +352,7 @@ function createTestApp(testToken, settings, done) {
var app = loopback();
app.use(loopback.cookieParser('secret'));
app.use(loopback.token({model: Token}));
app.use(loopback.token({model: 'MyToken', currentUserLiteral: 'me'}));
app.get('/token', function(req, res) {
res.cookie('authorization', testToken.id, {signed: true});
res.end();
@ -321,6 +366,15 @@ function createTestApp(testToken, settings, done) {
}
res.send('ok');
});
app.use('/users/:uid', function(req, res) {
var result = {userId: req.params.uid};
if (req.query.state) {
result.state = req.query.state;
} else if (req.url !== '/') {
result.state = req.url.substring(1);
}
res.status(200).send(result);
});
app.use(loopback.rest());
app.enableAuth();