Skip to content

Commit

Permalink
Add change password functionality
Browse files Browse the repository at this point in the history
Co-authored-by: Stanislav Malishevskiy <[email protected]>
  • Loading branch information
fiaxh and smalishevskiy committed Sep 21, 2024
1 parent b09556f commit f8c0046
Show file tree
Hide file tree
Showing 12 changed files with 235 additions and 8 deletions.
6 changes: 6 additions & 0 deletions libdino/src/service/registration.vala
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ public class Register : StreamInteractionModule, Object{
return ret;
}

public async string? change_password(Account account, string new_pw){
XmppStream stream = stream_interactor.get_stream(account);
if (stream == null) return null;
return (yield stream.get_module(Xep.InBandRegistration.Module.IDENTITY).change_password(stream, account.full_jid, new_pw)).condition;
}

public class ServerAvailabilityReturn {
public bool available { get; set; }
public TlsCertificateFlags? error_flags { get; set; }
Expand Down
2 changes: 2 additions & 0 deletions main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ set(RESOURCE_LIST

preferences_window/account_preferences_subpage.ui
preferences_window/add_account_dialog.ui
preferences_window/change_password_dialog.ui
preferences_window/general_preferences_page.ui
preferences_window/preferences_window.ui

Expand Down Expand Up @@ -241,6 +242,7 @@ SOURCES
src/windows/preferences_window/account_preferences_subpage.vala
src/windows/preferences_window/accounts_preferences_page.vala
src/windows/preferences_window/add_account_dialog.vala
src/windows/preferences_window/change_password_dialog.vala
src/windows/preferences_window/encryption_preferences_page.vala
src/windows/preferences_window/general_preferences_page.vala
src/windows/preferences_window/preferences_window.vala
Expand Down
1 change: 1 addition & 0 deletions main/data/gresource.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@

<file>preferences_window/account_preferences_subpage.ui</file>
<file>preferences_window/add_account_dialog.ui</file>
<file>preferences_window/change_password_dialog.ui</file>
<file>preferences_window/general_preferences_page.ui</file>
<file>preferences_window/preferences_window.ui</file>

Expand Down
22 changes: 22 additions & 0 deletions main/data/preferences_window/account_preferences_subpage.ui
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,28 @@
<property name="title" translatable="yes">Local alias</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="password_change">
<property name="title" translatable="yes">Password</property>
<child type="suffix">
<object class="GtkBox">
<property name="opacity">0.7</property>
<property name="spacing">6</property>
<property name="orientation">horizontal</property>
<child>
<object class="GtkLabel">
<property name="label">•••••</property>
</object>
</child>
<child>
<object class="GtkImage">
<property name="icon-name">go-next-symbolic</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="connection_status">
<property name="title" translatable="yes">Connection status</property>
Expand Down
85 changes: 85 additions & 0 deletions main/data/preferences_window/change_password_dialog.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="DinoUiChangePasswordDialog">
<property name="title" translatable="1">Change password</property>
<property name="default-width">430</property>
<property name="default-height">270</property>
<property name="modal">True</property>
<child type="titlebar">
<object class="GtkHeaderBar">
<property name="show-title-buttons">False</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">Cancel</property>
</object>
</child>
<child type="end">
<object class="GtkButton" id="change_password_button">
<property name="sensitive">0</property>
<style>
<class name="suggested-action"/>
</style>
<child>
<object class="GtkStack" id="change_password_stack">
<child>
<object class="GtkStackPage">
<property name="name">label</property>
<property name="child">
<object class="GtkLabel" >
<property name="label" translatable="1">Change</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">spinner</property>
<property name="child">
<object class="GtkSpinner">
<property name="spinning">True</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesPage">
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwPasswordEntryRow" id="current_password_entry">
<property name="title" translatable="yes">Current password</property>
</object>
</child>
<child>
<object class="AdwPasswordEntryRow" id="new_password_entry">
<property name="title" translatable="yes">New password</property>
</object>
</child>
<child>
<object class="AdwPasswordEntryRow" id="confirm_new_password_entry">
<property name="title" translatable="yes">Confirm password</property>
</object>
</child>
<child>
<object class="GtkLabel" id="change_password_error_label">
<property name="halign">center</property>
<property name="hexpand">True</property>
<property name="margin-top">7</property>
<attributes>
<attribute name="scale" value="0.9"></attribute>
</attributes>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
</interface>
4 changes: 2 additions & 2 deletions main/data/preferences_window/preferences_window.ui
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<requires lib="gtk" version="4.0"/>
<object class="DinoUiViewModelPreferencesWindow" id="model" />
<template class="DinoUiPreferencesWindow">
<property name="default-width">500</property>
<property name="default-height">600</property>
<property name="default-width">700</property>
<property name="default-height">550</property>
<property name="modal">True</property>
<child>
<object class="DinoUiPreferencesWindowAccounts" id="accounts_page">
Expand Down
1 change: 1 addition & 0 deletions main/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ sources = files(
'src/windows/preferences_window/account_preferences_subpage.vala',
'src/windows/preferences_window/accounts_preferences_page.vala',
'src/windows/preferences_window/add_account_dialog.vala',
'src/windows/preferences_window/change_password_dialog.vala',
'src/windows/preferences_window/encryption_preferences_page.vala',
'src/windows/preferences_window/general_preferences_page.vala',
'src/windows/preferences_window/preferences_window.vala',
Expand Down
20 changes: 20 additions & 0 deletions main/src/view_model/preferences_window.vala
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ public class Dino.Ui.ViewModel.PreferencesWindow : Object {
update_data();
}

public ChangePasswordDialog get_change_password_dialog_model() {
return new ChangePasswordDialog() {
account = selected_account.account,
stream_interactor = stream_interactor
};
}

private void bind_general_page() {
var settings = Dino.Application.get_default().settings;
settings.bind_property("send-typing", general_page, "send-typing", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
Expand All @@ -107,3 +114,16 @@ public class Dino.Ui.ViewModel.PreferencesWindow : Object {
}
}

public class Dino.Ui.ViewModel.ChangePasswordDialog : Object {
public Entities.Account account { get; set; }
public StreamInteractor stream_interactor { get; set; }

public async string? change_password(string new_password) {
var res = yield stream_interactor.get_module(Register.IDENTITY).change_password(account, new_password);
if (res == null) {
account.password = new_password;
}
return res;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class Dino.Ui.AccountPreferencesSubpage : Gtk.Box {
[GtkChild] public unowned AvatarPicture avatar;
[GtkChild] public unowned Adw.ActionRow xmpp_address;
[GtkChild] public unowned Adw.EntryRow local_alias;
[GtkChild] public unowned Adw.ActionRow password_change;
[GtkChild] public unowned Adw.ActionRow connection_status;
[GtkChild] public unowned Button enter_password_button;
[GtkChild] public unowned Box avatar_menu_box;
Expand Down Expand Up @@ -50,6 +51,12 @@ public class Dino.Ui.AccountPreferencesSubpage : Gtk.Box {
remove_account_button.clicked.connect(() => {
show_remove_account_dialog();
});
password_change.activatable_widget = new Label("");
password_change.activated.connect(() => {
var dialog = new ChangePasswordDialog(model.get_change_password_dialog_model());
dialog.set_transient_for((Gtk.Window)this.get_root());
dialog.present();
});
enter_password_button.clicked.connect(() => {
var dialog = new Adw.MessageDialog((Window)this.get_root(), "Enter password for %s".printf(account.bare_jid.to_string()), null);
var password = new PasswordEntry() { show_peek_icon=true };
Expand All @@ -76,10 +83,10 @@ public class Dino.Ui.AccountPreferencesSubpage : Gtk.Box {
avatar.model = model.selected_account.avatar_model;
xmpp_address.subtitle = account.bare_jid.to_string();

if (alias_entry_changed != 0) local_alias_entry.disconnect(alias_entry_changed);
local_alias_entry.text = account.alias ?? "";
alias_entry_changed = local_alias_entry.changed.connect(() => {
account.alias = local_alias_entry.text;
if (alias_entry_changed != 0) local_alias.disconnect(alias_entry_changed);
local_alias.text = account.alias ?? "";
alias_entry_changed = local_alias.changed.connect(() => {
account.alias = local_alias.text;
});

bindings += account.bind_property("enabled", disable_account_button, "label", BindingFlags.SYNC_CREATE, (binding, from, ref to) => {
Expand All @@ -88,6 +95,7 @@ public class Dino.Ui.AccountPreferencesSubpage : Gtk.Box {
return true;
});
bindings += account.bind_property("enabled", avatar_menu_box, "visible", BindingFlags.SYNC_CREATE);
bindings += account.bind_property("enabled", password_change, "visible", BindingFlags.SYNC_CREATE);
bindings += account.bind_property("enabled", connection_status, "visible", BindingFlags.SYNC_CREATE);
bindings += model.selected_account.bind_property("connection-state", connection_status, "subtitle", BindingFlags.SYNC_CREATE, (binding, from, ref to) => {
to = get_status_label();
Expand Down
66 changes: 66 additions & 0 deletions main/src/windows/preferences_window/change_password_dialog.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Gee;
using Gtk;

using Dino.Entities;
using Xmpp;

namespace Dino.Ui{

[GtkTemplate (ui = "/im/dino/Dino/preferences_window/change_password_dialog.ui")]
public class ChangePasswordDialog : Gtk.Dialog {

[GtkChild] private unowned Button change_password_button;
[GtkChild] private unowned Stack change_password_stack;
[GtkChild] private unowned Button cancel_button;
[GtkChild] private unowned Adw.PasswordEntryRow current_password_entry;
[GtkChild] private unowned Adw.PasswordEntryRow new_password_entry;
[GtkChild] private unowned Adw.PasswordEntryRow confirm_new_password_entry;
[GtkChild] private unowned Label change_password_error_label;

private ViewModel.ChangePasswordDialog model;

public ChangePasswordDialog(ViewModel.ChangePasswordDialog model) {
Object(use_header_bar : 1);
this.model = model;

Util.force_error_color(change_password_error_label);
cancel_button.clicked.connect(() => { close(); });
current_password_entry.changed.connect(is_form_filled);
new_password_entry.changed.connect(is_form_filled);
confirm_new_password_entry.changed.connect(is_form_filled);
change_password_button.clicked.connect(on_change_password_button_clicked);
}

private void is_form_filled(){
if (current_password_entry.get_text().length > 0
&& new_password_entry.get_text().length > 0
&& confirm_new_password_entry.get_text().length > 0
&& new_password_entry.get_text() == confirm_new_password_entry.get_text()){
change_password_button.sensitive = true;
} else {
change_password_button.sensitive = false;
}
}

private async void on_change_password_button_clicked(){
string? pw_input = current_password_entry.get_text();
string? new_pw_input = new_password_entry.get_text();

if (pw_input != null && model.account.password == pw_input){
change_password_button.sensitive = false;
change_password_stack.visible_child_name = "spinner";
string? ret = yield model.change_password(new_pw_input);
change_password_button.sensitive = true;
change_password_stack.visible_child_name = "label";
if (ret == null) {
close();
}

change_password_error_label.label = "Error: %s".printf(ret);

} else {
change_password_error_label.label = "Wrong current password";
}
}
}
}
2 changes: 0 additions & 2 deletions main/src/windows/preferences_window/preferences_window.vala
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ public class Dino.Ui.PreferencesWindow : Adw.PreferencesWindow {
[GtkChild] public unowned ViewModel.PreferencesWindow model { get; }

construct {
this.default_height = 500;
this.default_width = 700;
this.can_navigate_back = true; // remove once we require Adw > 1.4
this.bind_property("model", accounts_page, "model", BindingFlags.SYNC_CREATE);
this.bind_property("model", account_page, "model", BindingFlags.SYNC_CREATE);
Expand Down
18 changes: 18 additions & 0 deletions xmpp-vala/src/module/xep/0077_in_band_registration.vala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ public class Module : XmppStreamNegotiationModule {
return null;
}

public async ErrorStanza? change_password(XmppStream stream, Jid jid, string new_pw) {
StanzaNode pw_change_node = new StanzaNode.build("query", NS_URI).add_self_xmlns();
StanzaNode username_node = new StanzaNode.build("username", NS_URI);
StanzaNode pw_node = new StanzaNode.build("password", NS_URI);
username_node.put_node(new StanzaNode.text(jid.localpart));
pw_node.put_node(new StanzaNode.text(new_pw));
pw_change_node.put_node(username_node);
pw_change_node.put_node(pw_node);
Iq.Stanza set_password_iq = new Iq.Stanza.set(pw_change_node) { to=jid.bare_jid.domain_jid };

Iq.Stanza chpw_result = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, set_password_iq);
if (chpw_result.is_error()) {
return chpw_result.get_error();
}

return null;
}

public override bool mandatory_outstanding(XmppStream stream) { return false; }

public override bool negotiation_active(XmppStream stream) { return false; }
Expand Down

0 comments on commit f8c0046

Please sign in to comment.