Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
7a990d745c
|
@ -13,8 +13,9 @@ var Application = loopback.Application;
|
|||
assert(RoleMapping, 'RoleMapping model must be defined before Role model');
|
||||
|
||||
/**
|
||||
* The Role Model
|
||||
* The Role model
|
||||
* @class Role
|
||||
* @header Role object
|
||||
*/
|
||||
module.exports = function(Role) {
|
||||
|
||||
|
@ -139,7 +140,7 @@ module.exports = function(Role) {
|
|||
}
|
||||
|
||||
/*!
|
||||
* Check if two user ids matches
|
||||
* Check if two user IDs matches
|
||||
* @param {*} id1
|
||||
* @param {*} id2
|
||||
* @returns {boolean}
|
||||
|
@ -227,11 +228,12 @@ module.exports = function(Role) {
|
|||
});
|
||||
|
||||
/**
|
||||
* Check if the user id is authenticated
|
||||
* @param {Object} context The security context
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err
|
||||
* @param {Boolean} isAuthenticated
|
||||
* Check if the user ID is authenticated
|
||||
* @param {Object} context The security context.
|
||||
*
|
||||
* @callback {Function} callback Callback function.
|
||||
* @param {Error} err Error object.
|
||||
* @param {Boolean} isAuthenticated True if the user is authenticated.
|
||||
*/
|
||||
Role.isAuthenticated = function isAuthenticated(context, callback) {
|
||||
process.nextTick(function() {
|
||||
|
@ -252,13 +254,14 @@ module.exports = function(Role) {
|
|||
});
|
||||
|
||||
/**
|
||||
* Check if a given principal is in the role
|
||||
* Check if a given principal is in the specified role.
|
||||
*
|
||||
* @param {String} role The role name
|
||||
* @param {Object} context The context object
|
||||
* @callback {Function} callback
|
||||
* @param {Error} err
|
||||
* @param {Boolean} isInRole
|
||||
* @param {String} role The role name.
|
||||
* @param {Object} context The context object.
|
||||
*
|
||||
* @callback {Function} callback Callback function.
|
||||
* @param {Error} err Error object.
|
||||
* @param {Boolean} isInRole True if the principal is in the specified role.
|
||||
*/
|
||||
Role.isInRole = function(role, context, callback) {
|
||||
if (!(context instanceof AccessContext)) {
|
||||
|
@ -343,13 +346,12 @@ module.exports = function(Role) {
|
|||
};
|
||||
|
||||
/**
|
||||
* List roles for a given principal
|
||||
* @param {Object} context The security context
|
||||
* @param {Function} callback
|
||||
* List roles for a given principal.
|
||||
* @param {Object} context The security context.
|
||||
*
|
||||
* @callback {Function} callback
|
||||
* @param {Error=} err
|
||||
* @param {String[]} roles An array of role ids
|
||||
* @callback {Function} callback Callback function.
|
||||
* @param {Error} err Error object.
|
||||
* @param {String[]} roles An array of role IDs
|
||||
*/
|
||||
Role.getRoles = function(context, callback) {
|
||||
if (!(context instanceof AccessContext)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue