aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfiaxh <git@lightrise.org>2024-09-16 22:47:49 +0200
committerfiaxh <git@lightrise.org>2024-09-21 17:06:20 +0100
commitf8c004630f56914438fa1b114530f639748e41c1 (patch)
tree4a9d2ae1de436c00e9d1ac75e05426291be52c4b
parentb09556f03349b51d95c14d1046add355a4754e01 (diff)
downloaddino-f8c004630f56914438fa1b114530f639748e41c1.tar.gz
dino-f8c004630f56914438fa1b114530f639748e41c1.zip
Add change password functionality
Co-authored-by: Stanislav Malishevskiy <stanislav.malishevskiy@gmail.com>
-rw-r--r--libdino/src/service/registration.vala6
-rw-r--r--main/CMakeLists.txt2
-rw-r--r--main/data/gresource.xml1
-rw-r--r--main/data/preferences_window/account_preferences_subpage.ui22
-rw-r--r--main/data/preferences_window/change_password_dialog.ui85
-rw-r--r--main/data/preferences_window/preferences_window.ui4
-rw-r--r--main/meson.build1
-rw-r--r--main/src/view_model/preferences_window.vala20
-rw-r--r--main/src/windows/preferences_window/account_preferences_subpage.vala16
-rw-r--r--main/src/windows/preferences_window/change_password_dialog.vala66
-rw-r--r--main/src/windows/preferences_window/preferences_window.vala2
-rw-r--r--xmpp-vala/src/module/xep/0077_in_band_registration.vala18
12 files changed, 235 insertions, 8 deletions
diff --git a/libdino/src/service/registration.vala b/libdino/src/service/registration.vala
index 4255f322..8a8e75b1 100644
--- a/libdino/src/service/registration.vala
+++ b/libdino/src/service/registration.vala
@@ -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; }
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 13111de8..fe7528cf 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -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
@@ -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
diff --git a/main/data/gresource.xml b/main/data/gresource.xml
index 304fa7a4..edceea3e 100644
--- a/main/data/gresource.xml
+++ b/main/data/gresource.xml
@@ -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>
diff --git a/main/data/preferences_window/account_preferences_subpage.ui b/main/data/preferences_window/account_preferences_subpage.ui
index fa273053..a4e0b300 100644
--- a/main/data/preferences_window/account_preferences_subpage.ui
+++ b/main/data/preferences_window/account_preferences_subpage.ui
@@ -80,6 +80,28 @@
</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>
<style>
diff --git a/main/data/preferences_window/change_password_dialog.ui b/main/data/preferences_window/change_password_dialog.ui
new file mode 100644
index 00000000..350d2a2d
--- /dev/null
+++ b/main/data/preferences_window/change_password_dialog.ui
@@ -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> \ No newline at end of file
diff --git a/main/data/preferences_window/preferences_window.ui b/main/data/preferences_window/preferences_window.ui
index d262dd76..3cf08f77 100644
--- a/main/data/preferences_window/preferences_window.ui
+++ b/main/data/preferences_window/preferences_window.ui
@@ -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">
diff --git a/main/meson.build b/main/meson.build
index a2902dbd..30a28032 100644
--- a/main/meson.build
+++ b/main/meson.build
@@ -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',
diff --git a/main/src/view_model/preferences_window.vala b/main/src/view_model/preferences_window.vala
index 9cc5a80e..7d967605 100644
--- a/main/src/view_model/preferences_window.vala
+++ b/main/src/view_model/preferences_window.vala
@@ -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);
@@ -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;
+ }
+}
+
diff --git a/main/src/windows/preferences_window/account_preferences_subpage.vala b/main/src/windows/preferences_window/account_preferences_subpage.vala
index d6ddb2b4..1a7d5696 100644
--- a/main/src/windows/preferences_window/account_preferences_subpage.vala
+++ b/main/src/windows/preferences_window/account_preferences_subpage.vala
@@ -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;
@@ -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 };
@@ -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) => {
@@ -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();
diff --git a/main/src/windows/preferences_window/change_password_dialog.vala b/main/src/windows/preferences_window/change_password_dialog.vala
new file mode 100644
index 00000000..f0c7dcf9
--- /dev/null
+++ b/main/src/windows/preferences_window/change_password_dialog.vala
@@ -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";
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/windows/preferences_window/preferences_window.vala b/main/src/windows/preferences_window/preferences_window.vala
index 1a75aa0a..662a6924 100644
--- a/main/src/windows/preferences_window/preferences_window.vala
+++ b/main/src/windows/preferences_window/preferences_window.vala
@@ -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);
diff --git a/xmpp-vala/src/module/xep/0077_in_band_registration.vala b/xmpp-vala/src/module/xep/0077_in_band_registration.vala
index baaa4ee0..87b654a8 100644
--- a/xmpp-vala/src/module/xep/0077_in_band_registration.vala
+++ b/xmpp-vala/src/module/xep/0077_in_band_registration.vala
@@ -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; }