own-couchdb
Version:
users for usepages
404 lines (325 loc) • 9.85 kB
JavaScript
;
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var finalhandler = _interopDefault(require('finalhandler'));
var fs = require('fs');
var invariant = _interopDefault(require('invariant'));
var http = _interopDefault(require('http'));
var PouchDB = _interopDefault(require('pouchdb'));
var fetchAuth = _interopDefault(require('fetch-auth-node'));
var random = _interopDefault(require('random-gen'));
var Router = _interopDefault(require('router'));
var babelHelpers = {};
babelHelpers.asyncToGenerator = function (fn) {
return function () {
var gen = fn.apply(this, arguments);
return new Promise(function (resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(function (value) {
return step("next", value);
}, function (err) {
return step("throw", err);
});
}
}
return step("next");
});
};
};
babelHelpers.extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
babelHelpers;
function getServerConfig() {
var _JSON$parse = JSON.parse(fs.readFileSync(process.env.CONFIG || 'config.json', 'utf8'));
const auth = _JSON$parse.auth;
var _JSON$parse$db = _JSON$parse.db;
const db = _JSON$parse$db === undefined ? 'own' : _JSON$parse$db;
var _JSON$parse$endpoint = _JSON$parse.endpoint;
const endpoint = _JSON$parse$endpoint === undefined ? 'http://127.0.0.1:5984' : _JSON$parse$endpoint;
var _JSON$parse$port = _JSON$parse.port;
const port = _JSON$parse$port === undefined ? 5986 : _JSON$parse$port;
invariant(auth, 'missing couchdb auth header that can write to the own db');
invariant(endpoint, `missing couchdb endpoint's own db`);
invariant(endpoint, 'missing couchdb endpoint');
invariant(port, 'missing port');
return {
auth,
db,
endpoint,
port
};
}
function getDb(endpoint, db, Authorization) {
return new PouchDB(`${ endpoint }/${ db }`, { ajax: { headers: { Authorization } } });
}
const NOT_FOUND = 404;
var getAcl = (() => {
var ref = babelHelpers.asyncToGenerator(function* (req, res, next) {
const app = req.params.app;
const own = req.own;
const db = getDb(own.endpoint, own.db, own.auth);
let doc;
try {
doc = yield db.get(app);
} catch (err) {
if (err.status !== NOT_FOUND) {
res.statusCode = 401;
return res.end('{}');
}
}
req.own.acl = {
db,
doc
};
next();
});
function getAcl(_x, _x2, _x3) {
return ref.apply(this, arguments);
}
return getAcl;
})();
function hasRole(user, role) {
return user.roles.indexOf(role) !== -1;
}
const ADMIN = '_admin';
function isAdmin$1(user) {
return hasRole(user, ADMIN);
}
function isOwner$1(user, aclDoc) {
return hasRole(user, aclDoc._id) && aclDoc.admins.indexOf(user.name) !== -1;
}
function getCode(req, res) {
var _req$own = req.own;
const acl = _req$own.acl;
const userCtx = _req$own.userCtx;
if (typeof acl.doc !== 'undefined') {
if (isAdmin$1(userCtx) || isOwner$1(userCtx, acl.doc)) {
res.statusCode = 200;
res.end(JSON.stringify(acl.doc.code));
} else {
res.statusCode = 401;
res.end('{}');
}
} else {
res.statusCode = 404;
res.end('{}');
}
}
var getUserContext = (() => {
var ref = babelHelpers.asyncToGenerator(function* (req, res, next) {
const endpoint = req.own.endpoint;
try {
var _ref = yield fetchAuth(`${ endpoint }/_session`, req.headers.authorization);
const userCtx = _ref.userCtx;
req.own.userCtx = userCtx;
next();
} catch (err) {
console.error(err);
res.statusCode = 401;
res.end('{}');
}
});
function getUserContext(_x, _x2, _x3) {
return ref.apply(this, arguments);
}
return getUserContext;
})();
var join = (() => {
var ref = babelHelpers.asyncToGenerator(function* (req, res) {
var _req$params = req.params;
const app = _req$params.app;
const code = _req$params.code;
var _req$own = req.own;
const acl = _req$own.acl;
const auth = _req$own.auth;
const endpoint = _req$own.endpoint;
const userCtx = _req$own.userCtx;
const usersDb = getDb(endpoint, '_users', auth);
try {
if (acl.doc.code === code.toUpperCase()) {
const user = yield usersDb.get(`org.couchdb.user:${ userCtx.name }`);
if (!hasRole(user, app)) {
yield usersDb.put(babelHelpers.extends({}, user, {
roles: [...user.roles, app]
}));
}
res.statusCode = 200;
res.end('{}');
} else {
res.statusCode = 401;
res.end('{}');
}
} catch (err) {
res.statusCode = 500;
res.end('{}');
}
});
function join(_x, _x2) {
return ref.apply(this, arguments);
}
return join;
})();
function getRandomCode() {
let blacklist = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];
let code;
do {
code = random.upper(4);
} while (blacklist.indexOf(code) > -1);
return code;
}
var renewCode = (() => {
var ref = babelHelpers.asyncToGenerator(function* (req, res) {
var _req$own = req.own;
const acl = _req$own.acl;
const userCtx = _req$own.userCtx;
if (typeof acl.doc !== 'undefined') {
if (isAdmin$1(userCtx) || isOwner$1(userCtx, acl.doc)) {
try {
const blacklist = [acl.doc.code, ...acl.doc.blacklist];
const code = getRandomCode(blacklist);
yield acl.db.put(babelHelpers.extends({}, acl.doc, {
blacklist,
code
}));
res.statusCode = 200;
res.end(JSON.stringify(code));
} catch (err) {
console.error(err);
res.statusCode = 500;
res.end();
}
} else {
res.statusCode = 401;
res.end('{}');
}
} else {
res.statusCode = 404;
res.end('{}');
}
});
function renewCode(_x, _x2) {
return ref.apply(this, arguments);
}
return renewCode;
})();
var leave = (() => {
var ref = babelHelpers.asyncToGenerator(function* (req, res) {
var _req$own = req.own;
const acl = _req$own.acl;
const auth = _req$own.auth;
const endpoint = _req$own.endpoint;
const userCtx = _req$own.userCtx;
var _req$params = req.params;
const app = _req$params.app;
const email = _req$params.email;
const usersDb = getDb(endpoint, '_users', auth);
try {
if (isAdmin(userCtx) || isOwner(userCtx, acl.doc)) {
const user = yield usersDb.get(`org.couchdb.user:${ email }`);
if (hasRole(user, app)) {
yield usersDb.put(babelHelpers.extends({}, user, {
roles: user.roles.filter(function (r) {
return r !== app;
})
}));
yield renewCode(req, res);
}
} else {
res.statusCode = 401;
res.end('{}');
}
} catch (err) {
res.statusCode(500);
res.end('{}');
}
});
function leave(_x, _x2) {
return ref.apply(this, arguments);
}
return leave;
})();
function appToDbName(app) {
return app.replace(/\./g, '');
}
function createDesignDocForApp(app) {
return `function(newDoc, oldDoc, userCtx) {
if (userCtx.roles.indexOf("_admin") === -1 && userCtx.roles.indexOf("${ app }") === -1) {
throw({forbidden: true});
}
}`;
}
var own = (() => {
var ref = babelHelpers.asyncToGenerator(function* (req, res) {
var _req$own = req.own;
const acl = _req$own.acl;
const auth = _req$own.auth;
const endpoint = _req$own.endpoint;
const userCtx = _req$own.userCtx;
const app = req.params.app;
if (typeof acl.doc === 'undefined') {
try {
// create a design doc that will only allow users on the group to edit stuff
yield getDb(endpoint, appToDbName(app), auth).put({
_id: `_design/only-${ app }`,
validate_doc_update: createDesignDocForApp(app)
});
// set the admins on the ACL doc
const code = getRandomCode();
yield acl.db.put({
_id: app,
admins: [userCtx.name],
blacklist: [],
code
});
req.own.acl.doc = yield acl.db.get(app);
req.params.code = code;
// allow the user to access the app
yield join(req, res);
} catch (err) {
console.error('own', err, err.stack);
res.statusCode = 500;
res.end('{}');
}
} else {
res.statusCode = 401;
res.end('{}');
}
});
function own(_x, _x2) {
return ref.apply(this, arguments);
}
return own;
})();
const router = Router();
router.use(getUserContext);
router.route('/:app').all(getAcl).get(getCode).post(own).purge(renewCode);
router.route('/:app/:code').all(getAcl).put(join);
router.route('/:app/:email').all(getAcl).delete(leave);
var _getServerConfig = getServerConfig();
const auth = _getServerConfig.auth;
const db = _getServerConfig.db;
const endpoint = _getServerConfig.endpoint;
const port = _getServerConfig.port;
http.createServer((req, res) => {
req.own = { auth, db, endpoint };
router(req, res, finalhandler(req, res));
}).listen(port);
console.log(`CouchDB own running on port http://localhost:${ port } against ${ endpoint }`);