git.delta.rocks / remowt / refs/commits / 5cb6be498b69

difftreelog

feat properly cancel agent task

wplzsrwuYaroslav Bolyukin2024-08-12parent: #9ee216a.patch.diff
in: trunk

3 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -307,16 +307,22 @@
 ]
 
 [[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
 name = "bytes"
-version = "1.6.1"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952"
+checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
 
 [[package]]
 name = "cc"
-version = "1.1.6"
+version = "1.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f"
+checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc"
 
 [[package]]
 name = "cexpr"
@@ -644,9 +650,9 @@
 
 [[package]]
 name = "indexmap"
-version = "2.2.6"
+version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
 dependencies = [
  "equivalent",
  "hashbrown",
@@ -905,9 +911,12 @@
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.17"
+version = "0.2.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
 
 [[package]]
 name = "proc-macro-crate"
@@ -968,9 +977,9 @@
 
 [[package]]
 name = "regex"
-version = "1.10.5"
+version = "1.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
+checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1089,11 +1098,12 @@
 
 [[package]]
 name = "serde_json"
-version = "1.0.120"
+version = "1.0.122"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5"
+checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
 dependencies = [
  "itoa",
+ "memchr",
  "ryu",
  "serde",
 ]
@@ -1205,12 +1215,13 @@
 
 [[package]]
 name = "tempfile"
-version = "3.10.1"
+version = "3.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53"
 dependencies = [
  "cfg-if",
  "fastrand",
+ "once_cell",
  "rustix",
  "windows-sys",
 ]
@@ -1276,9 +1287,9 @@
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.7"
+version = "0.6.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
 
 [[package]]
 name = "toml_edit"
@@ -1606,6 +1617,27 @@
 ]
 
 [[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.72",
+]
+
+[[package]]
 name = "zvariant"
 version = "4.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
modifiedcmds/remowt-agent/src/main.rsdiffbeforeafterboth
after · cmds/remowt-agent/src/main.rs
1use std::borrow::Cow;2use std::collections::{BTreeMap, HashMap};3use std::io::{stdout, Write};4use std::marker::PhantomData;5use std::sync::{Arc, Mutex, OnceLock};6use std::{future, process};78use clap::Parser;9use polkit_shared::{emphasize, BackendRequest, Identity, PidDisplay};10use tokio::runtime::Handle;11use tokio::task::{AbortHandle, JoinHandle, LocalSet};12use tracing::{info, trace};13use ui_prompt::dbus::DbusPrompterInterface;14use ui_prompt::rofi::RofiPrompter;15use ui_prompt::{PrependSourcePrompter, Prompter, Source};16use zbus::zvariant::{OwnedValue, Str};17use zbus::{fdo, ObjectServer};18use zbus::{interface, proxy, Connection};19use zbus_polkit::policykit1::Subject;2021struct TemporaryPrompterInterface<P: Prompter + Send + Sync + 'static> {22    connection: Connection,23    path: String,24    _marker: PhantomData<P>,25}26impl<P: Prompter + Send + Sync + 'static> TemporaryPrompterInterface<P> {27    async fn new(connection: Connection, prompter: P) -> Self {28        let path = format!(29            "/remowt/prompters/{}",30            uuid::Uuid::new_v4().to_string().replace("-", "_")31        );32        let _ = connection33            .object_server()34            .at(path.clone(), DbusPrompterInterface(prompter))35            .await;36        Self {37            connection,38            path,39            _marker: PhantomData,40        }41    }42}43impl<P: Prompter + Send + Sync + 'static> Drop for TemporaryPrompterInterface<P> {44    fn drop(&mut self) {45        // FIXME: block_in_place prevents to moving to current_thread runtime46        // There should be a blocking way to remove ObjectServer listener.47        // As far as I can see, it is only async because of async RwLock, shouldn't it be48        // just a sync lock?49        tokio::task::block_in_place(move || {50            Handle::current().block_on(async {51                let _ = self52                    .connection53                    .object_server()54                    .remove::<DbusPrompterInterface<P>, String>(self.path.clone())55                    .await;56            });57        });58    }59}6061struct CancelTaskOnDrop {62    tasks: Arc<Mutex<HashMap<String, AbortHandle>>>,63    handle: String,64}65impl Drop for CancelTaskOnDrop {66    fn drop(&mut self) {67        info!("cancel on drop");68        if let Some(task) = self69            .tasks70            .lock()71            .expect("not poisoned")72            .remove(&self.handle)73        {74            task.abort();75        }76    }77}7879struct Agent {80    helper: PolkitHelperProxy<'static>,81    tasks: Arc<Mutex<HashMap<String, AbortHandle>>>,82    connection: Connection,83}84impl Agent {85    async fn new(connection: Connection) -> anyhow::Result<Self> {86        Ok(Self {87            helper: PolkitHelperProxy::new(&connection).await?,88            tasks: Arc::new(Mutex::new(HashMap::new())),89            connection,90        })91    }92}9394#[interface(name = "org.freedesktop.PolicyKit1.AuthenticationAgent")]95impl Agent {96    /// BeginAuthentication method97    #[allow(clippy::too_many_arguments)]98    async fn begin_authentication(99        &self,100        action_id: String,101        message: String,102        icon_name: String,103        mut details: BTreeMap<String, String>,104        cookie: String,105        identities: Vec<Identity>,106    ) -> zbus::fdo::Result<()> {107        use std::fmt::Write;108        info!("begin auth");109        let _cancel_guard = Arc::new(OnceLock::new());110        let task = {111            let connection = self.connection.clone();112            let helper = self.helper.clone();113            let cookie = cookie.clone();114            let _cancel_guard = _cancel_guard.clone();115            tokio::task::spawn(async move {116                let _cancel_guard = _cancel_guard.clone();117                trace!("conversation task");118                let mut description = format!("{message}\n\n<b>Action id:</b> {action_id}",);119                if let Some(subject) = details.remove("polkit.caller-pid") {120                    let _ = write!(description, "\n<b>Caller:</b> ");121                    if let Ok(pid) = subject.parse::<u32>() {122                        let _ = write!(description, "{}", PidDisplay(pid));123                    } else {124                        let _ = write!(description, "{}", emphasize("invalid pid"));125                    }126                }127                if let Some(subject) = details.remove("polkit.subject-pid") {128                    let _ = write!(description, "\n<b>Subject:</b> ");129                    if let Ok(pid) = subject.parse::<u32>() {130                        let _ = write!(description, "{}", PidDisplay(pid));131                    } else {132                        let _ = write!(description, "{}", emphasize("invalid pid"));133                    }134                }135                let mut prompter = PrependSourcePrompter {136                    source: vec![Source(Cow::Borrowed("polkit agent"))],137                    description: description.clone(),138                    prompter: RofiPrompter,139                };140141                let identity_displays: Vec<String> =142                    identities.iter().map(|v| v.to_string()).collect();143                let identity_displays: Vec<&str> =144                    identity_displays.iter().map(|v| v.as_str()).collect();145                info!("choose identity");146                let choosen_identity = match identity_displays.len() {147                    0 => {148                        return Err(fdo::Error::AuthFailed(149                            "no identity to authenticate as".to_owned(),150                        ))151                    }152                    1 => 0,153                    _ => {154                        prompter155                            .prompt_enum(156                                "Identity",157                                "Select identity to use for polkit authorization",158                                &identity_displays,159                                &[],160                            )161                            .await?162                    }163                };164                info!("identity chosen");165166                let _ = write!(167                    description,168                    "\n<b>Identity:</b> {}",169                    identities[choosen_identity as usize]170                );171                prompter.description = description;172173                prompter.source.push(Source(Cow::Borrowed("polkit daemon")));174                // let connection = Connection::system().await?;175                // let helper = PolkitHelperProxy::new(&connection).await?;176                let prompter = TemporaryPrompterInterface::new(connection, prompter).await;177                info!("init conv");178                helper179                    .init_conversation(180                        BackendRequest {181                            cookie: cookie.to_owned(),182                            environment: HashMap::new(),183                            prompter_path: prompter.path.clone(),184                            // TODO: Let user choose185                            identity: identities[choosen_identity as usize].clone(),186                        }, // cookie.to_owned(), HashMap::new(), prompter.path.clone()187                    )188                    .await?;189                println!("ASKED");190                dbg!(action_id, message, icon_name, details, cookie, identities);191192                Ok(())193            })194        };195        self.tasks196            .lock()197            .unwrap()198            .insert(cookie.clone(), task.abort_handle());199        info!("abort handle stored");200        let _ = _cancel_guard.set(CancelTaskOnDrop {201            tasks: self.tasks.clone(),202            handle: cookie.clone(),203        });204205        let _ = task.await;206207        Ok(())208    }209210    /// CancelAuthentication method211    async fn cancel_authentication(&self, cookie: &str) -> zbus::fdo::Result<()> {212        info!("auth cancelled");213        if let Some(abort) = self.tasks.lock().unwrap().remove(cookie) {214            info!("abort handle found");215            abort.abort();216        }217        // debug!("Authentication cancled ! {cookie}");218        Ok(())219    }220}221222const OBJ_PATH: &str = "/org/freedesktop/PolicyKit1/AuthenticationAgent";223224#[proxy(225    interface = "lach.PolkitHelper",226    default_service = "lach.polkit.helper1",227    default_path = "/lach/PolkitHelper"228)]229trait PolkitHelper {230    fn init_conversation(&self, request: BackendRequest) -> zbus::Result<()>;231}232233#[derive(Parser)]234enum Opts {235    Agent,236    AskPass { description: String },237}238239#[tokio::main]240async fn main() -> anyhow::Result<()> {241    tracing_subscriber::fmt::init();242    let opts = Opts::parse();243244    match opts {245        Opts::Agent => {246            trace!("started");247            let conn = Connection::system().await?;248249            let proxy = zbus_polkit::policykit1::AuthorityProxy::new(&conn).await?;250            conn.object_server()251                .at(OBJ_PATH, Agent::new(conn.clone()).await?)252                .await?;253254            let session_id = std::env::var("XDG_SESSION_ID")?;255            let mut details = HashMap::new();256            let val: OwnedValue = {257                let wrapped: Str<'_> = session_id.into();258                wrapped.into()259            };260            details.insert("session-id".to_string(), val);261            proxy262                .register_authentication_agent(263                    &Subject {264                        subject_kind: "unix-session".to_string(),265                        subject_details: details,266                    },267                    "C",268                    OBJ_PATH,269                )270                .await?;271        }272        Opts::AskPass { description } => {273            let password = RofiPrompter274                .prompt_text(false, &description, "SSH password request", &[])275                .await?;276            stdout().lock().write_all(password.as_bytes())?;277        }278    }279280    future::pending().await281}
modifiednix/nixos-modules.nixdiffbeforeafterboth
--- a/nix/nixos-modules.nix
+++ b/nix/nixos-modules.nix
@@ -10,6 +10,9 @@
       ];
       systemd.services.remowt-polkit-helper = {
         aliases = ["dbus-lach.polkit.helper1.service"];
+        # Restarting can kill in-progress auth requests.
+        # It is good to have it restarted for security, but I didn't decided on the flow yet, graceful shutdown?..
+        unitConfig.X-RestartIfChanged = false;
       };
     };
   };