agregado de nuevos usuarios listo

This commit is contained in:
2025-09-24 17:51:05 -06:00
parent 85886f1fed
commit 11b95c97a7
11 changed files with 275 additions and 42 deletions

View File

@@ -1,7 +1,9 @@
import express from 'express';
import morgan from 'morgan';
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import http from 'http';
import dgram from 'dgram';
import radius from 'radius';
@@ -21,10 +23,18 @@ const MAX_REQUESTS = parseInt(process.env.MAX_REQUESTS || '200', 10);
const RADIUS_HOST = process.env.RADIUS_HOST || 'freeradius';
const RADIUS_AUTH_PORT = parseInt(process.env.RADIUS_AUTH_PORT || '1812', 10);
const RADIUS_SECRET = process.env.RADIUS_SECRET || process.env.RADIUS_SHARED_SECRET || 'tamosbien';
const DOCKER_SOCK = process.env.DOCKER_SOCK || '/var/run/docker.sock';
const FREERADIUS_CONTAINER = process.env.FREERADIUS_CONTAINER || 'radiusnucleo-freeradius-1';
// In-memory request store + SSE clients
const requests = [];
// In-memory users: username -> { password, vlan }
const users = new Map([
['user1', { password: 'contra1', vlan: '2' }],
['user2', { password: 'contra2', vlan: '3' }],
]);
const sseClients = new Set();
let radiusReloading = false;
function pushRequest(rec) {
requests.push(rec);
@@ -36,6 +46,60 @@ function pushRequest(rec) {
}
}
function broadcastStatus(payload) {
const ev = `event: status\n` + `data: ${JSON.stringify(payload)}\n\n`;
for (const res of sseClients) { try { res.write(ev); } catch {}
}
}
const AUTH_FILE = process.env.AUTH_FILE || '/shared/authorize';
async function persistUsersToFreeradius() {
try {
const header = '# Managed by Node dashboard; do not edit manually\n';
const blocks = [];
for (const [username, { password, vlan }] of users.entries()) {
const v = String(vlan || VLAN_ID);
blocks.push(`${username} Cleartext-Password := "${password}"
Tunnel-Type = VLAN,
Tunnel-Medium-Type = IEEE-802,
Tunnel-Private-Group-Id = "${v}"\n`);
}
await fs.writeFile(AUTH_FILE, header + blocks.join('\n'));
// Trigger FreeRADIUS reload via Docker API (HUP)
triggerRadiusReload().catch(() => {});
} catch (e) {
console.error('Failed to persist users to FreeRADIUS files:', e);
}
}
async function triggerRadiusReload() {
try {
radiusReloading = true;
broadcastStatus({ radius_reloading: true });
// Call Docker Engine API over unix socket: POST /containers/{id}/kill?signal=HUP
await new Promise((resolve, reject) => {
const req = http.request({
method: 'POST',
socketPath: DOCKER_SOCK,
path: `/v1.41/containers/${encodeURIComponent(FREERADIUS_CONTAINER)}/kill?signal=HUP`,
}, (res) => {
res.resume();
res.on('end', resolve);
});
req.on('error', reject);
req.end();
});
} catch (e) {
console.error('Failed to HUP FreeRADIUS:', e?.message || e);
} finally {
setTimeout(() => {
radiusReloading = false;
broadcastStatus({ radius_reloading: false });
}, 1500);
}
}
// Helper: standard Accept with VLAN + bandwidth
function buildAcceptPayload(extra = {}) {
return {
@@ -105,12 +169,9 @@ app.post('/authorize-inner', (req, res) => {
console.log(JSON.stringify(req.body, null, 2));
const attrs = normalizeAttributes(req.body);
const users = {
'user1': 'contra1',
'user2': 'contra2',
};
const username = (attrs['User-Name'] || '').toString();
const password = users[username];
const entry = users.get(username);
const password = entry?.password;
if (!password) {
pushRequest({
@@ -133,12 +194,43 @@ app.post('/authorize-inner', (req, res) => {
});
return res.status(200).json({
control: {
'Cleartext-Password': password,
},
control: [
{ name: 'Cleartext-Password', value: String(password) }
]
});
});
// Post-auth: return reply attributes like VLAN based on user mapping
app.post('/post-auth', (req, res) => {
const attrs = normalizeAttributes(req.body);
const username = (attrs['User-Name'] || '').toString();
const vlan = users.get(username)?.vlan || VLAN_ID;
return res.status(200).json({
reply: [
{ name: 'Tunnel-Type', value: 'VLAN' },
{ name: 'Tunnel-Medium-Type', value: 'IEEE-802' },
{ name: 'Tunnel-Private-Group-Id', value: String(vlan) },
{ name: 'WISPr-Bandwidth-Max-Down', value: String(MAX_DOWN) },
{ name: 'WISPr-Bandwidth-Max-Up', value: String(MAX_UP) }
]
});
});
// Users API
app.get('/api/users', (req, res) => {
const items = Array.from(users.entries()).map(([username, { password, vlan }]) => ({ username, password, vlan }));
res.json({ items });
});
app.post('/api/users', (req, res) => {
const { username, password, vlan } = req.body || {};
if (!username || !password) return res.status(400).json({ ok: false, error: 'username and password required' });
const vlanStr = vlan ? String(vlan) : VLAN_ID;
users.set(String(username), { password: String(password), vlan: vlanStr });
persistUsersToFreeradius().then(() => console.log('Users synced to FreeRADIUS files'));
res.json({ ok: true });
});
// API: recent requests
app.get('/api/requests', (req, res) => {
res.json({ items: requests.slice(-MAX_REQUESTS) });
@@ -196,6 +288,9 @@ app.get('/events', (req, res) => {
// send a hello event
res.write(`event: hello\n`);
res.write(`data: {"ok":true}\n\n`);
// send initial status
res.write(`event: status\n`);
res.write(`data: ${JSON.stringify({ radius_reloading: radiusReloading })}\n\n`);
sseClients.add(res);
req.on('close', () => sseClients.delete(res));
});