diff --git a/lib/auth.js b/lib/auth.js index 5138cc040..2ac528d18 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -163,7 +163,6 @@ Auth.prototype.middleware = function() { var user = credentials.slice(0, index) , pass = credentials.slice(index + 1) -debugger self.authenticate(user, pass, function(err, groups) { if (!err && groups != null && groups != false) { req.remote_user = AuthenticatedUser(user, groups) diff --git a/lib/htpasswd.js b/lib/htpasswd.js deleted file mode 100644 index 50fa23bf7..000000000 --- a/lib/htpasswd.js +++ /dev/null @@ -1,141 +0,0 @@ -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 - } -} - -function add_user_to_htpasswd(body, user, passwd) { - if (user != encodeURIComponent(user)) { - throw new UError({ - status: 409, - message: "username shouldn't contain non-uri-safe characters", - }) - } - - passwd = crypt3(passwd) - if (!passwd) { - passwd = '{SHA}' + crypto.createHash('sha1').update(passwd, 'binary').digest('base64') - } - var comment = 'autocreated ' + (new Date()).toJSON() - - var newline = user + ':' + passwd + ':' + comment + '\n' - if (body.length && body[body.length-1] != '\n') newline = '\n' + newline - return body + newline -} - -module.exports = function(path) { - var result = {} - var users = {} - var last_time - - // hopefully race-condition-free way to add users: - // 1. lock file for writing (other processes can still read) - // 2. reload .htpasswd - // 3. write new data into .htpasswd.tmp - // 4. move .htpasswd.tmp to .htpasswd - // 5. reload .htpasswd - // 6. unlock file - result.add_user = function(user, passwd, maxusers, real_cb) { - function sanity_check() { - if (users[user]) { - return new UError({ - status: 403, - message: 'this user already exists', - }) - } else if (Object.keys(users).length >= maxusers) { - return new UError({ - status: 403, - message: 'maximum amount of users reached', - }) - } - } - - // preliminary checks, just to ensure that file won't be reloaded if it's not needed - var s_err = sanity_check() - if (s_err) return real_cb(s_err) - - fs_storage.lock_and_read(path, function(err, fd, res) { - // callback that cleanups fd first - function cb(err) { - if (!fd) return real_cb(err) - fs.close(fd, function() { - real_cb(err) - }) - } - - // ignore ENOENT errors, we'll just create .htpasswd in that case - if (err && err.code != 'ENOENT') return cb(err) - - var body = (res || '').toString('utf8') - users = parse_htpasswd(body) - - // real checks, to prevent race conditions - var s_err = sanity_check() - if (s_err) return cb(s_err) - - try { - body = add_user_to_htpasswd(body, user, passwd) - } catch(err) { - return cb(err) - } - fs_storage.write(path, body, function(err) { - if (err) return cb(err) - result.reload(cb) - }) - }) - } - 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 -} - -// exports for unit tests -module.exports._parse_htpasswd = parse_htpasswd -module.exports._verify_password = verify_password -module.exports._add_user_to_htpasswd = add_user_to_htpasswd diff --git a/package.yaml b/package.yaml index 745a71dea..b4205dabe 100644 --- a/package.yaml +++ b/package.yaml @@ -46,6 +46,7 @@ dependencies: onscroll: '>=0.0.3 <1.0.0-0' transition-complete: '>=0.0.2 <1.0.0-0' helpers.less: 'git://github.com/bpeacock/helpers.less.git' + sinopia-htpasswd: '>= 0.2.0' # TODO: not really needed tar.gz: '~0.1.1' @@ -55,9 +56,6 @@ optionalDependencies: # and unavailable on windows fs-ext: '>=0.3.2 <1.0.0-0' - # bundled - crypt3: '>=0.1.5 <1.0.0-0' - # Policy for now: # - small, unstable and rarely updated libraries (i.e. crypt3) get bundled # - big, stable and frequently updated libraries (i.e. request) don't @@ -72,7 +70,7 @@ bundledDependencies: - transition-complete - helpers.less - tar.gz - - crypt3 + - sinopia-htpasswd devDependencies: rimraf: '>=2.2.5 <3.0.0-0' diff --git a/test/unit/htpasswd.js b/test/unit/htpasswd.js deleted file mode 100644 index 98a7c3d5e..000000000 --- a/test/unit/htpasswd.js +++ /dev/null @@ -1,56 +0,0 @@ -var assert = require('assert') - , parse_htpasswd = require('../../lib/htpasswd')._parse_htpasswd - , verify_password = require('../../lib/htpasswd')._verify_password - , add_user_to_htpasswd = require('../../lib/htpasswd')._add_user_to_htpasswd - -describe('parse_htpasswd', function() { - // TODO -}) - -describe('verify_password', function() { - it('should verify plain', function() { - assert(verify_password('user', 'pass', '{PLAIN}pass')) - assert(!verify_password('user', 'p', '{PLAIN}pass')) - }) - it('should verify sha', function() { - assert(verify_password('user', 'pass', '{SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=')) - assert(!verify_password('user', 'p', '{SHA}nU4eI71bcnBGqeO0t9tXvY1u5oQ=')) - }) - it('should verify crypt', function() { - assert(verify_password('user', 'pass', 'ulINxGnqObi36')) - assert(!verify_password('user', 'p', 'ulINxGnqObi36')) - }) - it('should verify crypt-sha', function() { - assert(verify_password('user', 'pass', '$6$Qx0eNSKPbxocgA==$ugjO0.z9yOFiaJXJK4ulvGYIxF/KZBV4lGqasArYPqPPT4orZ6NlnIE5KhtiOVs.5EoWxLg1sjp318G8RpI2x1')) - assert(!verify_password('user', 'p', '$6$Qx0eNSKPbxocgA==$ugjO0.z9yOFiaJXJK4ulvGYIxF/KZBV4lGqasArYPqPPT4orZ6NlnIE5KhtiOVs.5EoWxLg1sjp318G8RpI2x1')) - }) -}) - -describe('add_user_to_htpasswd', function() { - it('should add user to empty file', function() { - var res = add_user_to_htpasswd('', 'user', 'passwd') - assert(res.match(/^user:[^:\n]+:autocreated [^\n]+\n$/)) - }) - - it('should append user / newline checks', function() { - var res = add_user_to_htpasswd('testtest', 'user', 'passwd') - assert(res.match(/^testtest\nuser:[^:\n]+:autocreated [^\n]+\n$/)) - var res = add_user_to_htpasswd('testtest\n', 'user', 'passwd') - assert(res.match(/^testtest\nuser:[^:\n]+:autocreated [^\n]+\n$/)) - var res = add_user_to_htpasswd('testtest\n\n', 'user', 'passwd') - assert(res.match(/^testtest\n\nuser:[^:\n]+:autocreated [^\n]+\n$/)) - }) - - it('should not append invalid users', function() { - assert.throws(function() { - add_user_to_htpasswd('', 'us:er', 'passwd') - }, /non-uri-safe/) - assert.throws(function() { - add_user_to_htpasswd('', 'us\ner', 'passwd') - }, /non-uri-safe/) - assert.throws(function() { - add_user_to_htpasswd('', 'us#er', 'passwd') - }, /non-uri-safe/) - }) -}) -