From 07af51fb1db363fbd0355e8cf0e1558e1182db74 Mon Sep 17 00:00:00 2001 From: David Cook Date: Wed, 12 May 2021 07:30:24 +0000 Subject: [PATCH] Bug 31380: Allow Koha Plugins to run using Mojolicious controllers This patch adds a new mount point in plack.psgi, which makes Koha able to run Mojolicious controllers. For example: http://localhost:8081/cgi-bin/koha/staff/plugins/run 0a) Apply patch 0b) cp debian/templates/plack.psgi /etc/koha/sites/kohadev/plack.psgi 0c) koha-plack --restart 1) Upload Koha Plugin koha-plugin-mojolicious.kpz 2) koha-plack --restart 3) Go to http://localhost:8081/cgi-bin/koha/plugins/plugins-home.pl 4) Next to "Mojolicious Test", click "Actions", then click "Run tool" 5) Note that the web page looks perfect 6) Note that the URL is a bit different to normal: http://localhost:8081/cgi-bin/koha/staff/plugins/run?class=Koha%3A%3APlugin%3A%3AProsentient%3A%3AMojolicious&method=tool instead of: http://localhost:8081/cgi-bin/koha/plugins/run.pl?class=Koha%3A%3APlugin%3A%3AProsentient%3A%3AMojolicious&method=tool Note: The test Koha plugin will only work with a Mojolicious controller, but it could be easily made to work with both --- Koha/Auth.pm | 87 +++++++++++++++++++++++++ Koha/Mojo/Plugins/Core.pm | 80 +++++++++++++++++++++++ Koha/Mojo/Staff.pm | 93 +++++++++++++++++++++++++++ Koha/Mojo/Staff/Controller/Plugins.pm | 52 +++++++++++++++ Koha/Template.pm | 64 ++++++++++++++++++ debian/templates/plack.psgi | 32 +++++++++ staff | 21 ++++++ 7 files changed, 429 insertions(+) create mode 100644 Koha/Auth.pm create mode 100644 Koha/Mojo/Plugins/Core.pm create mode 100644 Koha/Mojo/Staff.pm create mode 100644 Koha/Mojo/Staff/Controller/Plugins.pm create mode 100644 Koha/Template.pm create mode 100755 staff diff --git a/Koha/Auth.pm b/Koha/Auth.pm new file mode 100644 index 0000000000..30c95a0ff1 --- /dev/null +++ b/Koha/Auth.pm @@ -0,0 +1,87 @@ +package Koha::Auth; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use C4::Auth qw//; +use Koha::Patrons; + +=head1 NAME + +Koha::Auth - Koha class for handling authentication and authorization + +=head1 SYNOPSIS + +=head2 METHODS + +=head3 authenticate + + my $user = Koha::Auth->authenticate({ + sessionID => $sessionID, + }); + +Verifies that this user has an authenticated Koha session + +=cut + +sub authenticate { + my ($class,$args) = @_; + my ($auth_user,$auth_session); + my $sessionID = $args->{sessionID}; + if ($sessionID){ + my $flags; + my ( $return, $session, $more_info) = C4::Auth::check_cookie_auth($sessionID,$flags, + { remote_addr => $ENV{REMOTE_ADDR}, skip_version_check => 1 } + ); + if ($return && $return eq 'ok' && $session){ + my $userid = $session->param('id'); + if ( $userid ){ + my $patron = Koha::Patrons->find({ userid => $userid }); + if ($patron){ + $auth_user = $patron; + $auth_session = $session; + } + } + } + } + return ($auth_user,$auth_session); +} + +=head3 authorize + + my $flags = Koha::Auth->authorize({ + session => $session, + flagsrequired => { self_check => 'self_checkout_module' }, + }); + +=cut + +sub authorize { + my ($class,$args) = @_; + my $flags; + my $session = $args->{session}; + my $flagsrequired = $args->{flagsrequired}; + if ($session){ + my $userid = $session->param('id'); + if ($userid){ + $flags = C4::Auth::haspermission($userid,$flagsrequired); + } + } + return $flags; +} + +1; diff --git a/Koha/Mojo/Plugins/Core.pm b/Koha/Mojo/Plugins/Core.pm new file mode 100644 index 0000000000..7e3c81d5ed --- /dev/null +++ b/Koha/Mojo/Plugins/Core.pm @@ -0,0 +1,80 @@ +package Koha::Mojo::Plugins::Core; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Mojo::Base 'Mojolicious::Plugin'; + +use Koha::Auth; +use Koha::Auth::Permissions; +use Koha::Template; + +=head1 NAME + +Koha::Mojo::Plugins::Core - Mojolicious plugin that adds AuthZ and + template helpers for Koha Mojolicious controllers + +=head1 SYNOPSIS + +=head2 METHODS + +=head3 register + +=cut + +sub register { + my ($self, $app, $conf) = @_; + + $app->helper( + staff_authorize => sub { + my ($c,$args) = @_; + my $session = $c->stash->{__koha_session__}; + my $flags = Koha::Auth->authorize({ + session => $session, + flagsrequired => $args->{flagsrequired}, + }); + if($flags){ + $c->stash->{__koha_flags__} = $flags; + $c->stash->{__koha_authz__} = Koha::Auth::Permissions->get_authz_from_flags({ flags => $flags }); + return ($flags,$c->stash->{__koha__user__}); + } + else { + $c->render( text => 'Forbidden', status => 403 ); + return; + } + } + ); + + $app->helper( + prepare_template => sub { + my ($c,$args) = @_; + my $template_filename = $args->{template_filename}; + my $interface = $args->{interface}; + my $template = Koha::Template::prepare_template({ + template_filename => $template_filename, + interface => $interface, + koha_session => $c->stash->{__koha_session__}, + koha_user => $c->stash->{__koha_user__}, + koha_authz => $c->stash->{__koha_authz__}, + }); + return $template; + } + ); +} + +1; + diff --git a/Koha/Mojo/Staff.pm b/Koha/Mojo/Staff.pm new file mode 100644 index 0000000000..b05d2f2bfb --- /dev/null +++ b/Koha/Mojo/Staff.pm @@ -0,0 +1,93 @@ +package Koha::Mojo::Staff; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Mojo::Base 'Mojolicious'; + +use C4::Context; +use Koha::Auth; + +=head1 NAME + +Koha::Mojo::Staff - Mojolicious application for Koha staff interface + +=head1 SYNOPSIS + +=head2 METHODS + +=head3 startup + +=cut + +sub startup { + my $self = shift; + + #NOTE: Load plugin which sets up core helper methods needed for any Koha app + $self->plugin('Koha::Mojo::Plugins::Core'); + + # Router + my $r = $self->routes; + + #NOTE: The /login route is public and does not require authentication + #FIXME: Implement a Mojolicious login route + #$r->get('/login')->to( controller => 'login', action => 'index' ); + + #NOTE: All other routes require authentication + my $auth = $r->under('/' => sub { + my $c = shift; + my $sessionID = $c->cookie('CGISESSID'); + my ($user,$session) = Koha::Auth->authenticate({ + sessionID => $sessionID, + }); + if ($user && $session){ + $c->stash->{__koha_user__} = $user; + $c->stash->{__koha_session__} = $session; + $c->cookie( + 'CGISESSID' => $session->id, + { + httponly => 1, + secure => ( C4::Context->https_enabled() ? 1 : 0 ), + path => '/', + } + ); + return 1; + } + else { + #FIXME: In future, redirect to a /login route, or prompt for login here + $c->redirect_to('/index.html'); + return; + } + }); + my $plugins = $auth->under('plugins'); + #NOTE: Mojolicious 9 seems to require explicitly defining all routes (rather than getting :controller and :action from the path... + $plugins->any(['GET','POST'] => '/run')->to(controller => 'Plugins', action => 'run'); + + #NOTE: Catch-all route to redirect to CGI 404 handler for any unmatched routes + $auth->any('/*' => sub { + my $c = shift; + my ($flags,$loggedinuser) = $c->staff_authorize(); + my $template = $c->prepare_template({ + template_filename => 'errors/errorpage.tt', + interface => 'intranet', + }); + $template->{VARS}->{errno} = 404; + $template->{VARS}->{admin} = C4::Context->preference('KohaAdminEmailAddress'); + my $status = 404; + $c->render( text => $template->output(), status => $status ); + }); +} + +1; diff --git a/Koha/Mojo/Staff/Controller/Plugins.pm b/Koha/Mojo/Staff/Controller/Plugins.pm new file mode 100644 index 0000000000..880115593d --- /dev/null +++ b/Koha/Mojo/Staff/Controller/Plugins.pm @@ -0,0 +1,52 @@ +package Koha::Mojo::Staff::Controller::Plugins; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Mojo::Base 'Mojolicious::Controller'; + +use C4::Context; +use Koha::Plugins::Handler; + +=head1 NAME + +Koha::Mojo::Staff::Controller::Plugins - Mojolicious controller for running Koha plugins + +=head1 SYNOPSIS + +=head2 METHODS + +=head3 run + +=cut + +sub run { + my $c = shift; + my $class = $c->param('class'); + my $method = $c->param('method'); + my ($flags,$loggedinuser) = $c->staff_authorize({ flagsrequired => { 'plugins' => $method } }); + my $plugins_enabled = C4::Context->config("enable_plugins"); + if ( $plugins_enabled ) { + my $plugin = Koha::Plugins::Handler->run( { class => $class, method => $method, cgi => $c } ); + } else { + my $template = $c->prepare_template({ + template_filename => 'plugins/plugins-disabled.tt', + interface => 'intranet', + }); + $c->render( text => $template->output ); + } +} + +1; diff --git a/Koha/Template.pm b/Koha/Template.pm new file mode 100644 index 0000000000..dd5fc72d13 --- /dev/null +++ b/Koha/Template.pm @@ -0,0 +1,64 @@ +package Koha::Template; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use C4::Templates; + +=head1 NAME + +Koha::Template - Koha class for preparing a template for any Koha web view + +=head1 SYNOPSIS + +=head2 METHODS + +=head3 prepare_template + +=cut + +sub prepare_template { + my ($args) = @_; + my $template; + my $session = $args->{koha_session}; + my $user = $args->{koha_user}; + my $authz = $args->{koha_authz}; + my $template_filename = $args->{template_filename}; + my $interface = $args->{interface}; + if ($template_filename && $interface){ + $template = C4::Templates::gettemplate($template_filename,$interface); + if ($template){ + if ($session){ + $template->{VARS}->{ sessionID } = $session->param('id'); + } + if ($user){ + $template->{VARS}->{ logged_in_user } = $user; + $template->{VARS}->{ loggedinusernumber } = $user->borrowernumber; + $template->{VARS}->{ loggedinusername } = $user->userid; + } + if ($authz){ + foreach my $key ( keys %$authz ){ + $template->{VARS}->{ $key } = $authz->{$key}; + } + } + #NOTE: Instead of including syspref code here like in C4::Auth, start switching to Koha.Preference in templates + } + } + return $template; +} + +1; diff --git a/debian/templates/plack.psgi b/debian/templates/plack.psgi index 89cd6ef48f..fd4beb397f 100644 --- a/debian/templates/plack.psgi +++ b/debian/templates/plack.psgi @@ -66,6 +66,19 @@ my $apiv1 = builder { $server->to_psgi_app; }; +my $staff_interface = builder { + my $server = Mojo::Server::PSGI->new; + $server->build_app('Koha::Mojo::Staff'); + my $app = $server->app; + $app->hook( before_dispatch => sub { + my $c = shift; + #NOTE: Rewrite the base path to strip off the mount prefix + my $path = $c->req->url->base->path(Mojo::Path->new); + + }); + $server->to_psgi_app; +}; + Koha::Logger->_init; builder { @@ -114,6 +127,25 @@ builder { } $intranet; }; + mount '/intranet/staff' => builder { + #NOTE: it is important that these are relative links + enable 'ErrorDocument', + 400 => 'errors/400.pl', + 401 => 'errors/401.pl', + 402 => 'errors/402.pl', + 403 => 'errors/403.pl', + 404 => 'errors/404.pl', + 500 => 'errors/500.pl', + subrequest => 1; + #NOTE: Without this middleware to catch fatal errors, ErrorDocument won't be able to render a 500 document + #NOTE: This middleware must be closer to the PSGI app than ErrorDocument + enable "HTTPExceptions"; + if ( Log::Log4perl->get_logger('plack-intranet')->has_appenders ){ + enable 'Log4perl', category => 'plack-intranet'; + enable 'LogWarn'; + } + $staff_interface; + }; mount '/api/v1/app.pl' => builder { if ( Log::Log4perl->get_logger('plack-api')->has_appenders ){ enable 'Log4perl', category => 'plack-api'; diff --git a/staff b/staff new file mode 100755 index 0000000000..8be89e4bf7 --- /dev/null +++ b/staff @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +require Mojolicious::Commands; +Mojolicious::Commands->start_app('Koha::Mojo::Staff'); -- 2.30.2