Newer
Older
const { auth, requiresAuth } = require('express-openid-connect');
const httpProxy = require('http-proxy');
const PORT = 3000;
const app = express();
const config = {
auth0Logout: true,
baseURL: process.env.BASE_URL,
secret: process.env.SECRET,
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
issuerBaseURL: process.env.ISSUER_BASE_URL,
routes: {
callback: '/oauth2/callback',
login: '/oauth2/login',
logout: '/oauth2/logout',
postLogoutRedirect: '/',
},
authorizationParams: {
scope: 'openid profile email',
response_type: 'code',
},
};
const ACCESS_FILE = process.env.ACCESS_FILE;
function listsDiffer(a, b) {
if(a.length != b.length) {
return true;
}
for(let i = 0; i < a.length; ++i) {
if(a[i] != b[i]) {
return true;
}
}
return false;
}
let allowedUsers = [];
function loadAllowedUsers() {
fs.readFile(ACCESS_FILE, 'utf8', (err, data) => {
if(err) {
console.error(err);
process.exit(1);
}
// Load the list
const loadedUsers = [];
const loadedTokens = [];
for(let line of data.split('\n')) {
line = line.trim();
if(line.length > 0 && line[0] != '#') {
if(line[0] == '%') {
loadedTokens.push(line.substring(1));
} else {
loadedUsers.push(line);
}
if(listsDiffer(allowedUsers, loadedUsers)
|| listsDiffer(bypassTokens, loadedTokens)) {
console.log('Loaded new users list');
}
// Update global and reset timer
allowedUsers = loadedUsers;
bypassTokens = loadedTokens;
setTimeout(loadAllowedUsers, 30000);
});
}
loadAllowedUsers();
PUBLIC_REGEX = process.env.PUBLIC_REGEX;
if(PUBLIC_REGEX) {
PUBLIC_REGEX = new RegExp('^(?:' + PUBLIC_REGEX + ')');
let UPSTREAM_PROTO, UPSTREAM_PORT;
if(upstream_url.protocol === 'http:') {
UPSTREAM_PROTO = http;
UPSTREAM_PORT = 80;
} else if(upstream_url.protocol === 'https:') {
UPSTREAM_PROTO = https;
UPSTREAM_PORT = 443;
} else {
console.error('Invalid UPSTREAM: protocol should be http or https');
process.exit(1);
}
if(
(upstream_url.pathname !== '/' && upstream_url.pathname !== '')
|| upstream_url.search
|| upstream_url.hash
|| upstream_url.username
|| upstream_url.password
) {
console.error('Invalid UPSTREAM: path is set');
process.exit(1);
}
const UPSTREAM_HOST = upstream_url.hostname;
if(upstream_url.port) {
UPSTREAM_PORT = parseInt(upstream_url.port);
}
console.log(`Using upstream ${UPSTREAM_HOST}:${UPSTREAM_PORT}`);
// Create proxy
const proxy = httpProxy.createProxyServer({target: upstream_url});
proxy.on('error', function(err, req, res) {
res.writeHead(503, {'Content-type': 'text/plain'});
res.end('Upstream server is unavailable');
});
function accessCheck(req, res) {
// Check token header
if(req.headers['x-oidc-proxy-bypass']) {
// Token is set, check it
if(bypassTokens.indexOf(req.headers['x-oidc-proxy-bypass']) === -1) {
// Bad token
res.sendStatus(403);
// Check cookie
let cookies = req.headers['cookie'];
if(cookies) {
cookies = cookie.parse(cookies);
} else {
cookies = {};
}
if(cookies['oidc-proxy-bypass']) {
// Cookie is set, check it against tokens
if(bypassTokens.indexOf(cookies['oidc-proxy-bypass']) === -1) {
// Bad token, remove cookie
res.clearCookie('oidc-proxy-bypass');
} else {
return true;
}
}
// Not authenticated, send login form or error
if(req.accepts('html')) {
} else {
res.sendStatus(403);
}
// Logged in, validate user
if(allowedUsers.indexOf(req.oidc.user.sub) == -1) {
res.status(403);
res.setHeader('content-type', 'text/html');
let whereTo = 'the documentation';
if(process.env.DOCUMENTATION_URL) {
whereTo = `<a href="${process.env.DOCUMENTATION_URL}">${whereTo}</a>`;
}
res.send(`\
<!DOCTYPE html>
<html>
<head><title>Forbidden</title></head>
<body>You don't have access to this server. Please see ${whereTo}.</body>
</html>`);
return false;
} else {
return true;
}
}
app.all('/*', (req, res) => {
if(PUBLIC_REGEX && PUBLIC_REGEX.test(req.path)) {
// Public paths require no authentication
} else if(!accessCheck(req, res)) {
// Failed access check
});
const server = http.createServer(app);
server.on('upgrade', (req, socket, head) => {