Test ACL page

This commit is contained in:
Christopher Cookman 2025-09-01 00:06:41 -06:00
parent e03af9da37
commit 2ed971fbcc
5 changed files with 232 additions and 2 deletions

View file

@ -105,7 +105,7 @@ if (process.env.EVENT_TESTING == true) {
await getLastEventIndex();
await fetchEvents();
})();
test = true
test = false
if (test == true) {
log.warn("EVENT_TESTING is enabled - generating test events");
setInterval(() => {

139
routes/acl.js Normal file
View file

@ -0,0 +1,139 @@
const express = require('express');
const log = require("../logger.js");
const db = global.db;
const router = express.Router();
const expressWs = require('express-ws')(router);
router.use((req, res, next) => {
if (!req.session.user) return res.redirect('/login?err=4');
const perms = req.session.user.perms ? JSON.parse(req.session.user.perms) : [];
if (perms.includes('*') || perms.includes('acl')) {
return next();
}
return res.status(403).render('error', { error: 'Forbidden', button: { text: 'Back', action: 'back' } }); \
});
router.get('/', async (req, res) => {
const acl = await db.query('SELECT * FROM ACL');
res.render('acl', { acl, user: req.session.user });
});
router.put('/:cardNumber', async (req, res) => { // Attempt to create new ACL entry. Fail if cardNumber already exists
const cardNumber = parseInt(req.params.cardNumber);
if (isNaN(cardNumber) || cardNumber <= 0) {
return res.status(400).json({ error: 'Invalid card number' });
}
const data = req.body;
data.cardNumber = cardNumber;
try {
const check = await db.query('SELECT * FROM ACL WHERE CardNumber = ?', [cardNumber]);
if (check && check.length > 0) {
return res.status(409).json({ error: 'Card number already exists' });
}
// Name, CardNumber, PIN, StartDate, EndDate, [Doors (will be array of door names from client, for each add that column adn set to 1)]
const fields = ['Name', 'CardNumber', 'PIN', 'StartDate', 'EndDate'];
const placeholders = ['?', '?', '?', '?', '?'];
const values = [
data.Name || "Unknown/Not Set",
data.cardNumber,
data.PIN || null,
data.StartDate || new Date(),
data.EndDate || new Date(new Date().setFullYear(new Date().getFullYear() + 99))
];
data.doors.forEach(door => {
fields.push(door);
placeholders.push('?');
values.push(1);
});
const sql = `INSERT INTO ACL (${fields.join(', ')}) VALUES (${placeholders.join(', ')})`;
db.query(sql, values).then(result => {
return res.status(201).json({ message: 'ACL entry created' });
}).catch(err => {
log.error(`Database error creating ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
});
} catch (err) {
log.error(`Database error checking ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
}
});
router.patch('/:cardNumber', async (req, res) => { // Update ACL entry. Fail if cardNumber does not exist
const cardNumber = parseInt(req.params.cardNumber);
if (isNaN(cardNumber) || cardNumber <= 0) {
return res.status(400).json({ error: 'Invalid card number' });
}
const data = req.body;
data.cardNumber = cardNumber;
try {
const check = await db.query('SELECT * FROM ACL WHERE CardNumber = ?', [cardNumber]);
if (!check || check.length === 0) {
return res.status(404).json({ error: 'Card number not found' });
}
// Name, CardNumber, PIN, StartDate, EndDate, [Doors (will be object of door names : true/false from client, for each add that column adn set to 1)]
const fields = [];
const values = [];
if (data.Name) {
fields.push('Name = ?');
values.push(data.Name);
}
if (data.PIN) {
fields.push('PIN = ?');
values.push(data.PIN);
}
if (data.StartDate) {
fields.push('StartDate = ?');
values.push(data.StartDate);
}
if (data.EndDate) {
fields.push('EndDate = ?');
values.push(data.EndDate);
}
for (const door in data.doors) {
if (data.doors.hasOwnProperty(door)) {
fields.push(`${door} = ?`);
values.push(data.doors[door] ? 1 : 0);
}
if (fields.length === 0) {
return res.status(400).json({ error: 'No fields to update' });
}
}
values.push(cardNumber);
const sql = `UPDATE ACL SET ${fields.join(', ')} WHERE CardNumber = ?`;
db.query(sql, values).then(result => {
return res.status(200).json({ message: 'ACL entry updated' });
}).catch(err => {
log.error(`Database error updating ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
});
} catch (err) {
log.error(`Database error checking ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
}
});
router.delete('/:cardNumber', async (req, res) => { // Delete ACL entry. Fail if cardNumber does not exist
const cardNumber = parseInt(req.params.cardNumber);
if (isNaN(cardNumber) || cardNumber <= 0) {
return res.status(400).json({ error: 'Invalid card number' });
}
try {
const check = await db.query('SELECT * FROM ACL WHERE CardNumber = ?', [cardNumber]);
if (!check || check.length === 0) {
return res.status(404).json({ error: 'Card number not found' });
}
db.query('DELETE FROM ACL WHERE CardNumber = ?', [cardNumber]).then(result => {
return res.status(200).json({ message: 'ACL entry deleted' });
}).catch(err => {
log.error(`Database error deleting ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
});
} catch (err) {
log.error(`Database error checking ACL entry: ${err}`);
return res.status(500).json({ error: 'Internal server error' });
}
});
module.exports = router;

View file

@ -62,6 +62,15 @@ dataTypes = {
}
}
router.use((req, res, next) => {
if (!req.session.user) return res.redirect('/login?err=4');
const perms = req.session.user.perms ? JSON.parse(req.session.user.perms) : [];
if (perms.includes('*') || perms.includes('eventLog')) {
return next();
}
return res.status(403).render('error', { error: 'Forbidden', button: { text: 'Back', action: 'back' } });
});
router.get('/', async (req, res) => {
const logs = await db.query('SELECT * FROM Events ORDER BY EventIndex DESC LIMIT 100');
res.render('event-logs', { logs, user: req.session.user, dataTypes });

82
views/acl.ejs Normal file
View file

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Access Control List</title>
<style>
body {
font-family: Arial, sans-serif;
background: #f7f7f7;
margin: 0;
padding: 20px;
}
h1 {
text-align: center;
color: #333;
}
table {
width: 100%;
border-collapse: collapse;
background: #fff;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
margin: 20px auto;
}
th, td {
padding: 10px 12px;
border: 1px solid #ddd;
text-align: center;
}
th {
background: #f0f0f0;
color: #444;
}
tr:nth-child(even) {
background: #fafafa;
}
</style>
</head>
<body>
<h1>Access Control List</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>CardNumber</th>
<th>PIN</th>
<th>StartDate</th>
<th>EndDate</th>
<%
// Get door columns by filtering keys not in known columns
const knownCols = ['Name', 'CardNumber', 'PIN', 'StartDate', 'EndDate'];
const doors = acl.length > 0
? Object.keys(acl[0]).filter(col => !knownCols.includes(col))
: [];
doors.forEach(function(door) {
%>
<th><%= door %></th>
<% }); %>
</tr>
</thead>
<tbody>
<% acl.forEach(function(row) { %>
<tr>
<td><%= row.Name %></td>
<td><%= row.CardNumber %></td>
<td><%= row.PIN %></td>
<td><%= row.StartDate %></td>
<td><%= row.EndDate %></td>
<% doors.forEach(function(door) { %>
<td>
<% if (row[door]) { %>
<% } else { %>
<% } %>
</td>
<% }); %>
</tr>
<% }); %>
</tbody>
</table>
</body>
</html>

View file

@ -52,7 +52,7 @@
<form action="/event-logs" method="get">
<button type="submit">Event Logs</button>
</form>
<form action="/access-control-list" method="get">
<form action="/acl" method="get">
<button type="submit">Access Control List</button>
</form>
<form action="/audit-logs" method="get">