Test ACL page
This commit is contained in:
parent
e03af9da37
commit
2ed971fbcc
|
@ -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
139
routes/acl.js
Normal 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;
|
|
@ -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
82
views/acl.ejs
Normal 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>
|
|
@ -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">
|
||||
|
|
Loading…
Reference in a new issue