From 25a805e8e417858738a32eb250c6d1934a814f71 Mon Sep 17 00:00:00 2001
From: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Date: Mon, 5 May 2025 10:17:22 +0200
Subject: [PATCH] Bug 39826: UTF8-decode vendor interface's password on display
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

We store an encrypted version of the passwords for vendor's interfaces, but we don't utf8 decode it after it is decrypted

Test plan:
0. Do not apply this patch
1. Create or edit a vendor and add at least one interface with non ascii
   characters, eg. émyPassword❤
2. Go to the detail view of the vendor and click the "Show password"
   link
=> The password is not correctly utf8-decoded
3. Apply this patch, repeat 2
=> The password is correctly displayed
4. Repeat 1 and 2
=> The password is correctly displayed

Note that maybe we should make sure the encrypt_hex is getting a valid
utf8-encoded variable.
---
 Koha/Acquisition/Bookseller/Interface.pm      |  3 ++-
 t/db_dependent/Koha/Acquisition/Booksellers.t | 11 ++++++-----
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/Koha/Acquisition/Bookseller/Interface.pm b/Koha/Acquisition/Bookseller/Interface.pm
index 90195ab6a25..71ec82efc36 100644
--- a/Koha/Acquisition/Bookseller/Interface.pm
+++ b/Koha/Acquisition/Bookseller/Interface.pm
@@ -16,6 +16,7 @@ package Koha::Acquisition::Bookseller::Interface;
 # along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use Modern::Perl;
+use Encode qw(decode_utf8);
 use Koha::Encryption;
 
 use base qw( Koha::Object );
@@ -58,7 +59,7 @@ Decrypt the password and return its plain text form.
 
 sub plain_text_password {
     my ($self) = @_;
-    return Koha::Encryption->new->decrypt_hex( $self->password )
+    return decode_utf8( Koha::Encryption->new->decrypt_hex( $self->password ) )
         if $self->password;
 }
 
diff --git a/t/db_dependent/Koha/Acquisition/Booksellers.t b/t/db_dependent/Koha/Acquisition/Booksellers.t
index 464d9d472dc..e65e01fcefb 100755
--- a/t/db_dependent/Koha/Acquisition/Booksellers.t
+++ b/t/db_dependent/Koha/Acquisition/Booksellers.t
@@ -17,6 +17,7 @@
 
 use Modern::Perl;
 
+use utf8;
 use Test::More tests => 8;
 
 use t::lib::TestBuilder;
@@ -218,7 +219,7 @@ subtest 'interfaces' => sub {
     is( $interfaces->count, 2,                                           '2 interfaces stored' );
     is( ref($interfaces),   'Koha::Acquisition::Bookseller::Interfaces', 'Type is correct' );
 
-    $vendor->interfaces( [ { name => 'first interface', login => 'one_login', password => 'oneP@sswOrd' } ] );
+    $vendor->interfaces( [ { name => 'first interface', login => 'one_login', password => 'oneP@sswOrd❤' } ] );
     $vendor     = $vendor->get_from_storage;
     $interfaces = $vendor->interfaces;
     is( $interfaces->count, 1, '1 interface stored' );
@@ -226,10 +227,10 @@ subtest 'interfaces' => sub {
     is( $interface->name,  'first interface', 'name correctly saved' );
     is( $interface->login, 'one_login',       'login correctly saved' );
     is( $interface->uri,   undef,             'no value is stored as NULL' );
-    isnt( $interface->password, 'oneP@sswOrd', 'Password is not stored in plain text' );
-    isnt( $interface->password, '',            'Password is not removed' );
-    isnt( $interface->password, undef,         'Password is not set to NULL' );
-    is( $interface->plain_text_password, 'oneP@sswOrd', 'Password can be retrieved using ->plain_text_password' );
+    isnt( $interface->password, 'oneP@sswOrd❤', 'Password is not stored in plain text' );
+    isnt( $interface->password, '',             'Password is not removed' );
+    isnt( $interface->password, undef,          'Password is not set to NULL' );
+    is( $interface->plain_text_password, 'oneP@sswOrd❤', 'Password can be retrieved using ->plain_text_password' );
 
     $schema->storage->txn_rollback();
 };
-- 
2.34.1