From a445bb0ab38fb752cf93fc1aceaea7b23db43ea8 Mon Sep 17 00:00:00 2001 From: Andrii Nugged Date: Mon, 15 Jun 2026 08:25:32 +0300 Subject: [PATCH] Bug 42845: Add ILL config endpoint for Vue app The ILL Vue app currently reads ILLModule and ILLPartnerCode through the system preferences service. That service requires parameters/manage_sysprefs, so a staff user with ILL permissions but without system preference administration permissions cannot load the ILL module. Add an ILL-specific /ill/config REST endpoint protected by the ill permission, and switch the ILL Vue app to load its configuration from that endpoint. This follows the module-specific configuration endpoint pattern introduced by bug 33606 for the same manage_sysprefs problem in the ERM Vue app. Security/compatibility considerations: the endpoint returns only allow-listed, non-secret ILL configuration values needed by the ILL Vue app. It does not expose the generic system preferences API, and /api/v1/sysprefs remains restricted to parameters/manage_sysprefs. Regression test coverage: the new API test verifies that a staff user with the ill permission can read /ill/config, that a staff user without the ill permission cannot read it, and that the same ILL-only user still cannot access /api/v1/sysprefs. Test plan: 1. prove -I. -It/lib t/db_dependent/api/v1/ill_config.t 2. prove -I. -It/lib t/db_dependent/api/v1/ill_users.t Signed-off-by: David Nind Amended-by: Jonathan Druart chmod +x t/db_dependent/api/v1/ill_config.t Signed-off-by: Jonathan Druart Signed-off-by: Pedro Amorim --- Koha/REST/V1/ILL.pm | 21 ++++++ api/v1/swagger/definitions/ill_config.yaml | 7 ++ api/v1/swagger/paths/ill_config.yaml | 38 ++++++++++ api/v1/swagger/swagger.yaml | 7 ++ .../prog/js/fetch/ill-api-client.js | 9 +++ .../prog/js/vue/components/ILL/Main.vue | 33 ++++---- t/db_dependent/api/v1/ill_config.t | 75 +++++++++++++++++++ 7 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 api/v1/swagger/definitions/ill_config.yaml create mode 100644 api/v1/swagger/paths/ill_config.yaml create mode 100755 t/db_dependent/api/v1/ill_config.t diff --git a/Koha/REST/V1/ILL.pm b/Koha/REST/V1/ILL.pm index b3eb07e5f3..e563db43d9 100644 --- a/Koha/REST/V1/ILL.pm +++ b/Koha/REST/V1/ILL.pm @@ -19,6 +19,7 @@ use Modern::Perl; use Mojo::Base 'Mojolicious::Controller'; +use C4::Context; use Koha::Patrons; use Try::Tiny qw( catch try ); @@ -31,6 +32,26 @@ Koha::REST::V1::ILL =head2 Class methods +=head3 config + +Return the configuration options needed for the ILL Vue app + +=cut + +sub config { + my $c = shift->openapi->valid_input or return; + + return $c->render( + status => 200, + openapi => { + settings => { + ILLModule => C4::Context->preference('ILLModule'), + ILLPartnerCode => C4::Context->preference('ILLPartnerCode'), + }, + }, + ); +} + =head3 list_users Return the list of possible ILL users diff --git a/api/v1/swagger/definitions/ill_config.yaml b/api/v1/swagger/definitions/ill_config.yaml new file mode 100644 index 0000000000..9fbb6d30aa --- /dev/null +++ b/api/v1/swagger/definitions/ill_config.yaml @@ -0,0 +1,7 @@ +--- +type: object +properties: + settings: + type: object + description: List of sysprefs used for the ILL module +additionalProperties: false diff --git a/api/v1/swagger/paths/ill_config.yaml b/api/v1/swagger/paths/ill_config.yaml new file mode 100644 index 0000000000..9eee4d0297 --- /dev/null +++ b/api/v1/swagger/paths/ill_config.yaml @@ -0,0 +1,38 @@ +--- +/ill/config: + get: + x-mojo-to: ILL#config + operationId: getILLconfig + description: This resource returns a list of options needed for the ILL Vue app. EXPERIMENTAL - DO NOT RELY on this, it is subject to change! + summary: get the ILL config + tags: + - ill_config + produces: + - application/json + responses: + 200: + description: The ILL config + schema: + $ref: "../swagger.yaml#/definitions/ill_config" + 400: + description: Bad request + schema: + $ref: "../swagger.yaml#/definitions/error" + 403: + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + 500: + description: | + Internal server error. Possible `error_code` attribute values: + + * `internal_server_error` + schema: + $ref: "../swagger.yaml#/definitions/error" + 503: + description: Under maintenance + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + ill: 1 diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 39fac485bf..4c35664630 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -108,6 +108,8 @@ definitions: $ref: ./definitions/ill_backend.yaml ill_backends: $ref: ./definitions/ill_backends.yaml + ill_config: + $ref: ./definitions/ill_config.yaml ill_status: $ref: ./definitions/ill_status.yaml ill_request: @@ -471,6 +473,8 @@ paths: $ref: "./paths/holds.yaml#/~1holds~1{hold_id}~1suspension" "/holds/{hold_id}/lowest_priority": $ref: "./paths/holds.yaml#/~1holds~1{hold_id}~1lowest_priority" + /ill/config: + $ref: ./paths/ill_config.yaml#/~1ill~1config /ill/backends: $ref: ./paths/ill_backends.yaml#/~1ill~1backends "/ill/backends/{ill_backend_id}": @@ -1403,6 +1407,9 @@ tags: - description: "Manage ILL module batch statuses\n" name: ill_batchstatuses x-displayName: ILL batch statuses + - description: "Manage ILL configuration\n" + name: ill_config + x-displayName: ILL configuration - description: "Manage ILL requests\n" name: ill_requests x-displayName: ILL requests diff --git a/koha-tmpl/intranet-tmpl/prog/js/fetch/ill-api-client.js b/koha-tmpl/intranet-tmpl/prog/js/fetch/ill-api-client.js index c83f0b7e96..973c8b5f45 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/fetch/ill-api-client.js +++ b/koha-tmpl/intranet-tmpl/prog/js/fetch/ill-api-client.js @@ -5,6 +5,15 @@ export class ILLAPIClient { }); } + get config() { + return { + get: () => + this.httpClient.get({ + endpoint: "config", + }), + }; + } + get supplying() { return { get: id => diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ILL/Main.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ILL/Main.vue index 77c80939b3..e3a69845db 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ILL/Main.vue +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ILL/Main.vue @@ -49,26 +49,21 @@ export default { onBeforeMount(() => { loading(); - const client = APIClient.sysprefs; - client.sysprefs - .getAll(["ILLPartnerCode", "ILLModule"]) - .then(pref_values => { - config.value.settings.ILLPartnerCode = - pref_values.value.ILLPartnerCode; - config.value.settings.ILLModule = - pref_values.value.ILLModule; - if (config.value.settings.ILLModule != 1) { - loaded(); - return setError( - $__( - "The ILL module is disabled, turn on ILLModule to use it" - ), - false - ); - } + const client = APIClient.ill; + client.config.get().then(result => { + config.value = result; + if (config.value.settings.ILLModule != 1) { loaded(); - }); - initialized.value = true; + return setError( + $__( + "The ILL module is disabled, turn on ILLModule to use it" + ), + false + ); + } + loaded(); + initialized.value = true; + }); }); return { diff --git a/t/db_dependent/api/v1/ill_config.t b/t/db_dependent/api/v1/ill_config.t new file mode 100755 index 0000000000..e308fdc40a --- /dev/null +++ b/t/db_dependent/api/v1/ill_config.t @@ -0,0 +1,75 @@ +#!/usr/bin/env 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; + +use Test::NoWarnings; +use Test::More tests => 2; +use Test::Mojo; + +use t::lib::Mocks; +use t::lib::TestBuilder; + +use Koha::Database; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +my $t = Test::Mojo->new('Koha::REST::V1'); +t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); + +subtest 'config() tests' => sub { + + plan tests => 8; + + $schema->storage->txn_begin; + + t::lib::Mocks::mock_preference( 'ILLModule', 1 ); + t::lib::Mocks::mock_preference( 'ILLPartnerCode', 'IL' ); + + my $librarian = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 2**22 } # 22 => ill + } + ); + + my $password = 'thePassword123'; + $librarian->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $librarian->userid; + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } + ); + + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $unauth_userid = $patron->userid; + + $t->get_ok("//$userid:$password@/api/v1/ill/config") + ->status_is(200) + ->json_is( '/settings/ILLModule' => '1' ) + ->json_is( '/settings/ILLPartnerCode' => 'IL' ); + + $t->get_ok("//$userid:$password@/api/v1/sysprefs")->status_is(403); + + $t->get_ok("//$unauth_userid:$password@/api/v1/ill/config")->status_is(403); + + $schema->storage->txn_rollback; +};