feat: implement user management with CRUD operations and UI integration

This commit is contained in:
Herbert Wolverson (aider) 2025-01-24 09:13:03 -06:00
parent 512a1940fa
commit 78ce738841
3 changed files with 177 additions and 5 deletions

View File

@ -1 +1,120 @@
// Placeholder for user management configuration
$(document).ready(() => {
loadUsers();
// Handle add user form submission
$('#add-user-form').on('submit', function(e) {
e.preventDefault();
const username = $('#username').val();
const password = $('#password').val();
const role = $('#role').val();
$.ajax({
type: "POST",
url: "/local-api/addUser",
data: JSON.stringify({
username: username,
password: password,
role: role
}),
contentType: 'application/json',
success: () => {
$('#username').val('');
$('#password').val('');
loadUsers();
},
error: () => {
alert('Failed to add user');
}
});
});
// Handle edit user form submission
$('#save-user-changes').on('click', function() {
const username = $('#edit-username').val();
const password = $('#edit-password').val();
const role = $('#edit-role').val();
$.ajax({
type: "POST",
url: "/local-api/updateUser",
data: JSON.stringify({
username: username,
password: password,
role: role
}),
contentType: 'application/json',
success: () => {
$('#editUserModal').modal('hide');
loadUsers();
},
error: () => {
alert('Failed to update user');
}
});
});
});
function loadUsers() {
$.get('/local-api/getUsers', (users) => {
const userList = $('#users-list');
userList.empty();
if (users.length === 0) {
userList.html('<div class="alert alert-info">No users found</div>');
return;
}
const table = $('<table class="table table-striped">')
.append('<thead><tr><th>Username</th><th>Role</th><th>Actions</th></tr></thead>');
const tbody = $('<tbody>');
users.forEach(user => {
const row = $('<tr>')
.append(`<td>${user.username}</td>`)
.append(`<td>${user.role}</td>`)
.append(`<td>
<button class="btn btn-sm btn-primary edit-user" data-username="${user.username}">
<i class="fa fa-edit"></i> Edit
</button>
<button class="btn btn-sm btn-danger delete-user" data-username="${user.username}">
<i class="fa fa-trash"></i> Delete
</button>
</td>`);
tbody.append(row);
});
table.append(tbody);
userList.append(table);
// Attach edit handlers
$('.edit-user').on('click', function() {
const username = $(this).data('username');
const user = users.find(u => u.username === username);
$('#edit-username').val(user.username);
$('#edit-role').val(user.role);
$('#editUserModal').modal('show');
});
// Attach delete handlers
$('.delete-user').on('click', function() {
if (confirm('Are you sure you want to delete this user?')) {
const username = $(this).data('username');
$.ajax({
type: "POST",
url: "/local-api/deleteUser",
data: JSON.stringify({ username: username }),
contentType: 'application/json',
success: () => {
loadUsers();
},
error: () => {
alert('Failed to delete user');
}
});
}
});
}).fail(() => {
$('#users-list').html('<div class="alert alert-danger">Failed to load users</div>');
});
}

View File

@ -47,6 +47,9 @@ pub fn local_api() -> Router {
.route("/updateConfig", post(config::update_lqosd_config))
.route("/updateNetworkAndDevices", post(config::update_network_and_devices))
.route("/getUsers", get(config::get_users))
.route("/addUser", post(config::add_user))
.route("/updateUser", post(config::update_user))
.route("/deleteUser", post(config::delete_user))
.route("/circuitById", post(circuit::get_circuit_by_id))
.route("/requestAnalysis/:ip", get(packet_analysis::request_analysis))
.route("/pcapDump/:id", get(packet_analysis::pcap_dump))
@ -67,4 +70,4 @@ pub fn local_api() -> Router {
.route("/ltsCake/:seconds", get(lts::cake_period))
.layer(CorsLayer::very_permissive())
.route_layer(axum::middleware::from_fn(auth_layer))
}
}

View File

@ -4,10 +4,11 @@ use axum::http::StatusCode;
use lqos_config::{Config, ConfigShapedDevices, ShapedDevice, WebUser};
use crate::node_manager::auth::LoginResult;
use default_net::get_interfaces;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use lqos_bus::{bus_request, BusRequest};
use crate::shaped_devices_tracker::SHAPED_DEVICES;
use lqos_config::authentication::{WebUser, WebUsers};
pub async fn admin_check(
Extension(login): Extension<LoginResult>
@ -119,13 +120,62 @@ pub async fn update_network_and_devices(
"Ok".to_string()
}
#[derive(Serialize, Deserialize)]
struct UserRequest {
username: String,
password: String,
role: String,
}
pub async fn get_users(
Extension(login): Extension<LoginResult>,
) -> Result<Json<Vec<WebUser>>, StatusCode> {
if login != LoginResult::Admin {
return Err(StatusCode::FORBIDDEN);
}
let users = lqos_config::WebUsers::load_or_create()
let users = WebUsers::load_or_create()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(users.get_users()))
}
}
pub async fn add_user(
Extension(login): Extension<LoginResult>,
Json(data): Json<UserRequest>,
) -> Result<String, StatusCode> {
if login != LoginResult::Admin {
return Err(StatusCode::FORBIDDEN);
}
let mut users = WebUsers::load_or_create()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
users.add_or_update_user(&data.username, &data.password, data.role.into())
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok("User added".to_string())
}
pub async fn update_user(
Extension(login): Extension<LoginResult>,
Json(data): Json<UserRequest>,
) -> Result<String, StatusCode> {
if login != LoginResult::Admin {
return Err(StatusCode::FORBIDDEN);
}
let mut users = WebUsers::load_or_create()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
users.add_or_update_user(&data.username, &data.password, data.role.into())
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok("User updated".to_string())
}
pub async fn delete_user(
Extension(login): Extension<LoginResult>,
Json(data): Json<UserRequest>,
) -> Result<String, StatusCode> {
if login != LoginResult::Admin {
return Err(StatusCode::FORBIDDEN);
}
let mut users = WebUsers::load_or_create()
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
users.remove_user(&data.username)
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok("User deleted".to_string())
}