lib/account-auth-transport.js
Rama main · réplica de lectura del repositorio original.
var net = require('net');
var normalizeOrigin = require('./network-origins').normalizeAllowedOrigins;
function address(value) {
var result = String(value || '').replace(/^::ffff:/, '');
return net.isIP(result) ? result : '';
}
function loopback(value) { return value === '::1' || /^127\./.test(value); }
function attachTransport(ctx) {
function settings() { return ctx.users.loadUsers().accountTransport || { origin: '', trustedProxies: [] }; }
function info(req) {
var config = settings(); var peer = address(req.socket && req.socket.remoteAddress);
var proxy = !!peer && (config.trustedProxies || []).indexOf(peer) !== -1 && req.headers['x-forwarded-proto'] !== undefined;
var secure = !!(ctx.tlsOptions || req.socket && req.socket.encrypted);
if (proxy) secure = req.headers['x-forwarded-proto'] === 'https';
var origin = '';
try { origin = proxy ? config.origin : normalizeOrigin([(secure ? 'https://' : 'http://') + req.headers.host])[0]; } catch (error) { /* Invalid Host is not an authentication origin. */ }
var local = false;
try { var hostname = new URL(origin).hostname; local = !proxy && loopback(peer) && (hostname === 'localhost' || hostname === '[::1]' || /^127\./.test(hostname)); } catch (error) { /* Invalid origin remains denied. */ }
var client = peer;
if (proxy) {
// The configured immediate proxy must overwrite this single-address header.
client = address(req.headers['x-real-ip']) || peer;
}
return { origin: origin, secure: secure, local: local, clientAddress: client, proxy: proxy };
}
function allows(req, write) {
var value = info(req);
if (!value.origin || !value.secure && !value.local) return false;
if (req.headers.origin && req.headers.origin !== value.origin) return false;
if (!write) return true;
if (req.headers['sec-fetch-site'] && ['same-origin', 'none'].indexOf(req.headers['sec-fetch-site']) === -1) return false;
// Fetch writes send Origin; same-origin legacy clients may send Referer instead.
if (req.headers.origin) return req.headers.origin === value.origin;
if (req.headers.referer) { try { return new URL(req.headers.referer).origin === value.origin; } catch (error) { return false; } }
return false;
}
function save(data) {
var origin = data.origin ? normalizeOrigin([data.origin])[0] : '';
if (origin && new URL(origin).protocol !== 'https:') throw new Error('The external account address must use HTTPS.');
if (!Array.isArray(data.trustedProxies) || data.trustedProxies.length > 16) throw new Error('Enter up to 16 exact proxy IP addresses.');
var proxies = data.trustedProxies.map(function (value) { var ip = address(value); if (!ip) throw new Error('Trusted proxies must be exact IP addresses.'); return ip; });
if (proxies.length && !origin) throw new Error('An HTTPS address is required for a trusted proxy.');
var state = ctx.users.loadUsers(); state.accountTransport = { origin: origin, trustedProxies: Array.from(new Set(proxies)) }; ctx.users.saveUsers(state);
return state.accountTransport;
}
return { info: info, allows: allows, settings: settings, save: save, clientAddress: function (req) { return info(req).clientAddress; } };
}
module.exports = { attachTransport: attachTransport };