Bug 37543: Use CSRF tokens in connexion_import_daemon.pl

Since version 24.05, due to the changes mentioned at
https://wiki.koha-community.org/wiki/Koha_/svc/_HTTP_API#Changes_coming_in_Koha_24.05 ,
the `connexion_import_daemon.pl` stopped working. The reason for this is that
it did not use CSRF tokens.

To test:
1. Get a Koha instance on 24.05, before applying the patch.
2. Create a plain text file somewhere on the server containing a raw MARC
   record (not XML). Let's call it `marc.txt`.
3. On the server, create a config file like this:
```
host: 0.0.0.0
port: 5500
koha: http://localhost:82  # Where 82 is the port of the Koha staff interface.
user: foo  # A Koha staff user.
password: Fooo1234  # The Koha staff user's password.
import_mode: stage
```
4. Run `./connexion_import_daemon.pl --config the-config-file-path`
5. In another terminal on the same server (or from anywhere that can reach the
   port opened by the `connexion_import_daemon.pl` script,
   run `nc localhost 5500 < marc.txt`
6. Observe in the stderr of the daemon script: `Response: Unsuccessful request`
7. Stop the daemon script.
8. Apply the patch and repeat steps 4 and 5.
9. Observe in the stderr of the daemon script:
   `Response: Success.  Batch number ... - biblio record number HASH(...) added to Koha`
10. Check at /cgi-bin/koha/tools/manage-marc-import.pl for a batch named
    `(webservice)`. It should contain one record now. This is how we know that
    authentication between the daemon and Koha worked, which is what this
    patch tries to address.

Thanks-to: David Cook <dcook@prosentient.com.au>
Sponsored-by: Reformational Study Centre <www.refstudycentre.com>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Paul Derscheid <paul.derscheid@lmscloud.de>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
(cherry picked from commit 0e9ea3c9f5)
Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
Rudolf Byker 2024-08-01 16:14:29 +02:00 committed by Lucas Gass
parent f66130b020
commit c850195a67
1 changed files with 47 additions and 25 deletions

View File

@ -104,7 +104,7 @@ use MARC::File::XML;
use constant CLIENT_READ_TIMEOUT => 5;
use constant CLIENT_READ_BUFFER_SIZE => 100000;
use constant AUTH_URI => "/cgi-bin/koha/mainpage.pl";
use constant AUTH_URI => "/cgi-bin/koha/svc/authentication";
use constant IMPORT_SVC_URI => "/cgi-bin/koha/svc/import_bib";
sub new {
@ -239,6 +239,32 @@ sub _ua {
return $ua;
}
sub get_current_csrf_token {
my $self = shift;
my $ua = $self->{ua};
my $url = $self->{koha} . AUTH_URI;
return $ua->get($url)->header('CSRF-TOKEN');
}
sub authenticate {
my $self = shift;
my $ua = $self->{ua};
my $url = $self->{koha} . AUTH_URI;
my $resp = $ua->post(
$url,
{
login_userid => $self->{user},
login_password => $self->{password},
csrf_token => $self->get_current_csrf_token,
}
);
if ( !$resp->is_success ) {
$self->log("Authentication failed", $resp->request->as_string, $resp->as_string);
return;
}
return $resp->header('CSRF-TOKEN');
}
sub read_request {
my ( $self, $io ) = @_;
@ -355,38 +381,34 @@ sub handle_request {
}
my $base_url = $self->{koha};
my $post_body = {
'nomatch_action' => $self->{params}->{nomatch_action},
'overlay_action' => $self->{params}->{overlay_action},
'match' => $self->{params}->{match},
'import_mode' => $self->{params}->{import_mode},
'framework' => $self->{params}->{framework},
'overlay_framework' => $self->{params}->{overlay_framework},
'item_action' => $self->{params}->{item_action},
'xml' => $data
};
# If we have a token, try it, else, authenticate for the first time.
$self->{csrf_token} = $self->authenticate unless $self->{csrf_token};
my $resp = $ua->post(
$base_url . IMPORT_SVC_URI,
{
'nomatch_action' => $self->{params}->{nomatch_action},
'overlay_action' => $self->{params}->{overlay_action},
'match' => $self->{params}->{match},
'import_mode' => $self->{params}->{import_mode},
'framework' => $self->{params}->{framework},
'overlay_framework' => $self->{params}->{overlay_framework},
'item_action' => $self->{params}->{item_action},
'xml' => $data
}
$post_body,
csrf_token => $self->{csrf_token},
);
my $status = $resp->code;
if ( $status == HTTP_UNAUTHORIZED || $status == HTTP_FORBIDDEN ) {
my $user = $self->{user};
my $password = $self->{password};
$resp = $ua->post( $base_url . AUTH_URI, { userid => $user, password => $password } );
# Our token might have expired. Re-authenticate and post again.
$self->{csrf_token} = $self->authenticate;
$resp = $ua->post(
$base_url . IMPORT_SVC_URI,
{
'nomatch_action' => $self->{params}->{nomatch_action},
'overlay_action' => $self->{params}->{overlay_action},
'match' => $self->{params}->{match},
'import_mode' => $self->{params}->{import_mode},
'framework' => $self->{params}->{framework},
'overlay_framework' => $self->{params}->{overlay_framework},
'item_action' => $self->{params}->{item_action},
'xml' => $data
}
) if $resp->is_success;
$post_body,
csrf_token => $self->{csrf_token},
)
}
unless ($resp->is_success) {
$self->log("Unsuccessful request", $resp->request->as_string, $resp->as_string);