From 8db3febfce8057011156e80a371c4312a79be4cc Mon Sep 17 00:00:00 2001
From: oddlama <oddlama@oddlama.org>
Date: Sat, 14 Feb 2026 12:27:00 +0100
Subject: [PATCH 2/2] recover account

---
 server/core/src/actors/internal.rs |  5 +++--
 server/core/src/admin.rs           |  6 +++---
 server/daemon/src/main.rs          | 24 +++++++++++++++++++++++-
 server/daemon/src/opt.rs           |  7 +++++++
 4 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/server/core/src/actors/internal.rs b/server/core/src/actors/internal.rs
index abcc1b27c..2a63d0e9d 100644
--- a/server/core/src/actors/internal.rs
+++ b/server/core/src/actors/internal.rs
@@ -189,17 +189,18 @@ impl QueryServerWriteV1 {
 
     #[instrument(
         level = "info",
-        skip(self, eventid),
+        skip(self, password, eventid),
         fields(uuid = ?eventid)
     )]
     pub(crate) async fn handle_admin_recover_account(
         &self,
         name: String,
+        password: Option<String>,
         eventid: Uuid,
     ) -> Result<String, OperationError> {
         let ct = duration_from_epoch_now();
         let mut idms_prox_write = self.idms.proxy_write(ct).await?;
-        let pw = idms_prox_write.recover_account(name.as_str(), None)?;
+        let pw = idms_prox_write.recover_account(name.as_str(), password.as_deref())?;
 
         idms_prox_write.commit().map(|()| pw)
     }
diff --git a/server/core/src/admin.rs b/server/core/src/admin.rs
index e00eb0476..175a6f661 100644
--- a/server/core/src/admin.rs
+++ b/server/core/src/admin.rs
@@ -23,7 +23,7 @@ pub use kanidm_proto::internal::{
 
 #[derive(Serialize, Deserialize, Debug)]
 pub enum AdminTaskRequest {
-    RecoverAccount { name: String },
+    RecoverAccount { name: String, password: Option<String> },
     DisableAccount { name: String },
     ShowReplicationCertificate,
     RenewReplicationCertificate,
@@ -341,8 +341,8 @@ async fn handle_client(
 
         let resp = async {
             match req {
-                AdminTaskRequest::RecoverAccount { name } => {
-                    match server_rw.handle_admin_recover_account(name, eventid).await {
+                AdminTaskRequest::RecoverAccount { name, password } => {
+                    match server_rw.handle_admin_recover_account(name, password, eventid).await {
                         Ok(password) => AdminTaskResponse::RecoverAccount { password },
                         Err(e) => {
                             error!(err = ?e, "error during recover-account");
diff --git a/server/daemon/src/main.rs b/server/daemon/src/main.rs
index 611022a63..0b2f863e4 100644
--- a/server/daemon/src/main.rs
+++ b/server/daemon/src/main.rs
@@ -370,11 +370,32 @@ fn check_file_ownership(opt: &KanidmdParser) -> Result<(), ExitCode> {
 
 async fn scripting_command(cmd: ScriptingCommand, config: Configuration) -> ExitCode {
     match cmd {
-        ScriptingCommand::RecoverAccount { name } => {
+        ScriptingCommand::RecoverAccount { name, from_environment } => {
+            let password = if from_environment {
+                match std::env::var("KANIDM_RECOVER_ACCOUNT_PASSWORD_FILE") {
+                    Ok(path) => match tokio::fs::read_to_string(&path).await {
+                        Ok(contents) => Some(contents),
+                        Err(e) => {
+                            error!("Failed to read password file '{}': {}", path, e);
+                            return ExitCode::FAILURE;
+                        }
+                    },
+                    Err(_) => match std::env::var("KANIDM_RECOVER_ACCOUNT_PASSWORD") {
+                        Ok(val) => Some(val),
+                        Err(_) => {
+                            error!("Neither KANIDM_RECOVER_ACCOUNT_PASSWORD_FILE nor KANIDM_RECOVER_ACCOUNT_PASSWORD was set");
+                            return ExitCode::FAILURE;
+                        }
+                    }
+                }
+            } else {
+                None
+            };
             submit_admin_req_json(
                 config.adminbindpath.as_str(),
                 AdminTaskRequest::RecoverAccount {
                     name: name.to_owned(),
+                    password,
                 },
             )
             .await;
@@ -998,6 +1019,7 @@ async fn kanidm_main(config: Configuration, opt: KanidmdParser) -> ExitCode {
                 config.adminbindpath.as_str(),
                 AdminTaskRequest::RecoverAccount {
                     name: name.to_owned(),
+                    password: None,
                 },
             )
             .await;
diff --git a/server/daemon/src/opt.rs b/server/daemon/src/opt.rs
index ba5d00fc7..f1497f6dc 100644
--- a/server/daemon/src/opt.rs
+++ b/server/daemon/src/opt.rs
@@ -128,6 +128,13 @@ enum ScriptingCommand {
         #[clap(value_parser)]
         /// The account name to recover credentials for.
         name: String,
+        /// Use a password given via an environment variable.
+        /// - `KANIDM_RECOVER_ACCOUNT_PASSWORD_FILE` takes precedence and reads the desired
+        ///    password from the given file
+        /// - `KANIDM_RECOVER_ACCOUNT_PASSWORD` directly takes a
+        ///    password - beware that this will leave the password in the environment
+        #[clap(long = "from-environment")]
+        from_environment: bool,
     },
     /// Backup
     Backup {
-- 
2.52.0

