From ad94a1dd6a8665c87b925e372594a9299c34c35b Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Tue, 9 Jul 2024 13:10:27 +0200 Subject: [PATCH] Bug 37286: Fix REST API authentication when using Mojo apps Koha::REST::V1::Auth checks the request URL path and do various things depending on how it looks. For instance, it allows everyone to access paths starting with "/api/v1/oauth/" But because of how Koha::REST::V1 was written, when using mojolicious applications Koha::App::Intranet and Koha::App::Opac, paths had to add a path prefix ("/api"), which means the final path as seen by Koha::REST::V1::Auth looked like this: "/api/api/v1/oauth". I said "had to", but actually there is another way that does not require this path manipulation and that's what this patch does: Koha::REST::V1 now accepts a configuration parameter that allows to change the base path for API routes, which allows Koha::App::Plugin::RESTV1 (used by Koha::App::Intranet and Koha::App::Opac) to generate the right routes. This configuration parameter defaults to "/api/v1", so when outside of Koha::App::Intranet and Koha::App::Opac (when using debian/templates/plack.psgi for instance), the behavior is unchanged. Test plan: 1. Do not apply the patch yet 2. Run `bin/intranet daemon -l http://*:8080` 3. Run `curl -i -d{} http://127.0.0.1:8080/api/v1/oauth/token` It should return a 403 error, with an error message "Authentication failure". 4. Stop `bin/intranet daemon -l http://*:8080` by hitting Ctrl-C on the terminal you started it 5. Apply the patch 6. Run `bin/intranet daemon -l http://*:8080` again 7. Run `curl -i -d{} http://127.0.0.1:8080/api/v1/oauth/token` This time it should return a 400 error with an error message saying the "grant_type" property is missing. This error is normal as we did not send any data in the POST request body, and seeing this means Koha allowed us to use that route because it recognized '/api/v1/oauth/' at the start of the URL path 8. You can do the same test with `bin/opac` --- Koha/App/Intranet.pm | 6 ------ Koha/App/Opac.pm | 6 ------ Koha/App/Plugin/RESTV1.pm | 3 ++- Koha/REST/V1.pm | 4 +++- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/Koha/App/Intranet.pm b/Koha/App/Intranet.pm index bf66e9e62b..0b103c83a5 100644 --- a/Koha/App/Intranet.pm +++ b/Koha/App/Intranet.pm @@ -35,7 +35,6 @@ sub startup { $self->plugin('CGIBinKoha'); # Create routes for API - # FIXME This generates routes like this: /api/api/v1/... $self->plugin('RESTV1'); $self->hook(before_dispatch => \&_before_dispatch); @@ -54,11 +53,6 @@ sub _before_dispatch { # Remove Koha version from URL $path =~ s/_\d{2}\.\d{7}\.(js|css)/.$1/; - # See FIXME above - if ($path =~ m|^/api/v|) { - $path = '/api' . $path; - } - $c->req->url->path->parse($path); } diff --git a/Koha/App/Opac.pm b/Koha/App/Opac.pm index 7af85e1515..5a2bcd30db 100644 --- a/Koha/App/Opac.pm +++ b/Koha/App/Opac.pm @@ -35,7 +35,6 @@ sub startup { $self->plugin('CGIBinKoha', opac => 1); # Create routes for API - # FIXME This generates routes like this: /api/api/v1/... $self->plugin('RESTV1'); $self->hook(before_dispatch => \&_before_dispatch); @@ -54,11 +53,6 @@ sub _before_dispatch { # Remove Koha version from URL $path =~ s/_\d{2}\.\d{7}\.(js|css)/.$1/; - # See FIXME above - if ($path =~ m|^/api/v|) { - $path = '/api' . $path; - } - $c->req->url->path->parse($path); } diff --git a/Koha/App/Plugin/RESTV1.pm b/Koha/App/Plugin/RESTV1.pm index 883b989cbd..44c64d6c3f 100644 --- a/Koha/App/Plugin/RESTV1.pm +++ b/Koha/App/Plugin/RESTV1.pm @@ -26,7 +26,8 @@ use Koha::REST::V1; sub register { my ($self, $app) = @_; - $app->routes->any('/api')->partial(1)->to(app => Koha::REST::V1->new); + my $v1 = Koha::REST::V1->new(config => { route => '/v1' }); + $app->routes->any('/api')->partial(1)->to(app => $v1); } 1; diff --git a/Koha/REST/V1.pm b/Koha/REST/V1.pm index 52bd15697e..34100363ff 100644 --- a/Koha/REST/V1.pm +++ b/Koha/REST/V1.pm @@ -98,10 +98,12 @@ sub startup { } ) unless C4::Context->needs_install; # load only if Koha is installed + my $route = $self->config('route') // '/api/v1'; + $self->plugin( OpenAPI => { spec => $spec, - route => $self->routes->under('/api/v1')->to('Auth#under'), + route => $self->routes->under($route)->to('Auth#under'), } ); -- 2.30.2