difftreelog
feat properly cancel agent task
in: trunk
3 files changed
Cargo.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"
cmds/remowt-agent/src/main.rsdiffbeforeafterboth1use 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}nix/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;
};
};
};