feat: improved password workflow
This commit is contained in:
parent
2397a003d7
commit
a045947f12
2 changed files with 162 additions and 49 deletions
|
|
@ -32,8 +32,9 @@ pub struct User {
|
|||
id: u64,
|
||||
profile_icon: Option<icon::Handle>,
|
||||
full_name: String,
|
||||
password: String,
|
||||
username: String,
|
||||
password: String,
|
||||
password_confirm: String,
|
||||
full_name_edit: bool,
|
||||
password_edit: bool,
|
||||
username_edit: bool,
|
||||
|
|
@ -43,13 +44,13 @@ pub struct User {
|
|||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum EditorField {
|
||||
FullName,
|
||||
Password,
|
||||
Username,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Dialog {
|
||||
AddNewUser(User),
|
||||
UpdatePassword(User),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -62,8 +63,11 @@ pub struct Page {
|
|||
dialog: Option<Dialog>,
|
||||
default_icon: icon::Handle,
|
||||
password_label: String,
|
||||
password_confirm_label: String,
|
||||
username_label: String,
|
||||
fullname_label: String,
|
||||
password_hidden: bool,
|
||||
password_confirm_hidden: bool,
|
||||
}
|
||||
|
||||
impl Default for Page {
|
||||
|
|
@ -77,8 +81,11 @@ impl Default for Page {
|
|||
dialog: None,
|
||||
default_icon: icon::from_path(PathBuf::from(DEFAULT_ICON_FILE)),
|
||||
password_label: crate::fl!("password"),
|
||||
password_confirm_label: crate::fl!("password-confirm"),
|
||||
username_label: crate::fl!("username"),
|
||||
fullname_label: crate::fl!("full-name"),
|
||||
password_hidden: true,
|
||||
password_confirm_hidden: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -99,6 +106,9 @@ pub enum Message {
|
|||
SelectedUserDelete(u64),
|
||||
SelectedUserSetAdmin(u64, bool),
|
||||
ToggleEdit(usize, EditorField),
|
||||
TogglePasswordVisibility,
|
||||
TogglePasswordConfirmVisibility,
|
||||
SaveNewPassword(User),
|
||||
}
|
||||
|
||||
impl From<Message> for crate::app::Message {
|
||||
|
|
@ -159,15 +169,35 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
);
|
||||
|
||||
let password_input = widget::container(
|
||||
widget::text_input("", &user.password)
|
||||
.password()
|
||||
.label(&self.password_label)
|
||||
.on_input(|value| {
|
||||
Message::Dialog(Some(Dialog::AddNewUser(User {
|
||||
password: value,
|
||||
..user.clone()
|
||||
})))
|
||||
}),
|
||||
widget::secure_input(
|
||||
"",
|
||||
&user.password,
|
||||
Some(Message::TogglePasswordVisibility),
|
||||
self.password_hidden,
|
||||
)
|
||||
.label(&self.password_label)
|
||||
.on_input(|value| {
|
||||
Message::Dialog(Some(Dialog::AddNewUser(User {
|
||||
password: value,
|
||||
..user.clone()
|
||||
})))
|
||||
}),
|
||||
);
|
||||
|
||||
let password_confirm_input = widget::container(
|
||||
widget::secure_input(
|
||||
"",
|
||||
&user.password_confirm,
|
||||
Some(Message::TogglePasswordConfirmVisibility),
|
||||
self.password_confirm_hidden,
|
||||
)
|
||||
.label(&self.password_confirm_label)
|
||||
.on_input(|value| {
|
||||
Message::Dialog(Some(Dialog::AddNewUser(User {
|
||||
password_confirm: value,
|
||||
..user.clone()
|
||||
})))
|
||||
}),
|
||||
);
|
||||
|
||||
let admin_toggler = widget::toggler(user.is_admin).on_toggle(|value| {
|
||||
|
|
@ -184,9 +214,16 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
let complete_maybe = if !username_valid && !user.username.is_empty() {
|
||||
validation_msg = fl!("invalid-username");
|
||||
None
|
||||
} else if user.password != user.password_confirm
|
||||
&& user.password != ""
|
||||
&& user.password_confirm != ""
|
||||
{
|
||||
validation_msg = fl!("password-mismatch");
|
||||
None
|
||||
} else if user.full_name.is_empty()
|
||||
|| user.username.is_empty()
|
||||
|| user.password.is_empty()
|
||||
|| user.password_confirm.is_empty()
|
||||
{
|
||||
None
|
||||
} else {
|
||||
|
|
@ -212,6 +249,7 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
.add(full_name_input)
|
||||
.add(username_input)
|
||||
.add(password_input)
|
||||
.add(password_confirm_input)
|
||||
.add(
|
||||
row::with_capacity(3)
|
||||
.push(
|
||||
|
|
@ -232,6 +270,73 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
.tertiary_action(widget::text::body(validation_msg))
|
||||
.apply(Element::from)
|
||||
}
|
||||
|
||||
Dialog::UpdatePassword(user) => {
|
||||
let password_input = widget::container(
|
||||
widget::secure_input(
|
||||
"",
|
||||
&user.password,
|
||||
Some(Message::TogglePasswordVisibility),
|
||||
self.password_hidden,
|
||||
)
|
||||
.label(&self.password_label)
|
||||
.on_input(|value| {
|
||||
Message::Dialog(Some(Dialog::UpdatePassword(User {
|
||||
password: value,
|
||||
..user.clone()
|
||||
})))
|
||||
}),
|
||||
);
|
||||
|
||||
let password_confirm_input = widget::container(
|
||||
widget::secure_input(
|
||||
"",
|
||||
&user.password_confirm,
|
||||
Some(Message::TogglePasswordConfirmVisibility),
|
||||
self.password_confirm_hidden,
|
||||
)
|
||||
.label(&self.password_confirm_label)
|
||||
.on_input(|value| {
|
||||
Message::Dialog(Some(Dialog::UpdatePassword(User {
|
||||
password_confirm: value,
|
||||
..user.clone()
|
||||
})))
|
||||
}),
|
||||
);
|
||||
|
||||
// validation
|
||||
let mut validation_msg = String::new();
|
||||
let complete_maybe = if user.password != user.password_confirm
|
||||
&& user.password != ""
|
||||
&& user.password_confirm != ""
|
||||
{
|
||||
validation_msg = fl!("password-mismatch");
|
||||
None
|
||||
} else if user.password.is_empty() || user.password_confirm.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(Message::SaveNewPassword(user.clone()))
|
||||
};
|
||||
|
||||
let save_button = widget::button::suggested(fl!("save"))
|
||||
.on_press_maybe(complete_maybe)
|
||||
.apply(Element::from);
|
||||
|
||||
let cancel_button =
|
||||
widget::button::standard(fl!("cancel")).on_press(Message::Dialog(None));
|
||||
|
||||
widget::dialog()
|
||||
.title(fl!("change-password"))
|
||||
.control(
|
||||
widget::ListColumn::default()
|
||||
.add(password_input)
|
||||
.add(password_confirm_input),
|
||||
)
|
||||
.primary_action(save_button)
|
||||
.secondary_action(cancel_button)
|
||||
.tertiary_action(widget::text::body(validation_msg))
|
||||
.apply(Element::from)
|
||||
}
|
||||
};
|
||||
|
||||
dialog_element.map(crate::pages::Message::User).into()
|
||||
|
|
@ -293,6 +398,7 @@ impl Page {
|
|||
username: String::from(user.username),
|
||||
full_name: String::from(user.full_name),
|
||||
password: String::new(),
|
||||
password_confirm: String::new(),
|
||||
full_name_edit: false,
|
||||
password_edit: false,
|
||||
username_edit: false,
|
||||
|
|
@ -379,7 +485,6 @@ impl Page {
|
|||
if let Some(user) = self.users.get_mut(id) {
|
||||
match field {
|
||||
EditorField::FullName => user.full_name = value,
|
||||
EditorField::Password => user.password = value,
|
||||
EditorField::Username => user.username = value,
|
||||
}
|
||||
}
|
||||
|
|
@ -389,12 +494,18 @@ impl Page {
|
|||
if let Some(user) = self.users.get_mut(id) {
|
||||
match field {
|
||||
EditorField::FullName => user.full_name_edit = !user.full_name_edit,
|
||||
EditorField::Password => user.password_edit = !user.password_edit,
|
||||
EditorField::Username => user.username_edit = !user.username_edit,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Message::TogglePasswordVisibility => {
|
||||
self.password_hidden = !self.password_hidden;
|
||||
}
|
||||
Message::TogglePasswordConfirmVisibility => {
|
||||
self.password_confirm_hidden = !self.password_confirm_hidden;
|
||||
}
|
||||
|
||||
Message::ApplyEdit(id, field) => {
|
||||
if let Some(user) = self.users.get_mut(id) {
|
||||
let uid = user.id;
|
||||
|
|
@ -420,34 +531,6 @@ impl Page {
|
|||
.discard();
|
||||
}
|
||||
|
||||
EditorField::Password => {
|
||||
let password = std::mem::take(&mut user.password);
|
||||
|
||||
return cosmic::Task::future(async move {
|
||||
let Ok(conn) = zbus::Connection::system().await else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(user) = accounts_zbus::UserProxy::from_uid(&conn, uid).await
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
match request_permission_on_denial(&conn, || {
|
||||
user.set_password(&password, "")
|
||||
})
|
||||
.await
|
||||
{
|
||||
Err(why) => {
|
||||
tracing::error!(?why, "failed to set password");
|
||||
}
|
||||
|
||||
Ok(_) => (),
|
||||
}
|
||||
})
|
||||
.discard();
|
||||
}
|
||||
|
||||
EditorField::Username => {
|
||||
let username = user.username.clone();
|
||||
return cosmic::Task::future(async move {
|
||||
|
|
@ -471,6 +554,34 @@ impl Page {
|
|||
}
|
||||
}
|
||||
|
||||
Message::SaveNewPassword(user) => {
|
||||
self.dialog = None;
|
||||
|
||||
let uid = user.id;
|
||||
let password = user.password;
|
||||
|
||||
return cosmic::Task::future(async move {
|
||||
let Ok(conn) = zbus::Connection::system().await else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(user) = accounts_zbus::UserProxy::from_uid(&conn, uid).await else {
|
||||
return;
|
||||
};
|
||||
|
||||
match request_permission_on_denial(&conn, || user.set_password(&password, ""))
|
||||
.await
|
||||
{
|
||||
Err(why) => {
|
||||
tracing::error!(?why, "failed to set password");
|
||||
}
|
||||
|
||||
Ok(_) => (),
|
||||
}
|
||||
})
|
||||
.discard();
|
||||
}
|
||||
|
||||
Message::LoadPage(uid, users) => {
|
||||
self.current_user_id = uid;
|
||||
self.users = users;
|
||||
|
|
@ -514,6 +625,8 @@ impl Page {
|
|||
}
|
||||
|
||||
Message::Dialog(dialog) => {
|
||||
self.password_hidden = true;
|
||||
self.password_confirm_hidden = true;
|
||||
self.dialog = dialog;
|
||||
}
|
||||
|
||||
|
|
@ -617,13 +730,9 @@ fn user_list() -> Section<crate::pages::Message> {
|
|||
.on_input(move |name| Message::Edit(idx, EditorField::Username, name))
|
||||
.on_submit(move |_| Message::ApplyEdit(idx, EditorField::Username));
|
||||
|
||||
let password =
|
||||
widget::editable_input("", &user.password, user.password_edit, move |_| {
|
||||
Message::ToggleEdit(idx, EditorField::Password)
|
||||
})
|
||||
.on_input(move |pass| Message::Edit(idx, EditorField::Password, pass))
|
||||
.on_submit(move |_| Message::ApplyEdit(idx, EditorField::Password))
|
||||
.password();
|
||||
let password = widget::button::standard(fl!("change-password"))
|
||||
.on_press(Message::Dialog(Some(Dialog::UpdatePassword(user.clone()))))
|
||||
.apply(Element::from);
|
||||
|
||||
let fullname = widget::editable_input(
|
||||
"",
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ network-and-wireless = Network & Wireless
|
|||
no-networks = No networks have been found.
|
||||
no-vpn = No VPN connections available.
|
||||
password = Password
|
||||
password-confirm = Confirm Password
|
||||
remove = Remove
|
||||
settings = Settings
|
||||
username = Username
|
||||
|
|
@ -888,6 +889,9 @@ administrator = Administrator
|
|||
.desc = Administrators can change settings for all users, add and remove other users.
|
||||
|
||||
add-user = Add user
|
||||
change-password = Change password
|
||||
remove-user = Remove user
|
||||
full-name = Full name
|
||||
invalid-username = Invalid username
|
||||
invalid-username = Invalid username.
|
||||
password-mismatch = Password and confirmation must match.
|
||||
save = Save
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue