From 645f13b285698896a69654d110392c6bcec1ef2c Mon Sep 17 00:00:00 2001
From: ToxicMushroom <32853531+ToxicMushroom@users.noreply.github.com>
Date: Thu, 13 Feb 2025 23:46:35 +0100
Subject: [PATCH] patch up rebase

---
 Cargo.lock                                    | 14 ++++
 server/core/src/https/views/constants.rs      | 22 +++---
 server/core/src/https/views/profile.rs        | 76 +++++++++++--------
 .../user_settings_profile_partial.html        |  6 +-
 4 files changed, 74 insertions(+), 44 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index fe306e47e..0f4fa00ad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -391,6 +391,7 @@ dependencies = [
  "multer",
  "pin-project-lite",
  "serde",
+ "serde_html_form",
  "tower 0.5.2",
  "tower-layer",
  "tower-service",
@@ -5136,6 +5137,19 @@ dependencies = [
  "syn 2.0.100",
 ]
 
+[[package]]
+name = "serde_html_form"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d2de91cf02bbc07cde38891769ccd5d4f073d22a40683aa4bc7a95781aaa2c4"
+dependencies = [
+ "form_urlencoded",
+ "indexmap 2.7.1",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
 [[package]]
 name = "serde_json"
 version = "1.0.140"
diff --git a/server/core/src/https/views/constants.rs b/server/core/src/https/views/constants.rs
index 8e4167941..7276e8c2f 100644
--- a/server/core/src/https/views/constants.rs
+++ b/server/core/src/https/views/constants.rs
@@ -9,17 +9,17 @@ pub(crate) enum ProfileMenuItems {
     UnixPassword,
 }
 
-pub(crate) enum UiMessage {
-    UnlockEdit,
-}
-
-impl std::fmt::Display for UiMessage {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            UiMessage::UnlockEdit => write!(f, "Unlock Edit 🔒"),
-        }
-    }
-}
+// pub(crate) enum UiMessage {
+//     UnlockEdit,
+// }
+//
+// impl std::fmt::Display for UiMessage {
+//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+//         match self {
+//             UiMessage::UnlockEdit => write!(f, "Unlock Edit 🔒"),
+//         }
+//     }
+// }
 
 pub(crate) enum Urls {
     Apps,
diff --git a/server/core/src/https/views/profile.rs b/server/core/src/https/views/profile.rs
index 93e58ea3b..99ff596fc 100644
--- a/server/core/src/https/views/profile.rs
+++ b/server/core/src/https/views/profile.rs
@@ -1,20 +1,34 @@
+use super::constants::{ProfileMenuItems, Urls};
+use super::errors::HtmxError;
+use super::login::{LoginDisplayCtx, Reauth, ReauthPurpose};
+use super::navbar::NavbarCtx;
 use crate::https::errors::WebError;
 use crate::https::extractors::{DomainInfo, VerifiedClientInformation};
 use crate::https::middleware::KOpId;
 use crate::https::ServerState;
 use askama::Template;
+use askama_axum::IntoResponse;
 use axum::extract::State;
+use axum::http::Uri;
 use axum::response::Response;
 use axum::Extension;
 use axum_extra::extract::cookie::CookieJar;
-use axum_htmx::{HxPushUrl, HxRequest};
+use axum_extra::extract::Form;
+use axum_htmx::{HxEvent, HxPushUrl, HxResponseTrigger};
 use futures_util::TryFutureExt;
+use kanidm_proto::attribute::Attribute;
+use kanidm_proto::constants::{ATTR_DISPLAYNAME, ATTR_LEGALNAME, ATTR_MAIL};
 use kanidm_proto::internal::UserAuthToken;
-
-use super::constants::{ProfileMenuItems, UiMessage, Urls};
-use super::errors::HtmxError;
-use super::login::{LoginDisplayCtx, Reauth, ReauthPurpose};
-use super::navbar::NavbarCtx;
+use kanidm_proto::v1::Entry;
+use kanidmd_lib::filter::{f_eq, f_id, Filter};
+use kanidmd_lib::prelude::f_and;
+use kanidmd_lib::prelude::PartialValue;
+use kanidmd_lib::prelude::FC;
+use serde::Deserialize;
+use serde::Serialize;
+use std::fmt;
+use std::fmt::Display;
+use std::fmt::Formatter;
 
 #[derive(Template)]
 #[template(path = "user_settings.html")]
@@ -28,8 +42,7 @@ pub(crate) struct ProfileView {
 struct ProfilePartialView {
     menu_active_item: ProfileMenuItems,
     can_rw: bool,
-    attrs: ProfileAttributes,
-    posix_enabled: bool,
+    attrs: ProfileAttributes
 }
 
 #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -45,9 +58,10 @@ pub(crate) struct ProfileAttributes {
 #[derive(Template, Clone)]
 #[template(path = "user_settings/profile_changes_partial.html")]
 struct ProfileChangesPartialView {
+    menu_active_item: ProfileMenuItems,
     can_rw: bool,
     attrs: ProfileAttributes,
-    new_attrs: ProfileAttributes
+    new_attrs: ProfileAttributes,
 }
 
 #[derive(Template, Clone)]
@@ -75,7 +89,7 @@ pub(crate) async fn view_profile_get(
 ) -> Result<ProfileView, WebError> {
     let uat: UserAuthToken = state
         .qe_r_ref
-        .handle_whoami_uat(client_auth_info, kopid.eventid)
+        .handle_whoami_uat(client_auth_info.clone(), kopid.eventid)
         .await?;
 
     let filter = filter_all!(f_and!([f_eq(
@@ -85,7 +99,6 @@ pub(crate) async fn view_profile_get(
     let base: Vec<Entry> = state
         .qe_r_ref
         .handle_internalsearch(client_auth_info.clone(), filter, None, kopid.eventid)
-        .map_err(|op_err| HtmxError::new(&kopid, op_err))
         .await?;
 
     let self_entry = base.first().expect("Self no longer exists");
@@ -109,22 +122,23 @@ pub(crate) async fn view_profile_get(
                 legal_name: "hardcoded".to_string(),
                 emails,
                 primary_email,
-
             },
+        },
     })
 }
 
-
 pub(crate) async fn view_profile_diff_start_save_post(
     State(state): State<ServerState>,
     Extension(kopid): Extension<KOpId>,
     VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
+    DomainInfo(domain_info): DomainInfo,
+    // Form must be the last parameter because it consumes the request body
     Form(new_attrs): Form<ProfileAttributes>,
 ) -> axum::response::Result<Response> {
     let uat: UserAuthToken = state
         .qe_r_ref
         .handle_whoami_uat(client_auth_info.clone(), kopid.eventid)
-        .map_err(|op_err| HtmxError::new(&kopid, op_err))
+        .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
         .await?;
 
     let time = time::OffsetDateTime::now_utc() + time::Duration::new(60, 0);
@@ -137,7 +151,7 @@ pub(crate) async fn view_profile_diff_start_save_post(
     let base: Vec<Entry> = state
         .qe_r_ref
         .handle_internalsearch(client_auth_info.clone(), filter, None, kopid.eventid)
-        .map_err(|op_err| HtmxError::new(&kopid, op_err))
+        .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info))
         .await?;
 
     let self_entry = base.first().expect("Self no longer exists");
@@ -146,6 +160,7 @@ pub(crate) async fn view_profile_diff_start_save_post(
     let primary_email = emails.first().cloned();
 
     let profile_view = ProfileChangesPartialView {
+        menu_active_item: ProfileMenuItems::UserProfile,
         can_rw,
         attrs: ProfileAttributes {
             account_name: uat.name().to_string(),
@@ -154,13 +169,12 @@ pub(crate) async fn view_profile_diff_start_save_post(
             emails,
             primary_email,
         },
-        new_attrs,
-        posix_enabled: true,
+        new_attrs
     };
 
     Ok((
         HxPushUrl(Uri::from_static("/ui/profile/diff")),
-        HtmlTemplate(profile_view),
+        profile_view,
     )
         .into_response())
 }
@@ -168,14 +182,15 @@ pub(crate) async fn view_profile_diff_start_save_post(
 pub(crate) async fn view_profile_diff_confirm_save_post(
     State(state): State<ServerState>,
     Extension(kopid): Extension<KOpId>,
-    HxRequest(hx_request): HxRequest,
     VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
+    DomainInfo(domain_info): DomainInfo,
+    // Form must be the last parameter because it consumes the request body
     Form(new_attrs): Form<ProfileAttributes>,
 ) -> axum::response::Result<Response> {
     let uat: UserAuthToken = state
         .qe_r_ref
         .handle_whoami_uat(client_auth_info.clone(), kopid.eventid)
-        .map_err(|op_err| HtmxError::new(&kopid, op_err))
+        .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
         .await?;
     dbg!(&new_attrs);
 
@@ -191,7 +206,7 @@ pub(crate) async fn view_profile_diff_confirm_save_post(
             filter.clone(),
             kopid.eventid,
         )
-        .map_err(|op_err| HtmxError::new(&kopid, op_err))
+        .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
         .await?;
 
     state
@@ -204,7 +219,7 @@ pub(crate) async fn view_profile_diff_confirm_save_post(
             filter.clone(),
             kopid.eventid,
         )
-        .map_err(|op_err| HtmxError::new(&kopid, op_err))
+        .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
         .await?;
 
     state
@@ -217,7 +232,7 @@ pub(crate) async fn view_profile_diff_confirm_save_post(
             filter.clone(),
             kopid.eventid,
         )
-        .map_err(|op_err| HtmxError::new(&kopid, op_err))
+        .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
         .await?;
 
     // TODO: These are normally not permitted, user should be prevented from changing non modifiable fields in the UI though
@@ -248,13 +263,15 @@ pub(crate) async fn view_profile_diff_confirm_save_post(
     //     .await?;
 
     // TODO: Calling this here returns the old attributes
-    view_profile_get(
+    match view_profile_get(
         State(state),
         Extension(kopid),
-        HxRequest(hx_request),
         VerifiedClientInformation(client_auth_info),
-    )
-        .await
+        DomainInfo(domain_info)
+    ).await {
+        Ok(pv) => Ok(pv.into_response()),
+        Err(e) => Ok(e.into_response()),
+    }
 }
 
 // Sends the user a new email input to fill in :)
@@ -267,14 +284,13 @@ pub(crate) async fn view_new_email_entry_partial(
         HxResponseTrigger::after_swap([HxEvent::new("addEmailSwapped".to_string())]);
     Ok((
         passkey_init_trigger,
-        HtmlTemplate(FormModEntryModListPartial {
+        FormModEntryModListPartial {
             can_rw: true,
             r#type: "email".to_string(),
             name: "emails[]".to_string(),
             value: "".to_string(),
             invalid_feedback: "Please enter a valid email address.".to_string(),
-        })
-            .into_response(),
+        },
     )
         .into_response())
 }
diff --git a/server/core/templates/user_settings_profile_partial.html b/server/core/templates/user_settings_profile_partial.html
index d4ec7487d..193684c2f 100644
--- a/server/core/templates/user_settings_profile_partial.html
+++ b/server/core/templates/user_settings_profile_partial.html
@@ -10,21 +10,21 @@ Profile
     <div class="mb-2 row">
         <label for="profileUserName" class="col-12 col-md-3 col-lg-2 col-form-label">User name</label>
         <div class="col-12 col-md-6 col-lg-5">
-            <input type="text" readonly class="form-control-plaintext" id="profileUserName" value="(( account_name ))">
+            <input type="text" readonly class="form-control-plaintext" id="profileUserName" value="(( attrs.account_name ))">
         </div>
     </div>
     
     <div class="mb-2 row">
         <label for="profileDisplayName" class="col-12 col-md-3 col-lg-2 col-form-label">Display name</label>
         <div class="col-12 col-md-6 col-lg-5">
-            <input type="text" class="form-control-plaintext" id="profileDisplayName" value="(( display_name ))">
+            <input type="text" class="form-control-plaintext" id="profileDisplayName" value="(( attrs.display_name ))">
         </div>
     </div>
 
     <div class="mb-2 row">
         <label for="profileLegalName" class="col-12 col-md-3 col-lg-2 col-form-label">Legal name</label>
         <div class="col-12 col-md-6 col-lg-5">
-            <input type="text" class="form-control-plaintext" id="profileLegalName" value="(( legal_name ))">
+            <input type="text" class="form-control-plaintext" id="profileLegalName" value="(( attrs.legal_name ))">
         </div>
     </div>