From 9f35550e58e2e59eac04581882fa17bfdf87a507 Mon Sep 17 00:00:00 2001 From: josedario87 Date: Wed, 24 Sep 2025 18:03:34 -0600 Subject: [PATCH] sistema de usuarios completo --- freeradius/mods-config/files/authorize | 10 ++ node-api/index.js | 123 +++++++++++++++++++------ node-api/public/index.html | 65 ++++++++++++- 3 files changed, 170 insertions(+), 28 deletions(-) diff --git a/freeradius/mods-config/files/authorize b/freeradius/mods-config/files/authorize index 979e40a..00c35d9 100644 --- a/freeradius/mods-config/files/authorize +++ b/freeradius/mods-config/files/authorize @@ -13,3 +13,13 @@ prueba2 Cleartext-Password := "contra2" Tunnel-Type = VLAN, Tunnel-Medium-Type = IEEE-802, Tunnel-Private-Group-Id = "2" + +dario Cleartext-Password := "contra1" + Tunnel-Type = VLAN, + Tunnel-Medium-Type = IEEE-802, + Tunnel-Private-Group-Id = "2" + +margie Cleartext-Password := "bonita" + Tunnel-Type = VLAN, + Tunnel-Medium-Type = IEEE-802, + Tunnel-Private-Group-Id = "2" diff --git a/node-api/index.js b/node-api/index.js index deecefb..4610c4f 100644 --- a/node-api/index.js +++ b/node-api/index.js @@ -26,13 +26,8 @@ const RADIUS_SECRET = process.env.RADIUS_SECRET || process.env.RADIUS_SHARED_SEC 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 +// Requests 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; @@ -54,19 +49,66 @@ function broadcastStatus(payload) { 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`); +function parseUsersFromText(text) { + const users = []; + const lines = text.split(/\r?\n/); + let i = 0; + while (i < lines.length) { + const line = lines[i]; + const m = line.match(/^\s*(#?)\s*([^\s#]+)\s+Cleartext-Password\s*:=\s*"([^"]+)"/); + if (m) { + const disabled = m[1] === '#'; + const username = m[2]; + const password = m[3]; + let vlan = undefined; + i++; + while (i < lines.length && lines[i].trim() !== '') { + const l = lines[i].replace(/^\s*#\s*/, ''); + const mv = l.match(/Tunnel-Private-Group-Id\s*=\s*"?(\d+)"?/i); + if (mv) vlan = mv[1]; + i++; + } + users.push({ username, password, vlan: vlan || VLAN_ID, disabled }); + while (i < lines.length && lines[i].trim() === '') i++; + continue; } - await fs.writeFile(AUTH_FILE, header + blocks.join('\n')); - // Trigger FreeRADIUS reload via Docker API (HUP) + i++; + } + return users; +} + +async function readUsersFromFile() { + try { + const data = await fs.readFile(AUTH_FILE, 'utf8').catch(() => ''); + return parseUsersFromText(data); + } catch { + return []; + } +} + +async function writeUsersToFile(users) { + const header = '# Managed by Node dashboard; do not edit manually\n'; + const chunks = []; + for (const u of users) { + const lines = [ + `${u.username} Cleartext-Password := "${u.password}"`, + ` Tunnel-Type = VLAN,`, + ` Tunnel-Medium-Type = IEEE-802,`, + ` Tunnel-Private-Group-Id = "${String(u.vlan || VLAN_ID)}"`, + '' + ]; + if (u.disabled) { + chunks.push(lines.map(l => l ? `# ${l}` : '').join('\n')); + } else { + chunks.push(lines.join('\n')); + } + } + await fs.writeFile(AUTH_FILE, header + chunks.join('\n')); +} + +async function persistUsersToFreeradius(users) { + try { + await writeUsersToFile(users); triggerRadiusReload().catch(() => {}); } catch (e) { console.error('Failed to persist users to FreeRADIUS files:', e); @@ -170,7 +212,8 @@ app.post('/authorize-inner', (req, res) => { const attrs = normalizeAttributes(req.body); const username = (attrs['User-Name'] || '').toString(); - const entry = users.get(username); + const list = await readUsersFromFile(); + const entry = list.find(u => u.username === username && !u.disabled); const password = entry?.password; if (!password) { @@ -204,7 +247,9 @@ app.post('/authorize-inner', (req, res) => { 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; + const list = await readUsersFromFile(); + const entry = list.find(u => u.username === username && !u.disabled); + const vlan = entry?.vlan || VLAN_ID; return res.status(200).json({ reply: [ { name: 'Tunnel-Type', value: 'VLAN' }, @@ -217,17 +262,41 @@ app.post('/post-auth', (req, res) => { }); // Users API -app.get('/api/users', (req, res) => { - const items = Array.from(users.entries()).map(([username, { password, vlan }]) => ({ username, password, vlan })); +app.get('/api/users', async (req, res) => { + const items = await readUsersFromFile(); res.json({ items }); }); -app.post('/api/users', (req, res) => { - const { username, password, vlan } = req.body || {}; +app.post('/api/users', async (req, res) => { + const { username, password, vlan, disabled } = 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')); + const items = await readUsersFromFile(); + const idx = items.findIndex(u => u.username === String(username)); + const user = { username: String(username), password: String(password), vlan: String(vlan || VLAN_ID), disabled: !!disabled }; + if (idx >= 0) items[idx] = user; else items.push(user); + await persistUsersToFreeradius(items); + res.json({ ok: true }); +}); + +app.patch('/api/users/:username', async (req, res) => { + const uname = req.params.username; + const { password, vlan, disabled } = req.body || {}; + const items = await readUsersFromFile(); + const idx = items.findIndex(u => u.username === uname); + if (idx < 0) return res.status(404).json({ ok: false, error: 'not_found' }); + if (password !== undefined) items[idx].password = String(password); + if (vlan !== undefined) items[idx].vlan = String(vlan); + if (disabled !== undefined) items[idx].disabled = !!disabled; + await persistUsersToFreeradius(items); + res.json({ ok: true }); +}); + +app.delete('/api/users/:username', async (req, res) => { + const uname = req.params.username; + const items = await readUsersFromFile(); + const next = items.filter(u => u.username !== uname); + if (next.length === items.length) return res.status(404).json({ ok: false, error: 'not_found' }); + await persistUsersToFreeradius(next); res.json({ ok: true }); }); diff --git a/node-api/public/index.html b/node-api/public/index.html index 32027da..75ff4ef 100644 --- a/node-api/public/index.html +++ b/node-api/public/index.html @@ -41,6 +41,10 @@ +

Usuarios

+
+
+

Eventos