fix(vpn): retry username/pw entry dialog if connection fails

This commit is contained in:
Ashley Wulber 2025-06-17 10:56:29 -04:00 committed by Ashley Wulber
parent df71f293b8
commit 44a1a9ab90

View file

@ -40,6 +40,8 @@ pub enum Message {
Deactivate(ConnectionId), Deactivate(ConnectionId),
/// An error occurred. /// An error occurred.
Error(ErrorKind, String), Error(ErrorKind, String),
/// VPN connection error.
VpnDialogError(VpnDialog),
/// Update the list of known connections. /// Update the list of known connections.
KnownConnections(IndexMap<UUID, ConnectionSettings>), KnownConnections(IndexMap<UUID, ConnectionSettings>),
/// An update from the network manager daemon /// An update from the network manager daemon
@ -169,6 +171,7 @@ enum VpnDialog {
username: String, username: String,
password: SecureString, password: SecureString,
password_hidden: bool, password_hidden: bool,
error: Option<(ErrorKind, String)>,
}, },
RemoveProfile(ConnectionId), RemoveProfile(ConnectionId),
WireGuardName(String, String, String), WireGuardName(String, String, String),
@ -238,6 +241,7 @@ impl page::Page<crate::pages::Message> for Page {
username, username,
password, password,
password_hidden, password_hidden,
error,
.. ..
} => { } => {
let username = widget::text_input(fl!("username"), username.as_str()) let username = widget::text_input(fl!("username"), username.as_str())
@ -251,11 +255,20 @@ impl page::Page<crate::pages::Message> for Page {
) )
.on_input(|input| Message::PasswordUpdate(SecureString::from(input))) .on_input(|input| Message::PasswordUpdate(SecureString::from(input)))
.on_submit(|_| Message::ConnectWithPassword); .on_submit(|_| Message::ConnectWithPassword);
let (err_kind, error) = if let Some(err) = error.as_ref() {
(
Some(err.0),
Some(widget::text::body(err.1.as_str()).wrapping(Wrapping::Word)),
)
} else {
(None, None)
};
let controls = widget::column::with_capacity(2) let controls = widget::column::with_capacity(2)
.spacing(12) .spacing(12)
.push(username) .push(username)
.push(password) .push(password)
.push_maybe(error)
.apply(Element::from); .apply(Element::from);
let primary_action = widget::button::suggested(fl!("connect")) let primary_action = widget::button::suggested(fl!("connect"))
@ -265,7 +278,11 @@ impl page::Page<crate::pages::Message> for Page {
widget::button::standard(fl!("cancel")).on_press(Message::CancelDialog); widget::button::standard(fl!("cancel")).on_press(Message::CancelDialog);
widget::dialog() widget::dialog()
.title(fl!("auth-dialog")) .title(if let Some(error_kind) = err_kind {
error_kind.localized()
} else {
fl!("auth-dialog")
})
.icon(icon::from_name("network-vpn-symbolic").size(64)) .icon(icon::from_name("network-vpn-symbolic").size(64))
.body(fl!("auth-dialog", "vpn-description")) .body(fl!("auth-dialog", "vpn-description"))
.control(controls) .control(controls)
@ -488,17 +505,26 @@ impl Page {
username: settings.username.clone().unwrap_or_default(), username: settings.username.clone().unwrap_or_default(),
password: SecureString::from(""), password: SecureString::from(""),
password_hidden: true, password_hidden: true,
error: None,
}); });
} }
_ => { _ => {
let connection_name = settings.id.clone(); let connection_name = settings.id.clone();
let username = settings.username.clone();
return cosmic::task::future(async move { return cosmic::task::future(async move {
if let Err(why) = nmcli::connect(&connection_name).await { if let Err(why) = nmcli::connect(&connection_name).await {
return Message::Error( return Message::VpnDialogError(VpnDialog::Password {
ErrorKind::Connect, error: Some((
format!("failed to connect to VPN: {why}"), ErrorKind::Connect,
); format!("failed to connect to VPN: {why}"),
)),
id: connection_name.clone(),
uuid,
username: username.clone().unwrap_or_default(),
password: SecureString::from(""),
password_hidden: true,
});
} }
Message::Refresh Message::Refresh
@ -534,7 +560,6 @@ impl Page {
self.close_popup_and_apply_updates(); self.close_popup_and_apply_updates();
} }
} }
Message::Settings(uuid) => { Message::Settings(uuid) => {
self.close_popup_and_apply_updates(); self.close_popup_and_apply_updates();
@ -578,13 +603,14 @@ impl Page {
if let VpnDialog::Password { if let VpnDialog::Password {
id, id,
uuid,
username, username,
password, password,
.. ..
} = dialog } = dialog
{ {
return self return self
.activate_with_password(id, username, password) .activate_with_password(id, uuid, username, password)
.map(crate::app::Message::from); .map(crate::app::Message::from);
} }
} }
@ -597,11 +623,9 @@ impl Page {
*username = user; *username = user;
} }
} }
Message::CancelDialog => { Message::CancelDialog => {
self.dialog = None; self.dialog = None;
} }
Message::TogglePasswordVisibility => { Message::TogglePasswordVisibility => {
if let Some(VpnDialog::Password { if let Some(VpnDialog::Password {
ref mut password_hidden, ref mut password_hidden,
@ -620,6 +644,10 @@ impl Page {
Message::NetworkManagerConnect(conn) => { Message::NetworkManagerConnect(conn) => {
return self.connect(conn.clone()); return self.connect(conn.clone());
} }
Message::VpnDialogError(vpn_dialog) => {
self.dialog = Some(vpn_dialog);
}
} }
Task::none() Task::none()
@ -628,24 +656,64 @@ impl Page {
fn activate_with_password( fn activate_with_password(
&mut self, &mut self,
connection_name: String, connection_name: String,
uuid: Arc<str>,
username: String, username: String,
password: SecureString, password: SecureString,
) -> Task<Message> { ) -> Task<Message> {
cosmic::task::future(async move { cosmic::task::future(async move {
if let Err(why) = nmcli::set_username(&connection_name, &username).await { if let Err(why) = nmcli::set_username(&connection_name, &username).await {
return Message::Error(ErrorKind::WithPassword("username"), why.to_string()); return Message::VpnDialogError(VpnDialog::Password {
error: Some((ErrorKind::WithPassword("username"), why.to_string())),
id: connection_name.clone(),
uuid,
username,
password,
password_hidden: true,
});
} }
if let Err(why) = nmcli::set_password_flags_none(&connection_name).await { if let Err(why) = nmcli::set_password_flags_none(&connection_name).await {
return Message::Error(ErrorKind::WithPassword("password-flags"), why.to_string()); return Message::VpnDialogError(VpnDialog::Password {
error: Some((ErrorKind::WithPassword("password-flags"), why.to_string())),
id: connection_name.clone(),
uuid,
username,
password,
password_hidden: true,
});
}
if let Err(why) = nmcli::set_password_flags_none(&connection_name).await {
return Message::VpnDialogError(VpnDialog::Password {
error: Some((ErrorKind::WithPassword("password-flags"), why.to_string())),
id: connection_name.clone(),
uuid,
username,
password,
password_hidden: true,
});
} }
if let Err(why) = nmcli::set_password(&connection_name, password.unsecure()).await { if let Err(why) = nmcli::set_password(&connection_name, password.unsecure()).await {
return Message::Error(ErrorKind::WithPassword("password"), why.to_string()); return Message::VpnDialogError(VpnDialog::Password {
error: Some((ErrorKind::WithPassword("password"), why.to_string())),
id: connection_name.clone(),
uuid,
username,
password,
password_hidden: true,
});
} }
if let Err(why) = nmcli::connect(&connection_name).await { if let Err(why) = nmcli::connect(&connection_name).await {
return Message::Error(ErrorKind::Connect, why.to_string()); return Message::VpnDialogError(VpnDialog::Password {
error: Some((ErrorKind::Connect, why.to_string())),
id: connection_name.clone(),
uuid,
username,
password,
password_hidden: true,
});
} }
Message::Refresh Message::Refresh