2025-12-19 11:44:19 -05:00
use std ::{
collections ::{ HashMap , HashSet } ,
fmt ::Debug ,
sync ::Arc ,
} ;
use bitflags ::bitflags ;
use cosmic_dbus_networkmanager ::interface ::settings ::connection ::ConnectionSettingsProxy ;
use futures ::{ SinkExt , Stream } ;
use secure_string ::SecureString ;
use tokio ::sync ::oneshot ;
use zbus ::{
ObjectServer , fdo ,
zvariant ::{ OwnedValue , Str } ,
} ;
pub type SecretSender = Arc < tokio ::sync ::Mutex < Option < tokio ::sync ::oneshot ::Sender < SecureString > > > > ;
2026-02-19 18:33:47 +01:00
pub const SECRET_ID : & str = " com.system76.CosmicSettings.NetworkManager " ;
2025-12-19 11:44:19 -05:00
pub const DBUS_PATH : & str = " /org/freedesktop/NetworkManager/SecretAgent " ;
bitflags! {
#[ derive(Debug, Clone, Copy, PartialEq, Eq) ]
pub struct GetSecretsFlags : u32 {
/// No special behavior.
/// By default no user interaction is allowed and secrets must come
/// from persistent storage, otherwise an error is returned.
const NONE = 0x0 ;
/// Allows interaction with the user (eg. prompt via UI).
const ALLOW_INTERACTION = 0x1 ;
/// Explicitly request new secrets from the user.
/// Implies ALLOW_INTERACTION.
const REQUEST_NEW = 0x2 ;
/// Request was initiated by a user action (via D-Bus).
const USER_REQUESTED = 0x4 ;
/// Internal flag, not part of the public D-Bus API.
const ONLY_SYSTEM = 0x8000_0000 ;
/// Internal flag, not part of the public D-Bus API.
const NO_ERRORS = 0x4000_0000 ;
}
}
#[ derive(thiserror::Error, Clone, Debug) ]
pub enum Error {
#[ error( " zbus error " ) ]
Zbus ( #[ from ] zbus ::Error ) ,
#[ error( " listening for secret agent closed " ) ]
RecvError ( #[ from ] oneshot ::error ::RecvError ) ,
#[ error( " secret service error " ) ]
SecretService ( #[ from ] Arc < secret_service ::Error > ) ,
#[ error( " no password found for identifier: {0} " ) ]
NoPasswordForIdentifier ( String ) ,
#[ error( " utf8 error " ) ]
Utf8Error ( #[ from ] std ::string ::FromUtf8Error ) ,
}
#[ derive(Copy, Clone, Debug, Eq, PartialEq) ]
pub enum PasswordFlag {
/// The system is responsible for providing and storing this secret.
None = 0 ,
/// A user-session secret agent is responsible for providing and storing
/// this secret; when it is required, agents will be asked to provide it.
AgentOwned = 1 ,
/// This secret should not be saved but should be requested from the user
/// each time it is required. This flag should be used for One-Time-Pad
/// secrets, PIN codes from hardware tokens, or if the user simply does not
/// want to save the secret.
NotSaved = 2 ,
/// in some situations it cannot be automatically determined that a secret is required or not. This flag hints that the secret is not required and should not be requested from the user.
NotRequired = 4 ,
}
#[ derive(Debug, Clone) ]
pub struct SecretHint {
pub key : String ,
pub message : Option < String > ,
}
fn parse_hints ( hints : Vec < String > ) -> Vec < SecretHint > {
hints
. into_iter ( )
// fold message hints into previous hints
. fold ( Vec ::new ( ) , | mut acc , hint | {
if let Some ( ( key , msg ) ) = hint . split_once ( ':' ) {
if let Some ( last ) = acc . last_mut ( ) {
last . message = Some ( format! ( " {} : {} " , key , msg ) ) ;
}
} else {
acc . push ( SecretHint {
key : hint ,
message : None ,
} ) ;
}
acc
} )
}
#[ derive(Debug, Clone) ]
pub enum Event {
RequestSecret {
uuid : String ,
name : String ,
description : Option < String > ,
previous : SecureString ,
tx : SecretSender ,
} ,
CancelGetSecrets {
uuid : String ,
name : String ,
} ,
Failed ( Error ) ,
}
#[ derive(Debug) ]
pub enum Request {
SetSecrets {
setting_name : String ,
uuid : String ,
secrets : HashMap < String , SecureString > ,
applied_tx : oneshot ::Sender < ( ) > ,
} ,
GetSecrets {
setting_name : String ,
uuid : String ,
resp_tx : oneshot ::Sender < HashMap < String , SecureString > > ,
} ,
}
pub fn secret_agent_stream (
identifier : impl AsRef < str > ,
rx : tokio ::sync ::mpsc ::Receiver < Request > ,
) -> impl Stream < Item = Event > {
2026-03-17 16:56:55 -04:00
iced_futures ::stream ::channel (
4 ,
move | mut msg_tx : futures ::channel ::mpsc ::Sender < Event > | async move {
if let Err ( e ) = secret_agent_stream_impl ( identifier . as_ref ( ) , msg_tx . clone ( ) , rx ) . await
{
let _ = msg_tx . send ( Event ::Failed ( e ) ) . await ;
}
} ,
)
2025-12-19 11:44:19 -05:00
}
async fn secret_agent_stream_impl (
identifier : & str ,
msg_tx : futures ::channel ::mpsc ::Sender < Event > ,
mut rx : tokio ::sync ::mpsc ::Receiver < Request > ,
) -> Result < ( ) , Error > {
2026-01-28 20:35:01 -05:00
// fail early if we can't connect, closing the channel
{
let ss = secret_service ::SecretService ::connect ( secret_service ::EncryptionType ::Dh )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
let collection = ss . get_default_collection ( ) . await . map_err ( Arc ::new ) ? ;
if collection . is_locked ( ) . await . map_err ( Arc ::new ) ? {
collection . unlock ( ) . await . map_err ( Arc ::new ) ? ;
2026-01-28 20:35:01 -05:00
}
}
2025-12-19 11:44:19 -05:00
// register the secret agent with NetworkManager
let proxy =
nm_secret_agent_manager ::AgentManagerProxy ::builder ( & zbus ::Connection ::system ( ) . await ? )
. path ( " /org/freedesktop/NetworkManager/AgentManager " ) ?
. build ( )
. await ? ;
let _ = ObjectServer ::at (
proxy . inner ( ) . connection ( ) . object_server ( ) ,
DBUS_PATH ,
SettingsSecretAgent { tx : msg_tx } ,
)
. await ? ;
proxy . register_with_capabilities ( identifier , 1 ) . await ? ;
while let Some ( request ) = rx . recv ( ) . await {
match request {
Request ::SetSecrets {
setting_name ,
uuid ,
secrets ,
applied_tx ,
} = > {
let ss = secret_service ::SecretService ::connect ( secret_service ::EncryptionType ::Dh )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
let collection = ss . get_default_collection ( ) . await . map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
if secrets . is_empty ( ) {
let mut attributes = std ::collections ::HashMap ::new ( ) ;
attributes . insert ( " application " , SECRET_ID ) ;
attributes . insert ( " uuid " , & uuid ) ;
let search_items = collection
. search_items ( attributes )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
for item in & search_items {
2026-02-19 18:33:47 +01:00
item . delete ( ) . await . map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
}
let _ = applied_tx . send ( ( ) ) ;
continue ;
}
for ( name , secret ) in & secrets {
let mut attributes = std ::collections ::HashMap ::new ( ) ;
attributes . insert ( " application " , SECRET_ID ) ;
attributes . insert ( " uuid " , & uuid ) ;
attributes . insert ( " setting_name " , & setting_name ) ;
attributes . insert ( " name " , name ) ;
let _item = collection
. create_item (
" NetworkManager Secret " ,
attributes ,
secret . unsecure ( ) . as_bytes ( ) ,
true ,
" text/plain " ,
)
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
}
let _ = applied_tx . send ( ( ) ) ;
}
Request ::GetSecrets {
setting_name ,
uuid ,
resp_tx ,
} = > {
let ss = secret_service ::SecretService ::connect ( secret_service ::EncryptionType ::Dh )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
let collection = ss . get_default_collection ( ) . await . map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
let mut attributes = std ::collections ::HashMap ::new ( ) ;
attributes . insert ( " application " , SECRET_ID ) ;
attributes . insert ( " uuid " , & uuid ) ;
attributes . insert ( " setting_name " , & setting_name ) ;
let search_items = collection
. search_items ( attributes )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
let mut secrets = HashMap ::new ( ) ;
for item in & search_items {
let name = item
. get_attributes ( )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ?
2025-12-19 11:44:19 -05:00
. get ( " name " )
. cloned ( )
. unwrap_or_else ( | | " unknown " . to_string ( ) ) ;
2026-02-19 18:33:47 +01:00
let secret = item . get_secret ( ) . await . map_err ( Arc ::new ) ? ;
let secret : String = String ::from_utf8 ( secret ) ? ;
2025-12-19 11:44:19 -05:00
secrets . insert ( name , SecureString ::from ( secret ) ) ;
}
let _ = resp_tx . send ( secrets ) ;
}
}
}
Ok ( ( ) )
}
fn parse_secret_flag ( value : & str ) -> PasswordFlag {
match value {
" 0 " = > PasswordFlag ::None ,
" 1 " = > PasswordFlag ::AgentOwned ,
" 2 " = > PasswordFlag ::NotSaved ,
" 4 " = > PasswordFlag ::NotRequired ,
_ = > PasswordFlag ::AgentOwned ,
}
}
fn setting_has_always_ask ( setting : zbus ::zvariant ::Dict ) -> bool {
for ( key , value ) in setting . iter ( ) {
let Ok ( key ) = key . downcast_ref ::< zbus ::zvariant ::Str > ( ) else {
continue ;
} ;
let Ok ( value ) = value . downcast_ref ::< zbus ::zvariant ::Str > ( ) else {
continue ;
} ;
// we only care about "<secret>-flags"
if ! key . ends_with ( " -flags " ) {
continue ;
}
if parse_secret_flag ( value . as_str ( ) ) = = PasswordFlag ::NotSaved {
return true ;
}
}
false
}
fn has_always_ask ( setting : Option < zbus ::zvariant ::Dict > ) -> bool {
setting . map ( setting_has_always_ask ) . unwrap_or ( false )
}
fn is_connection_always_ask ( connection : & HashMap < String , HashMap < String , OwnedValue > > ) -> bool {
let conn_setting = match connection . get ( " connection " ) {
Some ( s ) = > s ,
None = > return false ,
} ;
let conn_type = match conn_setting
. get ( " type " )
. and_then ( | v | v . downcast_ref ::< String > ( ) . ok ( ) )
{
Some ( t ) = > t ,
None = > return false ,
} ;
// Primary setting (vpn, wifi, ethernet, etc)
if has_always_ask (
connection
. get ( & conn_type )
. and_then ( | d | d . get ( " data " ) )
. and_then ( | data | data . downcast_ref ::< zbus ::zvariant ::Dict > ( ) . ok ( ) ) ,
) {
return true ;
}
match conn_type . as_str ( ) {
" 802-11-wireless " = > {
if has_always_ask (
connection
. get ( " 802-11-wireless-security " )
. and_then ( | d | d . get ( " data " ) )
. and_then ( | data | data . downcast_ref ::< zbus ::zvariant ::Dict > ( ) . ok ( ) ) ,
) {
return true ;
}
if has_always_ask (
connection
. get ( " 802-1x " )
. and_then ( | d | d . get ( " data " ) )
. and_then ( | data | data . downcast_ref ::< zbus ::zvariant ::Dict > ( ) . ok ( ) ) ,
) {
return true ;
}
}
" 802-3-ethernet " = > {
if has_always_ask (
connection
. get ( " pppoe " )
. and_then ( | d | d . get ( " data " ) )
. and_then ( | data | data . downcast_ref ::< zbus ::zvariant ::Dict > ( ) . ok ( ) ) ,
) {
return true ;
}
if has_always_ask (
connection
. get ( " 802-1x " )
. and_then ( | d | d . get ( " data " ) )
. and_then ( | data | data . downcast_ref ::< zbus ::zvariant ::Dict > ( ) . ok ( ) ) ,
) {
return true ;
}
}
_ = > { }
}
false
}
#[ derive(Debug) ]
pub struct SettingsSecretAgent {
tx : futures ::channel ::mpsc ::Sender < Event > ,
}
#[ zbus::interface(name = " org.freedesktop.NetworkManager.SecretAgent " ) ]
impl SettingsSecretAgent {
/// CancelGetSecrets method
async fn cancel_get_secrets (
& mut self ,
connection_path : zbus ::zvariant ::ObjectPath < '_ > ,
setting_name : String ,
) -> fdo ::Result < ( ) > {
let conn = ConnectionSettingsProxy ::builder (
& zbus ::Connection ::system ( )
. await
2026-02-19 18:33:47 +01:00
. map_err ( | _ | fdo ::Error ::Failed ( " failed to get uuid " . to_string ( ) ) ) ? ,
2025-12-19 11:44:19 -05:00
)
. path ( connection_path ) ?
. build ( )
. await
. map_err ( | e | fdo ::Error ::Failed ( e . to_string ( ) ) ) ? ;
let uuid = conn
. get_settings ( )
. await
. map_err ( | e | fdo ::Error ::Failed ( e . to_string ( ) ) ) ?
. get ( " connection " )
. and_then ( | m | m . get ( " uuid " ) )
. and_then ( | v | v . downcast_ref ::< String > ( ) . ok ( ) )
. ok_or_else ( | | fdo ::Error ::Failed ( " failed to get uuid " . to_string ( ) ) ) ?
. to_string ( ) ;
if let Err ( e ) = self
. tx
. clone ( )
. send ( Event ::CancelGetSecrets {
uuid ,
name : setting_name ,
} )
. await
& & e . is_disconnected ( )
{
return Err ( fdo ::Error ::Failed (
" failed to send cancel message " . to_string ( ) ,
) ) ;
}
Ok ( ( ) )
}
/// DeleteSecrets method
async fn delete_secrets (
& self ,
connection : HashMap < String , HashMap < String , zbus ::zvariant ::OwnedValue > > ,
connection_path : zbus ::zvariant ::ObjectPath < '_ > ,
) -> fdo ::Result < ( ) > {
match self . delete_secrets_inner ( connection , connection_path ) . await {
Ok ( _ ) = > Ok ( ( ) ) ,
Err ( err ) = > Err ( fdo ::Error ::Failed ( err . to_string ( ) ) ) ,
}
}
/// GetSecrets method
async fn get_secrets (
& mut self ,
connection : HashMap < String , HashMap < String , zbus ::zvariant ::OwnedValue > > ,
connection_path : zbus ::zvariant ::ObjectPath < '_ > ,
setting_name : String ,
hints : Vec < String > ,
flags : u32 ,
) -> HashMap < String , HashMap < String , zbus ::zvariant ::OwnedValue > > {
2026-02-19 18:33:47 +01:00
self . get_secrets_inner ( connection , connection_path , setting_name , hints , flags )
2025-12-19 11:44:19 -05:00
. await
2026-02-19 18:33:47 +01:00
. unwrap_or_default ( )
2025-12-19 11:44:19 -05:00
}
/// SaveSecrets method
async fn save_secrets (
& self ,
connection : HashMap < String , HashMap < String , zbus ::zvariant ::OwnedValue > > ,
_connection_path : zbus ::zvariant ::ObjectPath < '_ > ,
) -> fdo ::Result < ( ) > {
match self . save_secrets_inner ( connection ) . await {
Ok ( _ ) = > Ok ( ( ) ) ,
Err ( err ) = > Err ( fdo ::Error ::Failed ( err . to_string ( ) ) ) ,
}
}
}
impl SettingsSecretAgent {
pub async fn get_secrets_inner (
& mut self ,
connection : HashMap < String , HashMap < String , zbus ::zvariant ::OwnedValue > > ,
connection_path : zbus ::zvariant ::ObjectPath < '_ > ,
setting_name : String ,
hints : Vec < String > ,
flags : u32 ,
) -> Result < HashMap < String , HashMap < String , zbus ::zvariant ::OwnedValue > > , Error > {
let flags = GetSecretsFlags ::from_bits_truncate ( flags ) ;
let ss = secret_service ::SecretService ::connect ( secret_service ::EncryptionType ::Dh )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
2026-02-19 18:33:47 +01:00
let collection = ss . get_default_collection ( ) . await . map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
let conn_uuid = connection
. get ( " connection " )
. and_then ( | m | m . get ( " uuid " ) )
. and_then ( | v | v . downcast_ref ::< String > ( ) . ok ( ) )
. ok_or_else ( | | Error ::NoPasswordForIdentifier ( setting_name . clone ( ) ) ) ?
. to_string ( ) ;
let conn =
2026-02-19 18:33:47 +01:00
ConnectionSettingsProxy ::builder ( & zbus ::Connection ::system ( ) . await . map_err ( | _ | {
Error ::Zbus ( fdo ::Error ::Failed ( " failed to get uuid " . to_string ( ) ) . into ( ) )
2025-12-19 11:44:19 -05:00
} ) ? )
. path ( connection_path ) ?
. build ( )
. await
. map_err ( | e | Error ::Zbus ( fdo ::Error ::Failed ( e . to_string ( ) ) . into ( ) ) ) ? ;
let settings = conn . get_settings ( ) . await ? ;
let is_vpn = settings
. get ( " connection " )
. and_then ( | m | m . get ( " type " ) )
. and_then ( | v | v . downcast_ref ::< String > ( ) . ok ( ) )
2026-02-19 18:33:47 +01:00
. is_some_and ( | t | t = = " vpn " ) ;
2025-12-19 11:44:19 -05:00
let is_always_ask = is_connection_always_ask ( & settings ) ;
let mut setting_attributes = std ::collections ::HashMap ::new ( ) ;
setting_attributes . insert ( " application " , SECRET_ID ) ;
setting_attributes . insert ( " uuid " , & conn_uuid ) ;
setting_attributes . insert ( " setting_name " , & setting_name ) ;
2026-01-09 21:32:07 +01:00
let search_items = collection
2025-12-19 11:44:19 -05:00
. search_items ( setting_attributes . clone ( ) )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
let mut result = HashMap ::new ( ) ;
let mut setting = HashMap ::new ( ) ;
if hints . is_empty ( ) {
for item in & search_items {
let name = item
. get_attributes ( )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ?
2025-12-19 11:44:19 -05:00
. get ( " name " )
. cloned ( )
. unwrap_or_else ( | | " unknown " . to_string ( ) ) ;
2026-02-19 18:33:47 +01:00
let secret = item . get_secret ( ) . await . map_err ( Arc ::new ) ? ;
let secret : String = String ::from_utf8 ( secret ) ? ;
2025-12-19 11:44:19 -05:00
setting . insert ( name , zbus ::zvariant ::OwnedValue ::from ( Str ::from ( secret ) ) ) ;
}
result . insert ( setting_name , setting ) ;
2026-02-19 18:33:47 +01:00
Ok ( result )
2025-12-19 11:44:19 -05:00
} else {
let hints = parse_hints ( hints ) ;
let mut requested = HashSet ::new ( ) ;
for SecretHint { key , message } in & hints {
if requested . contains ( key ) {
continue ;
}
requested . insert ( key ) ;
if flags . contains ( GetSecretsFlags ::REQUEST_NEW )
& & flags . contains ( GetSecretsFlags ::ALLOW_INTERACTION )
| | is_always_ask
{
// request the secret via the message channel
let ( resp_tx , resp_rx ) = oneshot ::channel ( ) ;
// msg begins after ":"
let actual_hint = message . as_ref ( ) . map ( | m | {
m . split_once ( " : " )
. map ( | ( _ , msg ) | msg . trim ( ) . to_string ( ) )
. unwrap_or ( m . clone ( ) )
} ) ;
if let Err ( e ) = self
. tx
. clone ( )
. send ( Event ::RequestSecret {
uuid : conn_uuid . clone ( ) ,
name : setting_name . clone ( ) ,
description : actual_hint . clone ( ) ,
previous : String ::new ( ) . into ( ) ,
tx : Arc ::new ( tokio ::sync ::Mutex ::new ( Some ( resp_tx ) ) ) ,
} )
. await
& & e . is_disconnected ( )
{
continue ;
2026-02-19 18:33:47 +01:00
} else if let Ok ( secret ) = resp_rx . await {
let mut named_attribute = setting_attributes . clone ( ) ;
named_attribute . insert ( " name " , key ) ;
let _item = collection
. create_item (
" NetworkManager Secret " ,
named_attribute ,
secret . unsecure ( ) . as_bytes ( ) ,
true ,
" text/plain " ,
)
. await
. map_err ( Arc ::new ) ? ;
setting . insert (
key . clone ( ) ,
zbus ::zvariant ::OwnedValue ::from ( Str ::from ( secret . unsecure ( ) ) ) ,
) ;
2025-12-19 11:44:19 -05:00
}
} else if ! is_always_ask {
let mut pos = None ;
let mut pos_with_message = None ;
for item in & search_items {
2026-02-19 18:33:47 +01:00
let attributes = item . get_attributes ( ) . await . map_err ( Arc ::new ) ? ;
if let Some ( value ) = attributes . get ( " name " )
& & value = = key
{
if let Some ( saved_message ) = attributes . get ( " message " ) {
if message . as_ref ( ) . is_some_and ( | msg | msg = = saved_message ) {
pos_with_message = Some ( item ) ;
2025-12-19 11:44:19 -05:00
}
2026-02-19 18:33:47 +01:00
break ;
} else {
pos = Some ( item ) ;
2025-12-19 11:44:19 -05:00
}
}
}
if let Some ( item ) = pos_with_message . or ( pos ) {
2026-02-19 18:33:47 +01:00
let secret = item . get_secret ( ) . await . map_err ( Arc ::new ) ? ;
let secret : String = String ::from_utf8 ( secret ) ? ;
2025-12-19 11:44:19 -05:00
if is_vpn {
// ask anyway, but offer the previous one as a hint
let ( resp_tx , resp_rx ) = oneshot ::channel ( ) ;
let actual_hint = message . as_ref ( ) . map ( | m | {
m . split_once ( " : " )
. map ( | ( _ , msg ) | msg . trim ( ) . to_string ( ) )
. unwrap_or ( m . clone ( ) )
} ) ;
if let Err ( e ) = self
. tx
. clone ( )
. send ( Event ::RequestSecret {
uuid : conn_uuid . clone ( ) ,
name : setting_name . clone ( ) ,
description : actual_hint . clone ( ) ,
previous : SecureString ::from ( secret . clone ( ) ) ,
tx : Arc ::new ( tokio ::sync ::Mutex ::new ( Some ( resp_tx ) ) ) ,
} )
. await
& & e . is_disconnected ( )
{
continue ;
2026-02-19 18:33:47 +01:00
} else if let Ok ( secret ) = resp_rx . await {
let mut named_attribute = setting_attributes . clone ( ) ;
named_attribute . insert ( " name " , key ) ;
let _item = collection
. create_item (
" NetworkManager Secret " ,
named_attribute ,
secret . unsecure ( ) . as_bytes ( ) ,
true ,
" text/plain " ,
)
. await
. map_err ( Arc ::new ) ? ;
setting . insert (
key . clone ( ) ,
zbus ::zvariant ::OwnedValue ::from ( Str ::from ( secret . unsecure ( ) ) ) ,
) ;
2025-12-19 11:44:19 -05:00
}
} else {
setting . insert (
key . clone ( ) ,
zbus ::zvariant ::OwnedValue ::from ( Str ::from ( secret ) ) ,
) ;
}
}
} else {
// can't find the secret, and we can't request it, so we just skip it
continue ;
}
}
result . insert ( setting_name , setting ) ;
2026-02-19 18:33:47 +01:00
Ok ( result )
2025-12-19 11:44:19 -05:00
}
}
pub async fn delete_secrets_inner (
& self ,
connection : HashMap < String , HashMap < String , zbus ::zvariant ::OwnedValue > > ,
_connection_path : zbus ::zvariant ::ObjectPath < '_ > ,
) -> Result < ( ) , Error > {
let ss = secret_service ::SecretService ::connect ( secret_service ::EncryptionType ::Dh )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
let collection = ss . get_default_collection ( ) . await . map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
let conn_uuid = connection
. get ( " connection " )
. and_then ( | m | m . get ( " uuid " ) )
. and_then ( | v | v . downcast_ref ::< String > ( ) . ok ( ) )
. ok_or_else ( | | Error ::NoPasswordForIdentifier ( " unknown " . to_string ( ) ) ) ?
. to_string ( ) ;
let mut attributes = std ::collections ::HashMap ::new ( ) ;
attributes . insert ( " application " , SECRET_ID ) ;
attributes . insert ( " uuid " , & conn_uuid ) ;
let search_items = collection
. search_items ( attributes )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
for item in & search_items {
2026-02-19 18:33:47 +01:00
item . delete ( ) . await . map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
}
Ok ( ( ) )
}
pub async fn save_secrets_inner (
& self ,
connection : HashMap < String , HashMap < String , zbus ::zvariant ::OwnedValue > > ,
) -> Result < ( ) , Error > {
let ss = secret_service ::SecretService ::connect ( secret_service ::EncryptionType ::Dh )
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
let collection = ss . get_default_collection ( ) . await . map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
let conn_uuid = connection
. get ( " connection " )
. and_then ( | m | m . get ( " uuid " ) )
. and_then ( | v | v . downcast_ref ::< String > ( ) . ok ( ) )
. ok_or_else ( | | Error ::NoPasswordForIdentifier ( " unknown " . to_string ( ) ) ) ?
. to_string ( ) ;
let secret : Option < ( String , String ) > = connection
. get ( " 802-11-wireless-security " )
. and_then ( | m | m . get ( " psk " ) )
. and_then ( | v | v . downcast_ref ::< String > ( ) . ok ( ) )
. map ( | password | ( " psk " . to_string ( ) , password . clone ( ) ) )
. or_else ( | | {
connection
. get ( " 802-1x " )
. and_then ( | s | s . get ( " password " ) )
. and_then ( | v | v . downcast_ref ::< String > ( ) . ok ( ) )
. map ( | password | ( " 802-1x-password " . to_string ( ) , password . clone ( ) ) )
} ) ;
if let Some ( ( name , secret ) ) = secret {
let mut attributes = std ::collections ::HashMap ::new ( ) ;
attributes . insert ( " application " , SECRET_ID ) ;
attributes . insert ( " uuid " , & conn_uuid ) ;
attributes . insert ( " setting_name " , & name ) ;
let _item = collection
. create_item (
" NetworkManager Secret " ,
attributes ,
secret . as_bytes ( ) ,
true ,
" text/plain " ,
)
. await
2026-02-19 18:33:47 +01:00
. map_err ( Arc ::new ) ? ;
2025-12-19 11:44:19 -05:00
Ok ( ( ) )
} else {
Err ( Error ::NoPasswordForIdentifier ( " unknown " . to_string ( ) ) )
}
}
}