web permission system updated

This commit is contained in:
Priec
2026-08-09 18:49:54 +02:00
parent fd551ef7fb
commit ff044d65f1
12 changed files with 40 additions and 34 deletions

View File

@@ -16,6 +16,7 @@ pub(crate) async fn load_page(
headers: &HeaderMap,
selection: Selection,
) -> Result<PermissionPageState, LoadError> {
let updated = selection.updated;
let mut auth = state.auth;
let authorization = auth
.get_authorization(
@@ -120,6 +121,7 @@ pub(crate) async fn load_page(
grantable_objects,
can_manage_roles,
can_manage_users,
updated,
})
}

View File

@@ -36,15 +36,17 @@ pub(crate) async fn add_role(
headers: HeaderMap,
Form(form): Form<AddRoleForm>,
) -> Response {
let role = form.name.trim().to_string();
let destination = format!("/admin/permissions?role={role}&updated=true");
let request_headers = headers.clone();
mutate(&headers, async move {
mutate(&headers, &destination, async move {
let mut auth = state.auth;
auth.add_role(authenticated_request(&request_headers, AddRoleRequest {
name: form.name.trim().to_string(),
name: role,
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.")
Ok(())
}).await
}
@@ -53,14 +55,15 @@ pub(crate) async fn remove_role(
headers: HeaderMap,
Form(form): Form<RoleForm>,
) -> Response {
let destination = "/admin/permissions?updated=true";
let request_headers = headers.clone();
mutate(&headers, async move {
mutate(&headers, destination, 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.")
Ok(())
}).await
}
@@ -69,8 +72,9 @@ pub(crate) async fn grant(
headers: HeaderMap,
Form(form): Form<PermissionForm>,
) -> Response {
let destination = format!("/admin/permissions?role={}&updated=true", form.role);
let request_headers = headers.clone();
mutate(&headers, async move {
mutate(&headers, &destination, async move {
let mut auth = state.auth;
auth.grant_permission(authenticated_request(&request_headers, GrantPermissionRequest {
role: form.role,
@@ -78,7 +82,7 @@ pub(crate) async fn grant(
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.")
Ok(())
}).await
}
@@ -87,8 +91,9 @@ pub(crate) async fn revoke(
headers: HeaderMap,
Form(form): Form<PermissionForm>,
) -> Response {
let destination = format!("/admin/permissions?role={}&updated=true", form.role);
let request_headers = headers.clone();
mutate(&headers, async move {
mutate(&headers, &destination, async move {
let mut auth = state.auth;
auth.revoke_permission(authenticated_request(&request_headers, RevokePermissionRequest {
role: form.role,
@@ -96,7 +101,7 @@ pub(crate) async fn revoke(
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.")
Ok(())
}).await
}
@@ -105,27 +110,28 @@ pub(crate) async fn assign_user_role(
headers: HeaderMap,
Form(form): Form<AssignRoleForm>,
) -> Response {
let destination = "/admin/permissions?updated=true";
let request_headers = headers.clone();
mutate(&headers, async move {
mutate(&headers, destination, 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.")
Ok(())
}).await
}
async fn mutate<F>(headers: &HeaderMap, operation: F) -> Response
async fn mutate<F>(headers: &HeaderMap, destination: &str, operation: F) -> Response
where
F: std::future::Future<Output = Result<&'static str, String>>,
F: std::future::Future<Output = Result<(), String>>,
{
if let Some(rejection) = reject_cross_site(headers) {
return rejection;
}
match operation.await {
Ok(_) => stale_session_response(),
Ok(()) => success_redirect(destination),
Err(message) => (
StatusCode::UNPROCESSABLE_ENTITY,
Html(ui::render_mutation_error(&message)),
@@ -133,19 +139,18 @@ where
}
}
fn stale_session_response() -> Response {
fn success_redirect(destination: &str) -> 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"),
);
let Ok(destination) = HeaderValue::try_from(destination) else {
return (StatusCode::INTERNAL_SERVER_ERROR, "Invalid redirect").into_response();
};
response.headers_mut().insert(
header::LOCATION,
HeaderValue::from_static("/login?permissions_changed=1"),
destination.clone(),
);
response.headers_mut().insert(
"hx-redirect",
HeaderValue::from_static("/login?permissions_changed=1"),
destination,
);
response
}

View File

@@ -4,6 +4,8 @@ use crate::auth::{GrantableObject, Permission, Role, UserSummary};
pub(crate) struct Selection {
#[serde(default)]
pub role: String,
#[serde(default)]
pub updated: bool,
}
#[derive(Clone, Debug, Default, serde::Deserialize)]
@@ -41,6 +43,7 @@ pub(crate) struct PermissionPageState {
pub grantable_objects: Vec<GrantableObject>,
pub can_manage_roles: bool,
pub can_manage_users: bool,
pub updated: bool,
}
impl PermissionPageState {

View File

@@ -85,6 +85,7 @@ mod tests {
}],
can_manage_roles: true,
can_manage_users: true,
updated: false,
};
let html = render_page(&page);

View File

@@ -16,7 +16,6 @@ pub(crate) async fn login_page(
) -> Html<String> {
Html(ui::render_page(
Nav::new(&headers, "login"),
query.permissions_changed,
query.initial_password_set,
))
}

View File

@@ -7,8 +7,6 @@ pub(crate) struct LoginInput {
#[derive(Default, serde::Deserialize)]
pub(crate) struct LoginQuery {
#[serde(default)]
pub permissions_changed: bool,
#[serde(default)]
pub initial_password_set: bool,
}

View File

@@ -7,18 +7,15 @@ use crate::ui::{Alert, Nav, render};
#[template(path = "pages/login/login.html")]
struct LoginPage {
nav: Nav,
permissions_changed: bool,
initial_password_set: bool,
}
pub(crate) fn render_page(
nav: Nav,
permissions_changed: bool,
initial_password_set: bool,
) -> String {
render(&LoginPage {
nav,
permissions_changed,
initial_password_set,
})
}