Bug 36084: Add CSRF token support to svc/authentication

GET svc/authentication will return a CSRF token in a response header

POST svc/authentication requires a CSRF token which can be sourced
from the response header of GET svc/authentication or some other
place like the meta element on a HTML page

Note: misc/migration_tools/koha-svc.pl is a simple script which
can be used to practically evaluate svc/authentication and svc/bib

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
This commit is contained in:
David Cook 2024-02-21 06:17:50 +00:00 committed by Jonathan Druart
parent 61f1f88c5c
commit 0fe82b601b
Signed by: jonathan.druart
GPG key ID: A085E712BEF0E0F0
3 changed files with 37 additions and 11 deletions

View file

@ -1602,7 +1602,7 @@ sub check_api_auth {
}
my ( $sessionID, $session );
unless ( $query->param('userid') ) {
unless ( $query->param('login_userid') ) {
$sessionID = $query->cookie("CGISESSID");
}
if ( $sessionID && not( $cas && $query->param('PT') ) ) {

View file

@ -104,7 +104,15 @@ sub new {
my $ua = LWP::UserAgent->new();
$ua->cookie_jar({});
my $resp = $ua->post( "$url/authentication", {userid =>$user, password => $password} );
my $get_resp = $ua->get("$url/authentication");
my $csrf_token = $get_resp->header('X-CSRF-TOKEN');
$self->{csrf_token} = $csrf_token;
my $resp = $ua->post(
"$url/authentication",
{ login_userid => $user, login_password => $password, csrf_token => $csrf_token }
);
die $resp->status_line unless $resp->is_success;
warn "# $user $url = ", $resp->decoded_content, "\n" if $self->{debug};
@ -140,7 +148,11 @@ sub post {
my ($self,$biblionumber,$marcxml) = @_;
my $url = $self->{url};
warn "# post $url/bib/$biblionumber\n" if $self->{debug};
my $resp = $self->{ua}->post( "$url/bib/$biblionumber", 'Content_type' => 'text/xml', Content => $marcxml );
my $csrf_token = $self->{csrf_token};
my $resp = $self->{ua}->post(
"$url/bib/$biblionumber", 'Content_type' => 'text/xml', Content => $marcxml,
csrf_token => $csrf_token
);
die $resp->status_line unless $resp->is_success;
return $resp->decoded_content;
}

View file

@ -21,7 +21,7 @@
use Modern::Perl;
use CGI qw ( -utf8 );
use C4::Auth qw/check_api_auth/;
use C4::Auth qw/check_api_auth get_session/;
use XML::Simple;
my $query = CGI->new;
@ -41,12 +41,26 @@ my $query = CGI->new;
# 3. The session cookie should not be (directly) sent back to the user's
# web browser, but instead should be stored and submitted by biblios.
my ($status, $cookie, $sessionID) = check_api_auth($query, { editcatalogue => 'edit_catalogue'} );
if ($status eq "ok") {
print $query->header(-type => 'text/xml', cookie => $cookie);
my $csrf_token;
my ( $status, $cookie, $sessionID ) = check_api_auth( $query, { editcatalogue => 'edit_catalogue' } );
if ( $status eq "ok" ) {
$csrf_token = Koha::Token->new->generate_csrf( { session_id => scalar $sessionID } );
} else {
print $query->header(-type => 'text/xml');
my $session = C4::Auth::get_session("");
$session->param( 'ip', $session->remote_addr() );
$session->param( 'lasttime', time() );
$session->param( 'sessiontype', 'anon' );
$session->param( 'interface', 'api' );
$session->flush();
$sessionID = $session->id();
$cookie = $query->cookie(
-name => 'CGISESSID',
-value => $sessionID,
-HttpOnly => 1,
-secure => ( C4::Context->https_enabled() ? 1 : 0 ),
-sameSite => 'Lax'
);
$csrf_token = Koha::Token->new->generate_csrf( { session_id => scalar $sessionID } );
}
print XMLout({ status => $status }, NoAttr => 1, RootName => 'response', XMLDecl => 1);
print $query->header( -type => 'text/xml', cookie => $cookie, -'X-CSRF_TOKEN' => $csrf_token );
print XMLout( { status => $status }, NoAttr => 1, RootName => 'response', XMLDecl => 1 );