session-open-group-server-l.../dialects/control/dialect.loki_control.js

203 lines
5.6 KiB
JavaScript

// global/general loki overlay setup/config
const overlay = require('../../lib.overlay');
// will need to upgrade to regexs when we support multiple channels...
const allowedEndpoints = {
get: [
// '/token', // used for ??
'/channels/1',
'/channels/1/messages',
],
post: [
'/files',
'/channels/1/messages',
'/channels/1/subscribe',
],
put: [
'/users/me',
'/channels/1',
],
patch: [
'/users/me'
],
delete: [
'/channels/1/subscribe',
],
}
// block
// - posts endpoints
// - user search (so you can't enumerate pubkeys)
// require token on user lookup
// if whitelist mode
// - we block tokens that aren't whitelisted
// - block everything not allowed...
module.exports = (app, prefix) => {
// set cache based on dispatcher object
cache = app.dispatcher.cache;
const utilities = overlay.setup(cache, app.dispatcher);
utilities.cache = cache;
utilities.dispatcher = app.dispatcher;
utilities.nconf = app.nconf;
app.use(function (req, res, next) {
// allow *_challenge
if (req.path === '/loki/v1/get_challenge' ||
req.path === '/loki/v1/submit_challenge') {
return next();
}
// you can view the homepage
if (req.path === '/') {
return next();
}
// allow access to pomf files...
if (req.path.match(/^\/f\//)) {
return next();
}
// allow access to pomf uploads...
if (req.path.match(/^\/upload/)) {
return next();
}
// allow access to public files...
if (req.path.match(/^\/css\//)) {
return next();
}
if (req.path.match(/^\/images\//)) {
return next();
}
if (req.path.match(/^\/js\//)) {
return next();
}
// disable posts system completely
if (req.path.match(/^\/posts/i)) {
console.log('loki control posts request?', req.path);
return res.status(403).type('application/json').end(JSON.stringify({
meta: {
code: 403,
error: 'Forbidden',
},
data: false
}));
}
// don't have to worry about the querystring
if (req.path === '/users/search') {
console.log('loki control user search request?', req.path);
return res.status(403).type('application/json').end(JSON.stringify({
meta: {
code: 403,
error: 'Forbidden',
},
data: false
}));
}
// require a token for all /users requests
if (req.path.match(/^\/users\//i) && !req.token) {
// mainly for /users/{user_id}
console.log('loki control user lookup required token now', req.path);
// FIXME: proper message
return res.status(401).type('application/json').end(JSON.stringify({
meta: {
code: 401,
error: 'Need token',
},
data: false
}));
}
const diskConfig = utilities.config.getDiskConfig();
if (!diskConfig.whitelist) {
return next();
}
// whitelist mode, hide some paths...
if (!allowedEndpoints[req.method.toLowerCase()]) {
console.log('method is not configured', req.method);
return res.status(405).type('application/json').end(JSON.stringify({
meta: {
code: 405,
error: 'Method Not Allowed',
},
data: false
}));
}
let ok = true;
// check non-dynamic
if (!allowedEndpoints[req.method.toLowerCase()].includes(req.path.toLowerCase())) {
ok = false;
}
// allow user look ups (but with a token, that check is done later)
if (req.method.toLowerCase() === 'get' && req.path.match(/^\/users\//i)) {
ok = true;
}
// need this for transpot unit tests
if (req.method.toLowerCase() === 'get' && req.path === '/users') {
ok = true;
}
// need this for create_message unit tests
if (req.method.toLowerCase() === 'get' && req.path.match(/^\/channels\//i) && req.path.match(/\/messages\//i)) {
ok = true;
}
// GET /token is valid, if you're passing a token...
if (req.method.toLowerCase() === 'get' && req.path.match(/^\/token/i)) {
ok = true;
}
// all loki endpoints are valid
if (req.path.match(/^\/loki\/v/)) {
ok = true;
}
// check dynamic
/*
if (req.method.toLowerCase() === 'post') {
} else if (req.method.toLowerCase() === 'put') {
} else if (req.method.toLowerCase() === 'delete') {
}
*/
if (!ok) {
console.log('control middleware blocking', req.method.toLowerCase(), req.path.toLowerCase());
return res.status(403).type('application/json').end(JSON.stringify({
meta: {
code: 403,
error: 'Forbidden',
},
data: false
}));
}
if (!req.token) {
console.log('weird session clients always have a token set', req.path);
next();
}
// if valid URL
if (req.token) {
// get pubKey from token...
cache.getAPIUserToken(req.token, async function(err, usertoken) {
if (err) {
console.error('control middleware getAPIUserToken err', err);
}
// usertoken.userid is now
if (!usertoken || !usertoken.userid) {
console.warn('control middleware no user or invalid token', req.token);
return res.status(403).type('application/json').end(JSON.stringify({
error: 'Forbidden',
}));
}
const passes = await logic.passesWhitelistByUserID(usertoken.userid);
if (!passes) {
console.log('control middleware', usertoken.userid, 'not whitelisted');
return res.status(403).type('application/json').end(JSON.stringify({
error: 'Forbidden',
}));
}
next();
});
}
});
}