web synchronized with the new changes

This commit is contained in:
Priec
2026-08-09 16:30:27 +02:00
parent e66b025188
commit fd551ef7fb
44 changed files with 1215 additions and 88 deletions

View File

@@ -0,0 +1,160 @@
use axum::{
extract::{Query, State},
http::{HeaderMap, HeaderValue, StatusCode, header},
response::{Html, IntoResponse, Redirect, Response},
};
use axum_extra::extract::Form;
use crate::{
AppState,
auth::{
AddRoleRequest, AssignUserRoleRequest, GrantPermissionRequest, RemoveRoleRequest,
RevokePermissionRequest,
},
services::{authenticated_request, reject_cross_site},
};
use super::{
loader,
state::{AddRoleForm, AssignRoleForm, LoadError, PermissionForm, RoleForm, Selection},
ui,
};
pub(crate) async fn page(
State(state): State<AppState>,
headers: HeaderMap,
Query(selection): Query<Selection>,
) -> Response {
match loader::load_page(state, &headers, selection).await {
Ok(page) => Html(ui::render_page(&page)).into_response(),
Err(error) => load_error(error),
}
}
pub(crate) async fn add_role(
State(state): State<AppState>,
headers: HeaderMap,
Form(form): Form<AddRoleForm>,
) -> Response {
let request_headers = headers.clone();
mutate(&headers, async move {
let mut auth = state.auth;
auth.add_role(authenticated_request(&request_headers, AddRoleRequest {
name: form.name.trim().to_string(),
parent: form.parent.trim().to_string(),
}).map_err(|_| "Missing session".to_string())?)
.await.map_err(|error| error.message().to_string())?;
Ok("Role created. Sign in again to continue.")
}).await
}
pub(crate) async fn remove_role(
State(state): State<AppState>,
headers: HeaderMap,
Form(form): Form<RoleForm>,
) -> Response {
let request_headers = headers.clone();
mutate(&headers, async move {
let mut auth = state.auth;
auth.remove_role(authenticated_request(&request_headers, RemoveRoleRequest {
name: form.role,
}).map_err(|_| "Missing session".to_string())?)
.await.map_err(|error| error.message().to_string())?;
Ok("Role removed. Sign in again to continue.")
}).await
}
pub(crate) async fn grant(
State(state): State<AppState>,
headers: HeaderMap,
Form(form): Form<PermissionForm>,
) -> Response {
let request_headers = headers.clone();
mutate(&headers, async move {
let mut auth = state.auth;
auth.grant_permission(authenticated_request(&request_headers, GrantPermissionRequest {
role: form.role,
object: form.object,
action: form.action,
}).map_err(|_| "Missing session".to_string())?)
.await.map_err(|error| error.message().to_string())?;
Ok("Permission granted. Sign in again to continue.")
}).await
}
pub(crate) async fn revoke(
State(state): State<AppState>,
headers: HeaderMap,
Form(form): Form<PermissionForm>,
) -> Response {
let request_headers = headers.clone();
mutate(&headers, async move {
let mut auth = state.auth;
auth.revoke_permission(authenticated_request(&request_headers, RevokePermissionRequest {
role: form.role,
object: form.object,
action: form.action,
}).map_err(|_| "Missing session".to_string())?)
.await.map_err(|error| error.message().to_string())?;
Ok("Permission revoked. Sign in again to continue.")
}).await
}
pub(crate) async fn assign_user_role(
State(state): State<AppState>,
headers: HeaderMap,
Form(form): Form<AssignRoleForm>,
) -> Response {
let request_headers = headers.clone();
mutate(&headers, async move {
let mut auth = state.auth;
auth.assign_user_role(authenticated_request(&request_headers, AssignUserRoleRequest {
username: form.username,
role: form.role,
}).map_err(|_| "Missing session".to_string())?)
.await.map_err(|error| error.message().to_string())?;
Ok("User role changed. Sign in again to continue.")
}).await
}
async fn mutate<F>(headers: &HeaderMap, operation: F) -> Response
where
F: std::future::Future<Output = Result<&'static str, String>>,
{
if let Some(rejection) = reject_cross_site(headers) {
return rejection;
}
match operation.await {
Ok(_) => stale_session_response(),
Err(message) => (
StatusCode::UNPROCESSABLE_ENTITY,
Html(ui::render_mutation_error(&message)),
).into_response(),
}
}
fn stale_session_response() -> Response {
let mut response = StatusCode::SEE_OTHER.into_response();
response.headers_mut().insert(
header::SET_COOKIE,
HeaderValue::from_static("analytics_token=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0"),
);
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_static("/login?permissions_changed=1"),
);
response.headers_mut().insert(
"hx-redirect",
HeaderValue::from_static("/login?permissions_changed=1"),
);
response
}
fn load_error(error: LoadError) -> Response {
match error {
LoadError::Unauthenticated => Redirect::to("/login").into_response(),
LoadError::Forbidden => (StatusCode::FORBIDDEN, Html(ui::render_error("You do not have role or user management permission."))).into_response(),
LoadError::InvalidSelection(message) => (StatusCode::BAD_REQUEST, Html(ui::render_error(&message))).into_response(),
LoadError::Backend(message) => (StatusCode::BAD_GATEWAY, Html(ui::render_error(&message))).into_response(),
}
}