From 8086c6f0bf1b4db7d6eccecc7c69fdb764e2e076 Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Tue, 2 Sep 2014 03:09:08 +0400 Subject: [PATCH 1/4] auth refactoring, part 1 moving stuff to auth.js basically + allowing usergroups --- lib/auth.js | 124 ++++++++++++++++++++++++++++++++++++++++++++++ lib/config.js | 51 +++---------------- lib/index.js | 29 ++++++----- lib/middleware.js | 55 +------------------- 4 files changed, 145 insertions(+), 114 deletions(-) create mode 100644 lib/auth.js diff --git a/lib/auth.js b/lib/auth.js new file mode 100644 index 000000000..92d73712d --- /dev/null +++ b/lib/auth.js @@ -0,0 +1,124 @@ +var Path = require('path') + , crypto = require('crypto') + , UError = require('./error').UError + +module.exports = Auth + +function Auth(config) { + if (!(this instanceof Auth)) return new Auth(config) + this.config = config + + if (config.users_file) { + this.HTPasswd = require('./htpasswd')( + Path.resolve( + Path.dirname(config.self_path), + config.users_file + ) + ) + } +} + +Auth.prototype.authenticate = function(user, password, cb) { + if (this.config.users != null && this.config.users[user] != null) { + // if user exists in this.users, verify password against it no matter what is in htpasswd + return cb(null, crypto.createHash('sha1').update(password).digest('hex') === this.config.users[user].password ? [user] : null) + } + + if (!this.HTPasswd) return cb(null, false) + this.HTPasswd.reload(function() { + cb(null, this.HTPasswd.verify(user, password) ? [user] : null) + }.bind(this)) +} + +Auth.prototype.add_user = function(user, password, cb) { + if (this.config.users && this.config.users[user]) return cb(new UError({ + status: 403, + message: 'this user already exists', + })) + + if (this.HTPasswd) { + if (this.max_users || this.max_users == null) { + var max_users = Number(this.max_users || Infinity) + this.HTPasswd.add_user(user, password, max_users, cb) + return + } + } + + return cb(new UError({ + status: 409, + message: 'registration is disabled', + })) +} + +Auth.prototype.middleware = function() { + var self = this + 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) + + // swallow error, user remains unauthorized + // set remoteUserError to indicate that user was attempting authentication + if (err) req.remote_user.error = err.message + return _next() + } + + if (req.remote_user != null) return next() + req.remote_user = AnonymousUser() + + var authorization = req.headers.authorization + if (authorization == null) return next() + + var parts = authorization.split(' ') + + if (parts.length !== 2) return next({ + status: 400, + message: 'bad authorization header', + }) + + var scheme = parts[0] + , credentials = new Buffer(parts[1], 'base64').toString() + , index = credentials.indexOf(':') + + if (scheme !== 'Basic' || index < 0) return next({ + status: 400, + message: 'bad authorization header', + }) + + var user = credentials.slice(0, index) + , pass = credentials.slice(index + 1) + + self.authenticate(user, pass, function(err, groups) { + if (err) return next(err) + if (groups != null && groups != false) { + req.remote_user = AuthenticatedUser(user, groups) + next() + } else { + req.remote_user = AnonymousUser() + next({ + status: 403, + message: 'bad username/password, access denied', + }) + } + }) + } +} + +function AnonymousUser() { + return { + name: undefined, + // groups without '@' are going to be deprecated eventually + groups: ['@all', '@anonymous', 'all', 'undefined', 'anonymous'], + } +} + +function AuthenticatedUser(name, groups) { + groups = groups.concat(['@all', '@authenticated', 'all']) + return { + name: name, + groups: groups, + } +} + diff --git a/lib/config.js b/lib/config.js index a634e20c0..9ce4055c9 100644 --- a/lib/config.js +++ b/lib/config.js @@ -113,31 +113,23 @@ 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 } function allow_action(package, who, action) { return (this.get_package_setting(package, action) || []).reduce(function(prev, curr) { - if (curr === String(who) || curr === 'all') return true + if (typeof(who) === 'string' && curr === who) return true + if (Array.isArray(who) && who.indexOf(curr) !== -1) return true return prev }, false) } Config.prototype.allow_access = function(package, user) { - return allow_action.call(this, package, user, 'allow_access') || allow_action.call(this, package, user, 'access') + return allow_action.call(this, package, user.groups, 'allow_access') || allow_action.call(this, package, user, 'access') } Config.prototype.allow_publish = function(package, user) { - return allow_action.call(this, package, user, 'allow_publish') || allow_action.call(this, package, user, 'publish') + return allow_action.call(this, package, user.groups, 'allow_publish') || allow_action.call(this, package, user, 'publish') } Config.prototype.proxy_access = function(package, uplink) { @@ -145,7 +137,8 @@ Config.prototype.proxy_access = function(package, uplink) { } Config.prototype.proxy_publish = function(package, uplink) { - return allow_action.call(this, package, uplink, 'proxy_publish') + throw new Error('deprecated') + //return allow_action.call(this, package, uplink, 'proxy_publish') } Config.prototype.get_package_setting = function(package, setting) { @@ -157,38 +150,6 @@ Config.prototype.get_package_setting = function(package, setting) { return undefined } -Config.prototype.authenticate = function(user, password, cb) { - if (this.users != null && this.users[user] != null) { - // if user exists in this.users, verify password against it no matter what is in htpasswd - return cb(null, crypto.createHash('sha1').update(password).digest('hex') === this.users[user].password) - } - - if (!this.HTPasswd) return cb(null, false) - this.HTPasswd.reload(function() { - cb(null, this.HTPasswd.verify(user, password)) - }.bind(this)) -} - -Config.prototype.add_user = function(user, password, cb) { - if (this.users && this.users[user]) return cb(new UError({ - status: 403, - message: 'this user already exists', - })) - - if (this.HTPasswd) { - if (this.max_users || this.max_users == null) { - var max_users = Number(this.max_users || Infinity) - this.HTPasswd.add_user(user, password, max_users, cb) - return - } - } - - return cb(new UError({ - status: 409, - message: 'registration is disabled', - })) -} - module.exports = Config var parse_interval_table = { diff --git a/lib/index.js b/lib/index.js index 48ce0b2cf..3c83366f5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,7 +7,6 @@ var express = require('express') , Middleware = require('./middleware') , Logger = require('./logger') , Cats = require('./status-cats') - , basic_auth = Middleware.basic_auth , validate_name = Middleware.validate_name , media = Middleware.media , expect_json = Middleware.expect_json @@ -16,6 +15,7 @@ var express = require('express') , localList = require('./local-list') , search = require('./search') , marked = require('marked') + , Auth = require('./auth') function match(regexp) { return function(req, res, next, value, name) { @@ -29,18 +29,19 @@ function match(regexp) { module.exports = function(config_hash) { var config = new Config(config_hash) - , storage = new Storage(config); + , storage = new Storage(config) + , auth = new Auth(config) search.configureStorage(storage); var can = function(action) { return function(req, res, next) { - if (config['allow_'+action](req.params.package, req.remoteUser)) { + if (config['allow_'+action](req.params.package, req.remote_user)) { next() } else { - if (!req.remoteUser) { - if (req.remoteUserError) { - var message = "can't "+action+' restricted package, ' + req.remoteUserError + if (!req.remote_user.name) { + if (req.remote_user.error) { + var message = "can't "+action+' restricted package, ' + req.remote_user.error } else { var message = "can't "+action+" restricted package without auth, did you forget 'npm set always-auth true'?" } @@ -51,7 +52,7 @@ module.exports = function(config_hash) { } else { next(new UError({ status: 403, - message: 'user '+req.remoteUser+' not allowed to '+action+' it' + message: 'user '+req.remote_user.name+' not allowed to '+action+' it' })) } } @@ -94,9 +95,7 @@ module.exports = function(config_hash) { next() }) app.use(Cats.middleware) - app.use(basic_auth(function(user, pass, cb) { - config.authenticate(user, pass, cb) - })) + app.use(auth.middleware()) app.use(express.json({strict: false, limit: config.max_body_size || '10mb'})) app.use(express.compress()) app.use(Middleware.anti_loop(config)) @@ -190,7 +189,7 @@ module.exports = function(config_hash) { storage.search(req.param.startkey || 0, {req: req}, function(err, result) { if (err) return next(err) for (var pkg in result) { - if (!config.allow_access(pkg, req.remoteUser)) { + if (!config.allow_access(pkg, req.remote_user)) { delete result[pkg] } } @@ -215,15 +214,15 @@ module.exports = function(config_hash) { app.get('/-/user/:org_couchdb_user', function(req, res, next) { res.status(200) return res.send({ - ok: 'you are authenticated as "' + req.remoteUser + '"', + ok: 'you are authenticated as "' + req.remote_user.name + '"', }) }) app.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req, res, next) { - if (req.remoteUser != null) { + if (req.remote_user.name != null) { res.status(201) return res.send({ - ok: 'you are authenticated as "' + req.remoteUser + '"', + ok: 'you are authenticated as "' + req.remote_user.name + '"', }) } else { if (typeof(req.body.name) !== 'string' || typeof(req.body.password) !== 'string') { @@ -232,7 +231,7 @@ module.exports = function(config_hash) { message: 'user/password is not found in request (npm issue?)', })) } - config.add_user(req.body.name, req.body.password, function(err) { + auth.add_user(req.body.name, req.body.password, function(err) { if (err) { if (err.status < 500 && err.message === 'this user already exists') { // with npm registering is the same as logging in diff --git a/lib/middleware.js b/lib/middleware.js index 088db2f7b..60339a0ba 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -40,59 +40,6 @@ module.exports.expect_json = function expect_json(req, res, next) { 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) - - // swallow error, user remains unauthorized - // set remoteUserError to indicate that user was attempting authentication - if (err) req.remoteUserError = err.message - return _next() - } - - var authorization = req.headers.authorization - - if (req.remoteUser != null) return next() - if (authorization == null) return next() - - var parts = authorization.split(' ') - - if (parts.length !== 2) return next({ - status: 400, - message: 'bad authorization header', - }) - - var scheme = parts[0] - , credentials = new Buffer(parts[1], 'base64').toString() - , index = credentials.indexOf(':') - - if (scheme !== 'Basic' || index < 0) return next({ - status: 400, - message: 'bad authorization header', - }) - - var user = credentials.slice(0, index) - , pass = credentials.slice(index + 1) - - callback(user, pass, function(err, is_ok) { - if (err) return next(err) - if (is_ok) { - req.remoteUser = user - next() - } else { - next({ - status: 403, - message: 'bad username/password, access denied', - }) - } - }) - } -} - module.exports.anti_loop = function(config) { return function(req, res, next) { if (req.headers.via != null) { @@ -187,7 +134,7 @@ module.exports.log_and_etagify = function(req, res, next) { req.log.warn({ request: {method: req.method, url: req.url}, level: 35, // http - user: req.remoteUser, + user: req.remote_user.name, status: res.statusCode, error: res._sinopia_error, bytes: { From 86394b25ee627b02a2c3206c8f3567a7e063daac Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Tue, 2 Sep 2014 04:26:54 +0400 Subject: [PATCH 2/4] undefined variable fix --- lib/storage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/storage.js b/lib/storage.js index 307ba6dfb..5a019b582 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -426,7 +426,7 @@ Storage.prototype.get_local = function(callback) { self.local.get_package(locals[i], function(err, info) { if (!err) { var latest = info['dist-tags'].latest; - packages.push(info.versions[latest]); + if (info.versions[latest]) packages.push(info.versions[latest]); } if (i >= locals.length - 1) { From 430a4791139d309146d4ee8d146f5a5777ebe77a Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Tue, 2 Sep 2014 04:27:04 +0400 Subject: [PATCH 3/4] auth refactoring: part 2 auth plugins --- lib/auth.js | 156 ++++++++++++++++++++++++++++++++------------ lib/config.js | 5 -- lib/config_def.yaml | 19 +++--- 3 files changed, 124 insertions(+), 56 deletions(-) diff --git a/lib/auth.js b/lib/auth.js index 92d73712d..5138cc040 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -1,53 +1,126 @@ var Path = require('path') , crypto = require('crypto') - , UError = require('./error').UError + , UError = require('./error').UserError + , Logger = require('./logger') + , assert = require('assert') module.exports = Auth function Auth(config) { if (!(this instanceof Auth)) return new Auth(config) this.config = config + this.logger = Logger.logger.child({sub: 'auth'}) + var stuff = { + config: config, + logger: this.logger, + } if (config.users_file) { - this.HTPasswd = require('./htpasswd')( - Path.resolve( - Path.dirname(config.self_path), - config.users_file - ) - ) - } -} - -Auth.prototype.authenticate = function(user, password, cb) { - if (this.config.users != null && this.config.users[user] != null) { - // if user exists in this.users, verify password against it no matter what is in htpasswd - return cb(null, crypto.createHash('sha1').update(password).digest('hex') === this.config.users[user].password ? [user] : null) - } - - if (!this.HTPasswd) return cb(null, false) - this.HTPasswd.reload(function() { - cb(null, this.HTPasswd.verify(user, password) ? [user] : null) - }.bind(this)) -} - -Auth.prototype.add_user = function(user, password, cb) { - if (this.config.users && this.config.users[user]) return cb(new UError({ - status: 403, - message: 'this user already exists', - })) - - if (this.HTPasswd) { - if (this.max_users || this.max_users == null) { - var max_users = Number(this.max_users || Infinity) - this.HTPasswd.add_user(user, password, max_users, cb) - return + if (!config.auth || !config.auth.htpasswd) { + // b/w compat + config.auth = config.auth || {} + config.auth.htpasswd = {file: config.users_file} } } - return cb(new UError({ - status: 409, - message: 'registration is disabled', - })) + this.plugins = Object.keys(config.auth || {}).map(function(p) { + var plugin, name + try { + name = 'sinopia-' + p + plugin = require(name) + } catch(x) { + try { + name = p + plugin = require(name) + } catch(x) {} + } + + if (plugin == null) { + throw Error('"' + p + '" auth plugin not found\n' + + 'try "npm install sinopia-' + p + '"') + } + + if (typeof(plugin) !== 'function') + throw Error('"' + name + '" doesn\'t look like a valid auth plugin') + + plugin = plugin(config.auth[p], stuff) + + if (plugin == null || typeof(plugin.authenticate) !== 'function') + throw Error('"' + name + '" doesn\'t look like a valid auth plugin') + + return plugin + }) + + this.plugins.unshift({ + authenticate: function(user, password, cb) { + if (config.users != null + && config.users[user] != null + && (crypto.createHash('sha1').update(password).digest('hex') + === config.users[user].password) + ) { + return cb(null, [ user ]) + } + + return cb() + }, + + adduser: function(user, password, cb) { + if (config.users && config.users[user]) return cb(new UError({ + status: 403, + message: 'this user already exists', + })) + + return cb() + }, + }) + + this.plugins.push({ + authenticate: function(user, password, cb) { + return cb(new UError({ + status: 403, + message: 'bad username/password, access denied', + })) + }, + + adduser: function(user, password, cb) { + return cb(new UError({ + status: 409, + message: 'registration is disabled', + })) + }, + }) +} + +Auth.prototype.authenticate = function(user, password, cb) { + var plugins = this.plugins.slice(0) + + !function next() { + var p = plugins.shift() + p.authenticate(user, password, function(err, groups) { + if (err || groups) return cb(err, groups) + next() + }) + }() +} + +Auth.prototype.add_user = function(user, password, cb) { + var plugins = this.plugins.slice(0) + + !function next() { + var p = plugins.shift() + var n = 'adduser' + if (typeof(p[n]) !== 'function') { + n = 'add_user' + } + if (typeof(p[n]) !== 'function') { + next() + } else { + p[n](user, password, function(err, ok) { + if (err || ok) return cb(err, ok) + next() + }) + } + }() } Auth.prototype.middleware = function() { @@ -90,17 +163,14 @@ 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) return next(err) - if (groups != null && groups != false) { + if (!err && groups != null && groups != false) { req.remote_user = AuthenticatedUser(user, groups) next() } else { req.remote_user = AnonymousUser() - next({ - status: 403, - message: 'bad username/password, access denied', - }) + next(err) } }) } diff --git a/lib/config.js b/lib/config.js index 9ce4055c9..7176e456b 100644 --- a/lib/config.js +++ b/lib/config.js @@ -75,11 +75,6 @@ function Config(config) { Array.isArray(hash[action]) , 'CONFIG: bad "'+i+'" package '+action+' description (array or string expected)') hash[action] = flatten(hash[action]) - hash[action].forEach(function(user) { - assert( - users[user] != null - , 'CONFIG: "'+i+'" package: user "'+user+'" doesn\'t exist') - }) } for (var i in this.packages) { diff --git a/lib/config_def.yaml b/lib/config_def.yaml index 308498398..803826c5b 100644 --- a/lib/config_def.yaml +++ b/lib/config_def.yaml @@ -12,11 +12,12 @@ users: title: Sinopia # logo: logo.png -users_file: ./htpasswd - -# Maximum amount of users allowed to register, defaults to "+inf". -# You can set this to 0 to disable registration. -#max_users: 1000 +auth: + htpasswd: + users_file: ./htpasswd + # Maximum amount of users allowed to register, defaults to "+inf". + # You can set this to 0 to disable registration. + #max_users: 1000 # a list of other known repositories we can talk to uplinks: @@ -51,9 +52,11 @@ packages: # storage: 'local_storage' '*': - # allow all users to read packages ('all' is a keyword) - # this includes non-authenticated users - allow_access: all + # allow all users to read packages (including non-authenticated users) + # + # you can specify usernames/groupnames (depending on your auth plugin) + # and three keywords: "@all", "@anonymous", "@authenticated" + allow_access: @all # allow 'admin' to publish packages allow_publish: admin From 734565dc054b02d1bea3a327501216671c34d9d3 Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Sat, 6 Sep 2014 13:03:22 +0400 Subject: [PATCH 4/4] auth refactoring: part 3 moving htpasswd out --- lib/auth.js | 1 - lib/htpasswd.js | 141 ------------------------------------------ package.yaml | 6 +- test/unit/htpasswd.js | 56 ----------------- 4 files changed, 2 insertions(+), 202 deletions(-) delete mode 100644 lib/htpasswd.js delete mode 100644 test/unit/htpasswd.js 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/) - }) -}) -