1
0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-02-21 07:29:37 +01:00

auth refactoring: part 2

auth plugins
This commit is contained in:
Alex Kocharin 2014-09-02 04:27:04 +04:00
parent 99795b747c
commit 6e247829a3
2 changed files with 124 additions and 51 deletions

@ -1,53 +1,126 @@
var Path = require('path') var Path = require('path')
, crypto = require('crypto') , crypto = require('crypto')
, UError = require('./error').UError , UError = require('./error').UserError
, Logger = require('./logger')
, assert = require('assert')
module.exports = Auth module.exports = Auth
function Auth(config) { function Auth(config) {
if (!(this instanceof Auth)) return new Auth(config) if (!(this instanceof Auth)) return new Auth(config)
this.config = config this.config = config
this.logger = Logger.logger.child({sub: 'auth'})
var stuff = {
config: config,
logger: this.logger,
}
if (config.users_file) { if (config.users_file) {
this.HTPasswd = require('./htpasswd')( if (!config.auth || !config.auth.htpasswd) {
Path.resolve( // b/w compat
Path.dirname(config.self_path), config.auth = config.auth || {}
config.users_file config.auth.htpasswd = {file: 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({ this.plugins = Object.keys(config.auth || {}).map(function(p) {
status: 409, var plugin, name
message: 'registration is disabled', 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() { Auth.prototype.middleware = function() {
@ -90,17 +163,14 @@ Auth.prototype.middleware = function() {
var user = credentials.slice(0, index) var user = credentials.slice(0, index)
, pass = credentials.slice(index + 1) , pass = credentials.slice(index + 1)
debugger
self.authenticate(user, pass, function(err, groups) { self.authenticate(user, pass, function(err, groups) {
if (err) return next(err) if (!err && groups != null && groups != false) {
if (groups != null && groups != false) {
req.remote_user = AuthenticatedUser(user, groups) req.remote_user = AuthenticatedUser(user, groups)
next() next()
} else { } else {
req.remote_user = AnonymousUser() req.remote_user = AnonymousUser()
next({ next(err)
status: 403,
message: 'bad username/password, access denied',
})
} }
}) })
} }

@ -12,11 +12,12 @@ users:
title: Sinopia title: Sinopia
# logo: logo.png # logo: logo.png
users_file: ./htpasswd auth:
htpasswd:
# Maximum amount of users allowed to register, defaults to "+inf". users_file: ./htpasswd
# You can set this to 0 to disable registration. # Maximum amount of users allowed to register, defaults to "+inf".
#max_users: 1000 # You can set this to 0 to disable registration.
#max_users: 1000
# a list of other known repositories we can talk to # a list of other known repositories we can talk to
uplinks: uplinks:
@ -51,9 +52,11 @@ packages:
# storage: 'local_storage' # storage: 'local_storage'
'*': '*':
# allow all users to read packages ('all' is a keyword) # allow all users to read packages (including non-authenticated users)
# this includes non-authenticated users #
allow_access: all # 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 'admin' to publish packages
allow_publish: admin allow_publish: admin