diff --git a/lib/auth.js b/lib/auth.js index d840b7093..ebe477612 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -11,7 +11,7 @@ function Auth(config) { var self = Object.create(Auth.prototype) self.config = config self.logger = Logger.logger.child({ sub: 'auth' }) - self.secret = config.secret || Crypto.pseudoRandomBytes(32) + self.secret = config.secret var stuff = { config: config, @@ -152,11 +152,17 @@ Auth.prototype.basic_middleware = function() { return next( Error[400]('bad authorization header') ) var scheme = parts[0] - var credentials = Buffer(parts[1], 'base64').toString() - var index = credentials.indexOf(':') - - if (scheme !== 'Basic' || index < 0) + if (scheme === 'Basic') { + var credentials = Buffer(parts[1], 'base64').toString() + } else if (scheme === 'Bearer') { + var credentials = self.aes_decrypt(Buffer(parts[1], 'base64')).toString('utf8') + if (!credentials) return next() + } else { return next() + } + + var index = credentials.indexOf(':') + if (index < 0) return next() var user = credentials.slice(0, index) var pass = credentials.slice(index + 1) @@ -229,15 +235,33 @@ Auth.prototype.cookie_middleware = function() { var token = req.cookies.get('token') if (token == null) return next() - try { - var user = self.decode_token(token, 60*60 /* 1 hour */) + /*try { + var user = self.decode_token(token, 60*60) } catch(err) { return next() } req.remote_user = AuthenticatedUser(user.u, user.g) req.remote_user.token = token - next() + next()*/ + var credentials = self.aes_decrypt(Buffer(token, 'base64')).toString('utf8') + if (!credentials) return next() + + var index = credentials.indexOf(':') + if (index < 0) return next() + + var user = credentials.slice(0, index) + var pass = credentials.slice(index + 1) + + self.authenticate(user, pass, function(err, user) { + if (!err) { + req.remote_user = user + next() + } else { + req.remote_user = AnonymousUser() + next(err) + } + }) } } @@ -276,6 +300,24 @@ Auth.prototype.decode_token = function(str, expire_time) { return data } +Auth.prototype.aes_encrypt = function(buf) { + var c = Crypto.createCipher('aes192', this.secret) + var b1 = c.update(buf) + var b2 = c.final() + return Buffer.concat([ b1, b2 ]) +} + +Auth.prototype.aes_decrypt = function(buf) { + try { + var c = Crypto.createDecipher('aes192', this.secret) + var b1 = c.update(buf) + var b2 = c.final() + } catch(_) { + return Buffer(0) + } + return Buffer.concat([ b1, b2 ]) +} + function AnonymousUser() { return { name: undefined, diff --git a/lib/config.js b/lib/config.js index fd73badd2..ea04e10c7 100644 --- a/lib/config.js +++ b/lib/config.js @@ -3,7 +3,7 @@ var Crypto = require('crypto') var Error = require('http-errors') var minimatch = require('minimatch') var Path = require('path') -var LocalList = require('./local-list') +var LocalData = require('./local-data') var Utils = require('./utils') // [[a, [b, c]], d] -> [a, b, c, d] @@ -29,12 +29,21 @@ function Config(config) { assert.equal(typeof(config), 'object', 'CONFIG: it doesn\'t look like a valid config file') assert(self.storage, 'CONFIG: storage path not defined') - self.localList = LocalList( + self.localList = LocalData( Path.join( Path.resolve(Path.dirname(self.self_path), self.storage), '.sinopia-db.json' ) ) + if (!self.secret) { + self.secret = self.localList.data.secret + + if (!self.secret) { + self.secret = Crypto.pseudoRandomBytes(32).toString('hex') + self.localList.data.secret = self.secret + self.localList.sync() + } + } var users = {all:true, anonymous:true, 'undefined':true, owner:true, none:true} diff --git a/lib/index-api.js b/lib/index-api.js index b79392c3b..ae84bd7c4 100644 --- a/lib/index-api.js +++ b/lib/index-api.js @@ -29,7 +29,7 @@ module.exports = function(config, auth, storage) { app.param('anything', match(/.*/)) app.use(auth.basic_middleware()) - app.use(auth.bearer_middleware()) + //app.use(auth.bearer_middleware()) app.use(expressJson5({ strict: false, limit: config.max_body_size || '10mb' })) app.use(Middleware.anti_loop(config)) @@ -113,11 +113,15 @@ module.exports = function(config, auth, storage) { }) app.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req, res, next) { + var token = (req.body.name && req.body.password) + ? auth.aes_encrypt(req.body.name + ':' + req.body.password).toString('base64') + : undefined if (req.remote_user.name != null) { res.status(201) return next({ ok: "you are authenticated as '" + req.remote_user.name + "'", - token: auth.issue_token(req.remote_user), + //token: auth.issue_token(req.remote_user), + token: token, }) } else { if (typeof(req.body.name) !== 'string' || typeof(req.body.password) !== 'string') { @@ -141,7 +145,8 @@ module.exports = function(config, auth, storage) { res.status(201) return next({ ok: "user '" + req.body.name + "' created", - token: auth.issue_token(req.remote_user), + //token: auth.issue_token(req.remote_user), + token: token, }) }) } diff --git a/lib/index-web.js b/lib/index-web.js index f3261bd2d..6fd8ee105 100644 --- a/lib/index-web.js +++ b/lib/index-web.js @@ -80,7 +80,10 @@ module.exports = function(config, auth, storage) { auth.authenticate(req.body.user, req.body.pass, function(err, user) { if (!err) { req.remote_user = user - res.cookies.set('token', auth.issue_token(req.remote_user)) + //res.cookies.set('token', auth.issue_token(req.remote_user)) + + var str = req.body.user + ':' + req.body.pass + res.cookies.set('token', auth.aes_encrypt(str).toString('base64')) } var base = config.url_prefix || req.protocol + '://' + req.get('host') diff --git a/lib/local-data.js b/lib/local-data.js new file mode 100644 index 000000000..5a5178945 --- /dev/null +++ b/lib/local-data.js @@ -0,0 +1,40 @@ +var fs = require('fs') + +module.exports = LocalData + +function LocalData(path) { + var self = Object.create(LocalData.prototype) + self.path = path + try { + self.data = JSON.parse(fs.readFileSync(self.path, 'utf8')) + } catch(_) { + self.data = { list: [] } + } + return self +} + +LocalData.prototype.add = function(name) { + if (this.data.list.indexOf(name) === -1) { + this.data.list.push(name) + this.sync() + } +} + +LocalData.prototype.remove = function(name) { + var i = this.data.list.indexOf(name) + if (i !== -1) { + this.data.list.splice(i, 1) + } + + this.sync() +} + +LocalData.prototype.get = function() { + return this.data.list +} + +LocalData.prototype.sync = function() { + // Uses sync to prevent ugly race condition + fs.writeFileSync(this.path, JSON.stringify(this.data)) +} + diff --git a/lib/local-list.js b/lib/local-list.js deleted file mode 100644 index 4e81d1ae0..000000000 --- a/lib/local-list.js +++ /dev/null @@ -1,40 +0,0 @@ -var fs = require('fs') - -module.exports = LocalList - -function LocalList(path) { - var self = Object.create(LocalList.prototype) - self.path = path - try { - self.list = JSON.parse(fs.readFileSync(self.path, 'utf8')).list - } catch(_) { - self.list = [] - } - return self -} - -LocalList.prototype.add = function(name) { - if (this.list.indexOf(name) === -1) { - this.list.push(name) - this.sync() - } -} - -LocalList.prototype.remove = function(name) { - var i = this.list.indexOf(name) - if (i !== -1) { - this.list.splice(i, 1) - } - - this.sync() -} - -LocalList.prototype.get = function() { - return this.list -} - -LocalList.prototype.sync = function() { - // Uses sync to prevent ugly race condition - fs.writeFileSync(this.path, JSON.stringify({ list: this.list })) -} -