-
-
Notifications
You must be signed in to change notification settings - Fork 135
Adds parseUser option to control user parsing behavior #274
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,123 +53,105 @@ module.exports.parseError = function parseError(err, kwargs, cb) { | |
}); | ||
}; | ||
|
||
module.exports.parseRequest = function parseRequest(req, kwargs) { | ||
kwargs = kwargs || {}; | ||
module.exports.parseRequest = function parseRequest(req, parseUser) { | ||
var kwargs = {}; | ||
|
||
// headers: | ||
// | ||
// node: req.headers | ||
// express: req.headers | ||
// node, express: req.headers | ||
// koa: req.header | ||
// | ||
var headers = req.headers || req.header || {}; | ||
|
||
// method: | ||
// | ||
// node: req.method | ||
// express: req.method | ||
// koa: req.method | ||
// | ||
// node, express, koa: req.method | ||
var method = req.method; | ||
|
||
// host: | ||
// | ||
// node: req.headers.host | ||
// express: req.hostname in > 4 and req.host in < 4 | ||
// koa: req.host | ||
// | ||
// node: req.headers.host | ||
var host = req.hostname || req.host || headers.host || '<no host>'; | ||
|
||
// protocol: | ||
// | ||
// node: <n/a> | ||
// express: req.protocol | ||
// koa: req.protocol | ||
// | ||
// express, koa: req.protocol | ||
var protocol = req.protocol === 'https' || req.secure || (req.socket || {}).encrypted ? 'https' : 'http'; | ||
|
||
// url (including path and query string): | ||
// | ||
// node: req.originalUrl | ||
// express: req.originalUrl | ||
// node, express: req.originalUrl | ||
// koa: req.url | ||
// | ||
var originalUrl = req.originalUrl || req.url; | ||
|
||
// absolute url | ||
var url = protocol + '://' + host + originalUrl; | ||
var absoluteUrl = protocol + '://' + host + originalUrl; | ||
|
||
// query string | ||
// | ||
// query string: | ||
// node: req.url (raw) | ||
// express: req.query | ||
// koa: req.query | ||
// | ||
// express, koa: req.query | ||
var query = req.query || urlParser.parse(originalUrl || '', true).query; | ||
|
||
// cookies: | ||
// | ||
// node: req.headers.cookie | ||
// express: req.headers.cookie | ||
// koa: req.headers.cookie | ||
// | ||
// node, express, koa: req.headers.cookie | ||
var cookies = cookie.parse(headers.cookie || ''); | ||
|
||
// body data: | ||
// | ||
// node: req.body | ||
// express: req.body | ||
// koa: req.body | ||
// | ||
// node, express, koa: req.body | ||
var data = req.body; | ||
if (['GET', 'HEAD'].indexOf(method) === -1) { | ||
if (typeof data === 'undefined') { | ||
data = '<unavailable>'; | ||
} | ||
} | ||
|
||
if (data && {}.toString.call(data) !== '[object String]') { | ||
if (data && typeof data !== 'string' && {}.toString.call(data) !== '[object String]') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why add this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
// Make sure the request body is a string | ||
data = stringify(data); | ||
} | ||
|
||
// client ip: | ||
// | ||
// node: req.connection.remoteAddress | ||
// express: req.ip | ||
// koa: req.ip | ||
// | ||
var ip = req.ip || (req.connection || {}).remoteAddress; | ||
|
||
// http interface | ||
var http = { | ||
method: method, | ||
query_string: query, | ||
headers: headers, | ||
cookies: cookies, | ||
data: data, | ||
url: url | ||
url: absoluteUrl | ||
}; | ||
|
||
// expose http interface | ||
kwargs.request = http; | ||
|
||
// user | ||
// | ||
// typically found on req.user according to Express and Passport | ||
|
||
var user = {}; | ||
if (!kwargs.user) { | ||
if (req.user) { | ||
// shallow copy is okay because we are only modifying top-level | ||
// object (req.user) | ||
for (var key in req.user) { | ||
if ({}.hasOwnProperty.call(req.user, key)) { | ||
user[key] = req.user[key]; | ||
// user: typically found on req.user in express/passport patterns | ||
// five cases for parseUser value: | ||
// absent: grab only id, username, email from req.user | ||
// false: capture nothing | ||
// true: capture all keys from req.user | ||
// array: provided whitelisted keys to grab from req.user | ||
// function :: req -> user: custom parsing function | ||
if (parseUser == null) parseUser = ['id', 'username', 'email']; | ||
if (parseUser) { | ||
var user = {}; | ||
if (typeof parseUser === 'function') { | ||
user = parseUser(req); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason we pass (I mean, this is probably more flexible, but want to get your take.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my mind the function option exists primarily as a catch-all for "well what if it's not at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
} else if (req.user) { | ||
if (parseUser === true) { | ||
for (var key in req.user) { | ||
if ({}.hasOwnProperty.call(req.user, key)) { | ||
user[key] = req.user[key]; | ||
} | ||
} | ||
} else { | ||
parseUser.forEach(function (fieldName) { | ||
if ({}.hasOwnProperty.call(req.user, fieldName)) { | ||
user[fieldName] = req.user[fieldName]; | ||
} | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this looks good to me but this is a big gnarly block of logic. consider labeling some of the control flow paths? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ehh...normally id agree but i did it that way at first and it felt like borderline overcommenting since each branch has a pretty straightforward condition check, so i made sure to describe the 5 cases together above instead |
||
} | ||
} | ||
|
||
// client ip: | ||
// node: req.connection.remoteAddress | ||
// express, koa: req.ip | ||
var ip = req.ip || req.connection && req.connection.remoteAddress; | ||
if (ip) { | ||
user.ip_address = ip; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -416,38 +416,80 @@ describe('raven.parsers', function () { | |
}); | ||
}); | ||
|
||
it('should NOT assign req.user if already present in kwargs', function () { | ||
it('should add ip address to user if available', function () { | ||
var mockReq = { | ||
method: 'POST', | ||
hostname: 'example.org', | ||
url: '/some/path?key=value', | ||
ip: '127.0.0.1', | ||
user: { | ||
username: 'janedoe', | ||
email: 'hello@janedoe.com' | ||
} | ||
}; | ||
|
||
var parsed = raven.parsers.parseRequest(mockReq, { user: {} }); | ||
parsed.should.have.property('user', {}); | ||
var parsed = raven.parsers.parseRequest(mockReq); | ||
parsed.should.have.property('user', { | ||
username: 'janedoe', | ||
email: 'hello@janedoe.com', | ||
ip_address: '127.0.0.1' | ||
}); | ||
}); | ||
|
||
it('should add ip address to user if available', function () { | ||
describe('parseUser option', function () { | ||
var mockReq = { | ||
method: 'POST', | ||
hostname: 'example.org', | ||
url: '/some/path?key=value', | ||
ip: '127.0.0.1', | ||
user: { | ||
username: 'janedoe', | ||
email: 'hello@janedoe.com' | ||
email: 'hello@janedoe.com', | ||
random: 'random' | ||
} | ||
}; | ||
|
||
var parsed = raven.parsers.parseRequest(mockReq); | ||
parsed.should.have.property('user', { | ||
username: 'janedoe', | ||
email: 'hello@janedoe.com', | ||
ip_address: '127.0.0.1' | ||
it('should limit to default whitelisted fields', function () { | ||
var parsed = raven.parsers.parseRequest(mockReq); | ||
parsed.should.have.property('user', { | ||
username: 'janedoe', | ||
email: 'hello@janedoe.com', | ||
ip_address: '127.0.0.1' | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this check that random is -not- set? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, it checks that the user property has that exact value There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks |
||
}); | ||
|
||
it('should limit to provided whitelisted fields array', function () { | ||
var parsed = raven.parsers.parseRequest(mockReq, ['username', 'random']); | ||
parsed.should.have.property('user', { | ||
username: 'janedoe', | ||
random: 'random', | ||
ip_address: '127.0.0.1' | ||
}); | ||
}); | ||
|
||
it('should parse all fields when passed true', function () { | ||
var parsed = raven.parsers.parseRequest(mockReq, true); | ||
parsed.should.have.property('user', { | ||
username: 'janedoe', | ||
email: 'hello@janedoe.com', | ||
random: 'random', | ||
ip_address: '127.0.0.1' | ||
}); | ||
}); | ||
|
||
it('should parse nothing when passed false', function () { | ||
var parsed = raven.parsers.parseRequest(mockReq, false); | ||
parsed.should.not.have.property('user'); | ||
}); | ||
|
||
it('should take a custom parsing function', function () { | ||
var parsed = raven.parsers.parseRequest(mockReq, function (req) { | ||
return { user: req.user.username }; | ||
}); | ||
parsed.should.have.property('user', { | ||
user: 'janedoe', | ||
ip_address: '127.0.0.1' | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
parseRequest
a public/documented API method? If so, might want to consider how to make this signature more extendable going forward.e.g. I'd almost prefer if we kept
kwargs
and you did:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not public; only callsite is currently in
errorHandler
middleware, soon we'll add another inrequestHandler
and inprocess
but all internal. Thekwargs
param was unused so I got rid of it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k