Browse Source

sessionless auth

pull/597/head
babadzhanov 3 years ago
parent
commit
260b790a3e
  1. 265
      src/lib/Server.js

265
src/lib/Server.js

@ -1,6 +1,7 @@
'use strict'; 'use strict';
const path = require('path'); const path = require('path');
const crypto = require('crypto');
const express = require('express'); const express = require('express');
const expressSession = require('express-session'); const expressSession = require('express-session');
@ -11,133 +12,149 @@ const ServerError = require('./ServerError');
const WireGuard = require('../services/WireGuard'); const WireGuard = require('../services/WireGuard');
const { const {
PORT, PORT,
RELEASE, RELEASE,
PASSWORD, PASSWORD,
} = require('../config'); } = require('../config');
module.exports = class Server { module.exports = class Server {
constructor() { constructor() {
// Express // Express
this.app = express() this.app = express()
.disable('etag') .disable('etag')
.use('/', express.static(path.join(__dirname, '..', 'www'))) .use('/', express.static(path.join(__dirname, '..', 'www')))
.use(express.json()) .use(express.json())
.use(expressSession({ .use(expressSession({
secret: String(Math.random()), secret: String(Math.random()),
resave: true, resave: true,
saveUninitialized: true, saveUninitialized: true,
})) }))
.get('/api/release', (Util.promisify(async () => { .get('/api/release', (Util.promisify(async () => {
return RELEASE; return RELEASE;
}))) })))
// Authentication // Authentication
.get('/api/session', Util.promisify(async req => { .get('/api/session', Util.promisify(async req => {
const requiresPassword = !!process.env.PASSWORD; const requiresPassword = !!process.env.PASSWORD;
const authenticated = requiresPassword const authenticated = requiresPassword
? !!(req.session && req.session.authenticated) ? !!(req.session && req.session.authenticated)
: true; : true;
return { return {
requiresPassword, requiresPassword,
authenticated, authenticated,
}; };
})) }))
.post('/api/session', Util.promisify(async req => { .post('/api/session', Util.promisify(async req => {
const { const {
password, password,
} = req.body; } = req.body;
if (typeof password !== 'string') { if (typeof password !== 'string') {
throw new ServerError('Missing: Password', 401); throw new ServerError('Missing: Password', 401);
} }
if (password !== PASSWORD) { if (password !== PASSWORD) {
throw new ServerError('Incorrect Password', 401); throw new ServerError('Incorrect Password', 401);
} }
req.session.authenticated = true; req.session.authenticated = true;
req.session.save(); req.session.save();
debug(`New Session: ${req.session.id}`); debug(`New Session: ${req.session.id}`);
})) }))
// WireGuard // WireGuard
.use((req, res, next) => { .use((req, res, next) => {
if (!PASSWORD) { if (!PASSWORD) {
return next(); return next();
} }
if (req.session && req.session.authenticated) { if (req.session && req.session.authenticated) {
return next(); return next();
} }
return res.status(401).json({ if (req.path.startsWith('/api/') && req.headers['authorization']) {
error: 'Not Logged In', const authorizationHash = crypto.createHash('sha256')
}); .update(req.headers['authorization'])
}) .digest('hex');
.delete('/api/session', Util.promisify(async req => { const passwordHash = crypto.createHash('sha256')
const sessionId = req.session.id; .update(PASSWORD)
.digest('hex');
req.session.destroy(); if (crypto.timingSafeEqual(Buffer.from(authorizationHash), Buffer.from(passwordHash))) {
return next();
debug(`Deleted Session: ${sessionId}`); }
}))
.get('/api/wireguard/client', Util.promisify(async req => { return res.status(401).json({
return WireGuard.getClients(); error: 'Incorrect Password',
})) });
.get('/api/wireguard/client/:clientId/qrcode.svg', Util.promisify(async (req, res) => { }
const { clientId } = req.params;
const svg = await WireGuard.getClientQRCodeSVG({ clientId }); return res.status(401).json({
res.header('Content-Type', 'image/svg+xml'); error: 'Not Logged In',
res.send(svg); });
})) })
.get('/api/wireguard/client/:clientId/configuration', Util.promisify(async (req, res) => { .delete('/api/session', Util.promisify(async req => {
const { clientId } = req.params; const sessionId = req.session.id;
const client = await WireGuard.getClient({ clientId });
const config = await WireGuard.getClientConfiguration({ clientId }); req.session.destroy();
const configName = client.name
.replace(/[^a-zA-Z0-9_=+.-]/g, '-') debug(`Deleted Session: ${sessionId}`);
.replace(/(-{2,}|-$)/g, '-') }))
.replace(/-$/, '') .get('/api/wireguard/client', Util.promisify(async req => {
.substring(0, 32); return WireGuard.getClients();
res.header('Content-Disposition', `attachment; filename="${configName || clientId}.conf"`); }))
res.header('Content-Type', 'text/plain'); .get('/api/wireguard/client/:clientId/qrcode.svg', Util.promisify(async (req, res) => {
res.send(config); const {clientId} = req.params;
})) const svg = await WireGuard.getClientQRCodeSVG({clientId});
.post('/api/wireguard/client', Util.promisify(async req => { res.header('Content-Type', 'image/svg+xml');
const { name } = req.body; res.send(svg);
return WireGuard.createClient({ name }); }))
})) .get('/api/wireguard/client/:clientId/configuration', Util.promisify(async (req, res) => {
.delete('/api/wireguard/client/:clientId', Util.promisify(async req => { const {clientId} = req.params;
const { clientId } = req.params; const client = await WireGuard.getClient({clientId});
return WireGuard.deleteClient({ clientId }); const config = await WireGuard.getClientConfiguration({clientId});
})) const configName = client.name
.post('/api/wireguard/client/:clientId/enable', Util.promisify(async req => { .replace(/[^a-zA-Z0-9_=+.-]/g, '-')
const { clientId } = req.params; .replace(/(-{2,}|-$)/g, '-')
return WireGuard.enableClient({ clientId }); .replace(/-$/, '')
})) .substring(0, 32);
.post('/api/wireguard/client/:clientId/disable', Util.promisify(async req => { res.header('Content-Disposition', `attachment; filename="${configName || clientId}.conf"`);
const { clientId } = req.params; res.header('Content-Type', 'text/plain');
return WireGuard.disableClient({ clientId }); res.send(config);
})) }))
.put('/api/wireguard/client/:clientId/name', Util.promisify(async req => { .post('/api/wireguard/client', Util.promisify(async req => {
const { clientId } = req.params; const {name} = req.body;
const { name } = req.body; return WireGuard.createClient({name});
return WireGuard.updateClientName({ clientId, name }); }))
})) .delete('/api/wireguard/client/:clientId', Util.promisify(async req => {
.put('/api/wireguard/client/:clientId/address', Util.promisify(async req => { const {clientId} = req.params;
const { clientId } = req.params; return WireGuard.deleteClient({clientId});
const { address } = req.body; }))
return WireGuard.updateClientAddress({ clientId, address }); .post('/api/wireguard/client/:clientId/enable', Util.promisify(async req => {
})) const {clientId} = req.params;
return WireGuard.enableClient({clientId});
.listen(PORT, () => { }))
debug(`Listening on http://0.0.0.0:${PORT}`); .post('/api/wireguard/client/:clientId/disable', Util.promisify(async req => {
}); const {clientId} = req.params;
} return WireGuard.disableClient({clientId});
}))
.put('/api/wireguard/client/:clientId/name', Util.promisify(async req => {
const {clientId} = req.params;
const {name} = req.body;
return WireGuard.updateClientName({clientId, name});
}))
.put('/api/wireguard/client/:clientId/address', Util.promisify(async req => {
const {clientId} = req.params;
const {address} = req.body;
return WireGuard.updateClientAddress({clientId, address});
}))
.listen(PORT, () => {
debug(`Listening on http://0.0.0.0:${PORT}`);
});
}
}; };

Loading…
Cancel
Save