diff --git a/lib/config.js b/lib/config.js index 3452a56b3..34ada9d21 100644 --- a/lib/config.js +++ b/lib/config.js @@ -1,5 +1,6 @@ var assert = require('assert') , crypto = require('crypto') + , Path = require('path') , minimatch = require('minimatch') , utils = require('./utils') @@ -111,6 +112,15 @@ function Config(config) { if (this.ignore_latest_tag == null) this.ignore_latest_tag = false + if (this.users_file) { + this.HTPasswd = require('./htpasswd')( + Path.resolve( + Path.dirname(this.self_path), + this.users_file + ) + ) + } + return this } @@ -147,8 +157,15 @@ Config.prototype.get_package_setting = function(package, setting) { } Config.prototype.authenticate = function(user, password, cb) { - if (this.users[user] == null) return cb(null, false) - return cb(null, crypto.createHash('sha1').update(password).digest('hex') === this.users[user].password) + if (this.users != null) { + if (this.users[user] == null) return cb(null, false) + if (crypto.createHash('sha1').update(password).digest('hex') === this.users[user].password) return cb(null, true) + } + + if (!this.HTPasswd) return cb(null, false) + this.HTPasswd.reload(function() { + cb(null, this.HTPasswd.verify(user, password)) + }.bind(this)) } module.exports = Config diff --git a/lib/config_def.yaml b/lib/config_def.yaml index 125c9a517..c1bbf7cd9 100644 --- a/lib/config_def.yaml +++ b/lib/config_def.yaml @@ -7,6 +7,11 @@ users: # crypto.createHash('sha1').update(pass).digest('hex') password: __PASSWORD__ +users_file: ./htpasswd + +# set this to 0 to disable registration +#max_users: 1000 + # a list of other known repositories we can talk to uplinks: npmjs: diff --git a/lib/htpasswd.js b/lib/htpasswd.js new file mode 100644 index 000000000..337a9df12 --- /dev/null +++ b/lib/htpasswd.js @@ -0,0 +1,65 @@ +var fs = require('fs') + , crypto = require('crypto') + , fs_storage = require('./local-fs') + , UError = require('./error').UserError + +try { + // optional, won't be available on windows + var crypt3 = require('crypt3') +} catch(err) { + crypt3 = function() { + return NaN + } +} + +function parse_htpasswd(input) { + var result = {} + input.split('\n').forEach(function(line) { + var args = line.split(':', 3) + if (args.length > 1) result[args[0]] = args[1] + }) + return result +} + +function verify_password(user, passwd, hash) { + if (hash.indexOf('{PLAIN}') === 0) { + return passwd === hash.substr(7) + } else if (hash.indexOf('{SHA}') === 0) { + return crypto.createHash('sha1').update(passwd, 'binary').digest('base64') === hash.substr(5) + } else { + return crypt3(passwd, hash) === hash + } +} + +module.exports = function(path) { + var result = {} + var users = {} + var last_time + result.add_user = function(user, passwd, cb) { + // TODO + } + result.verify = function(user, passwd) { + if (!users[user]) return false + return verify_password(user, passwd, users[user]) + } + result.reload = function(callback) { + fs.open(path, 'r', function(err, fd) { + if (err) return callback(err) + + fs.fstat(fd, function(err, st) { + if (err) return callback(err) + if (last_time === st.mtime) return callback() + + var buffer = new Buffer(st.size) + fs.read(fd, buffer, 0, st.size, null, function(err, bytesRead, buffer) { + if (err) return callback(err) + if (bytesRead != st.size) return callback(new Error('st.size != bytesRead')) + users = parse_htpasswd(buffer.toString('utf8')) + callback() + }) + }) + }) + } + return result +} + diff --git a/lib/middleware.js b/lib/middleware.js index ca5d89a9f..ed19da2c5 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -42,7 +42,9 @@ module.exports.expect_json = function expect_json(req, res, next) { module.exports.basic_auth = function basic_auth(callback) { return function(req, res, _next) { + req.pause() function next(err) { + req.resume() // uncomment this to reject users with bad auth headers //return _next.apply(null, arguments) diff --git a/lib/storage.js b/lib/storage.js index aa82c9a05..9a2f3f748 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -339,6 +339,7 @@ Storage.prototype.search = function(startkey, options, callback) { } function merge_with_local_packages(err, res, body) { + if (err) return callback(err) var j = 0 self.local.get_recent_packages(startkey, function(err, list) { diff --git a/package.yaml b/package.yaml index f62fc05df..5f407d149 100644 --- a/package.yaml +++ b/package.yaml @@ -31,6 +31,7 @@ dependencies: optionalDependencies: fs-ext: '>= 0.3.2' + crypt3: 'sendanor/node-crypt3' devDependencies: rimraf: '>= 2.2.5'