0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-23 22:27:34 -05:00
verdaccio/lib/middleware.js

203 lines
4.8 KiB
JavaScript
Raw Normal View History

2013-10-26 07:18:36 -05:00
var crypto = require('crypto')
, utils = require('./utils')
, UError = require('./error').UserError
, Logger = require('./logger')
2013-06-07 20:16:28 -05:00
module.exports.validate_name = function validate_name(req, res, next, value, name) {
2014-02-01 03:08:48 -05:00
if (value.charAt(0) === '-') {
// special case in couchdb usually
next('route')
} else if (utils.validate_name(value)) {
2013-10-26 07:18:36 -05:00
next()
2013-06-07 20:16:28 -05:00
} else {
2013-06-14 03:34:29 -05:00
next(new UError({
2013-06-07 20:16:28 -05:00
status: 403,
msg: 'invalid ' + name,
2013-10-26 07:18:36 -05:00
}))
2013-06-07 20:16:28 -05:00
}
2013-10-26 07:18:36 -05:00
}
2013-06-07 20:16:28 -05:00
module.exports.media = function media(expect) {
return function(req, res, next) {
if (req.headers['content-type'] !== expect) {
2013-06-14 03:34:29 -05:00
next(new UError({
2013-06-07 20:16:28 -05:00
status: 415,
msg: 'wrong content-type, expect: '+expect+', got: '+req.headers['content-type'],
2013-10-26 07:18:36 -05:00
}))
2013-06-07 20:16:28 -05:00
} else {
2013-10-26 07:18:36 -05:00
next()
2013-06-07 20:16:28 -05:00
}
}
}
module.exports.expect_json = function expect_json(req, res, next) {
if (!utils.is_object(req.body)) {
2013-06-07 20:16:28 -05:00
return next({
status: 400,
msg: 'can\'t parse incoming json',
2013-10-26 07:18:36 -05:00
})
2013-06-07 20:16:28 -05:00
}
2013-10-26 07:18:36 -05:00
next()
2013-06-07 20:16:28 -05:00
}
module.exports.basic_auth = function basic_auth(callback) {
2013-12-06 12:46:51 -05:00
return function(req, res, _next) {
function next(err) {
// uncomment this to reject users with bad auth headers
//return _next.apply(null, arguments)
2013-12-29 01:41:31 -05:00
2013-12-06 12:46:51 -05:00
// swallow error, user remains unauthorized
// set remoteUserError to indicate that user was attempting authentication
if (err) req.remoteUserError = err.msg
2013-12-06 12:46:51 -05:00
return _next()
}
2013-10-26 07:18:36 -05:00
var authorization = req.headers.authorization
2013-06-07 20:16:28 -05:00
2013-12-06 12:46:51 -05:00
if (req.remoteUser != null) return next()
if (authorization == null) return next()
2013-06-07 20:16:28 -05:00
2013-10-26 07:18:36 -05:00
var parts = authorization.split(' ')
2013-06-07 20:16:28 -05:00
if (parts.length !== 2) return next({
status: 400,
msg: 'bad authorization header',
2013-10-26 07:18:36 -05:00
})
2013-06-07 20:16:28 -05:00
var scheme = parts[0]
2013-12-18 22:18:45 -05:00
, credentials = new Buffer(parts[1], 'base64').toString()
, index = credentials.indexOf(':')
2013-06-07 20:16:28 -05:00
2014-02-06 15:56:46 -05:00
if (scheme !== 'Basic' || index < 0) return next({
2013-06-07 20:16:28 -05:00
status: 400,
msg: 'bad authorization header',
2013-10-26 07:18:36 -05:00
})
2013-06-07 20:16:28 -05:00
var user = credentials.slice(0, index)
2013-12-18 22:18:45 -05:00
, pass = credentials.slice(index + 1)
2013-06-07 20:16:28 -05:00
if (callback(user, pass)) {
2013-12-06 12:46:51 -05:00
req.remoteUser = user
2013-10-26 07:18:36 -05:00
next()
2013-06-07 20:16:28 -05:00
} else {
next({
status: 403,
msg: 'bad username/password, access denied',
2013-10-26 07:18:36 -05:00
})
2013-06-07 20:16:28 -05:00
}
}
2013-10-26 07:18:36 -05:00
}
2013-12-08 22:59:31 -05:00
module.exports.anti_loop = function(config) {
return function(req, res, next) {
if (req.headers.via != null) {
var arr = req.headers.via.split(',')
for (var i=0; i<arr.length; i++) {
var m = arr[i].match(/\s*(\S+)\s+(\S+)/)
if (m && m[2] === config.server_id) {
return next(new UError({
status: 508,
msg: 'loop detected',
}))
}
}
}
next()
}
}
2013-06-07 20:16:28 -05:00
2013-07-02 20:49:24 -05:00
// express doesn't do etags with requests <= 1024b
// we use md5 here, it works well on 1k+ bytes, but sucks with fewer data
// could improve performance using crc32 after benchmarks
function md5sum(data) {
2013-10-26 07:18:36 -05:00
return crypto.createHash('md5').update(data).digest('hex')
2013-07-02 20:49:24 -05:00
}
module.exports.log_and_etagify = function(req, res, next) {
// logger
2013-10-26 07:18:36 -05:00
req.log = Logger.logger.child({sub: 'in'})
2013-10-12 09:37:47 -05:00
var _auth = req.headers.authorization
if (_auth) req.headers.authorization = '<Classified>'
req.log.info({req: req, ip: req.ip}, '@{ip} requested \'@{req.method} @{req.url}\'')
if (_auth) req.headers.authorization = _auth
2013-10-26 07:18:36 -05:00
var bytesin = 0
req.on('data', function(chunk){ bytesin += chunk.length })
2013-10-26 07:18:36 -05:00
var _send = res.send
2013-07-02 20:49:24 -05:00
res.send = function(body) {
2014-03-07 13:20:41 -05:00
try {
if (typeof(body) === 'string' || typeof(body) === 'object') {
res.header('Content-type', 'application/json')
if (typeof(body) === 'object' && body != null) {
if (body.error) {
res._sinopia_error = body.error
}
body = JSON.stringify(body, undefined, '\t') + '\n'
}
2013-07-02 20:49:24 -05:00
2014-03-07 13:20:41 -05:00
// don't send etags with errors
if (!res.statusCode || (res.statusCode >= 200 && res.statusCode < 300)) {
res.header('ETag', '"' + md5sum(body) + '"')
}
2014-03-07 13:20:41 -05:00
} else {
// send(null), send(204), etc.
2013-07-02 20:49:24 -05:00
}
2014-03-07 13:20:41 -05:00
} catch(err) {
// if sinopia sends headers first, and then calls res.send()
// as an error handler, we can't report error properly,
// and should just close socket
if (err.message.match(/set headers after they are sent/)) {
return res.socket.destroy()
} else {
throw err
2013-12-06 12:46:11 -05:00
}
2013-07-02 20:49:24 -05:00
}
2013-10-26 07:18:36 -05:00
res.send = _send
res.send(body)
2013-10-18 16:53:27 -05:00
}
var bytesout = 0
2013-10-26 07:18:36 -05:00
, _write = res.write
2013-10-18 16:53:27 -05:00
res.write = function(buf) {
bytesout += buf.length
_write.apply(res, arguments)
}
2013-10-22 04:37:28 -05:00
function log() {
2013-10-26 07:18:36 -05:00
var msg = '@{status}, user: @{user}, req: \'@{request.method} @{request.url}\''
2013-10-18 16:53:27 -05:00
if (res._sinopia_error) {
2013-10-26 07:18:36 -05:00
msg += ', error: @{!error}'
} else {
2013-10-26 07:18:36 -05:00
msg += ', bytes: @{bytes.in}/@{bytes.out}'
}
req.log.warn({
request: {method: req.method, url: req.url},
level: 35, // http
2013-12-27 06:29:23 -05:00
user: req.remoteUser,
status: res.statusCode,
2013-10-18 16:53:27 -05:00
error: res._sinopia_error,
bytes: {
in: bytesin,
2013-10-18 16:53:27 -05:00
out: bytesout,
}
2013-10-26 07:18:36 -05:00
}, msg)
2013-10-22 04:37:28 -05:00
}
req.on('close', function() {
log(true)
})
2013-10-26 07:18:36 -05:00
var _end = res.end
2013-10-22 04:37:28 -05:00
res.end = function(buf) {
if (buf) bytesout += buf.length
_end.apply(res, arguments)
log()
}
next()
2013-07-02 20:49:24 -05:00
}