From fb5565a36a9faffc2e37b5a696a3de596a144086 Mon Sep 17 00:00:00 2001 From: Jacob O'Mara Date: Tue, 4 Nov 2025 15:22:17 +0000 Subject: [PATCH] Bug 39224: add REST API endpoints for shibboleth configuration Add API endpoints for managing Shibboleth configuration: - GET/PUT /api/v1/shibboleth/config - GET/POST /api/v1/shibboleth/mappings - GET/PUT/DELETE /api/v1/shibboleth/mappings/{mapping_id} --- Koha/REST/V1/ShibbolethConfig.pm | 67 ++++++ Koha/REST/V1/ShibbolethFieldMappings.pm | 150 ++++++++++++ .../definitions/shibboleth_config.yaml | 23 ++ .../definitions/shibboleth_mapping.yaml | 26 +++ api/v1/swagger/paths/shibboleth.yaml | 221 ++++++++++++++++++ api/v1/swagger/swagger.yaml | 19 ++ 6 files changed, 506 insertions(+) create mode 100644 Koha/REST/V1/ShibbolethConfig.pm create mode 100644 Koha/REST/V1/ShibbolethFieldMappings.pm create mode 100644 api/v1/swagger/definitions/shibboleth_config.yaml create mode 100644 api/v1/swagger/definitions/shibboleth_mapping.yaml create mode 100644 api/v1/swagger/paths/shibboleth.yaml diff --git a/Koha/REST/V1/ShibbolethConfig.pm b/Koha/REST/V1/ShibbolethConfig.pm new file mode 100644 index 00000000000..ff0738b8fce --- /dev/null +++ b/Koha/REST/V1/ShibbolethConfig.pm @@ -0,0 +1,67 @@ +package Koha::REST::V1::ShibbolethConfig; + +# 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::Controller'; + +use Try::Tiny qw(catch try); + +use Koha::ShibbolethConfig; +use Koha::ShibbolethConfigs; + +=head1 API + +=head2 Methods + +=head3 get + +Get the Shibboleth configuration + +=cut + +sub get { + my $c = shift->openapi->valid_input or return; + + return try { + my $config = Koha::ShibbolethConfigs->new->get_configuration; + return $c->render( status => 200, openapi => $c->objects->to_api($config) ); + } catch { + $c->unhandled_exception($_); + }; +} + +=head3 update + +Update Shibboleth configuration + +=cut + +sub update { + my $c = shift->openapi->valid_input or return; + + return try { + my $config = Koha::ShibbolethConfigs->new->get_configuration; + $config->set_from_api( $c->req->json )->store; + + return $c->render( status => 200, openapi => $c->objects->to_api($config) ); + } catch { + $c->unhandled_exception($_); + }; +} + +1; diff --git a/Koha/REST/V1/ShibbolethFieldMappings.pm b/Koha/REST/V1/ShibbolethFieldMappings.pm new file mode 100644 index 00000000000..a91aa12eab1 --- /dev/null +++ b/Koha/REST/V1/ShibbolethFieldMappings.pm @@ -0,0 +1,150 @@ +package Koha::REST::V1::ShibbolethFieldMappings; + +# 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::Controller'; + +use Try::Tiny qw(catch try); +use Scalar::Util qw(blessed); + +use Koha::ShibbolethFieldMapping; +use Koha::ShibbolethFieldMappings; + +=head1 API + +=head2 Methods + +=head3 list + +List all field mappings + +=cut + +sub list { + my $c = shift->openapi->valid_input or return; + + return try { + my $mappings = $c->objects->search( Koha::ShibbolethFieldMappings->new ); + return $c->render( status => 200, openapi => $mappings ); + } catch { + $c->unhandled_exception($_); + }; +} + +=head3 get + +Get a specific mapping + +=cut + +sub get { + my $c = shift->openapi->valid_input or return; + + return try { + my $mapping = Koha::ShibbolethFieldMappings->find( $c->param('mapping_id') ); + + return $c->render_resource_not_found('Mapping') unless $mapping; + + return $c->render( status => 200, openapi => $c->objects->to_api($mapping) ); + } catch { + $c->unhandled_exception($_); + }; +} + +=head3 add + +Add a new field mapping + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + return try { + my $mapping = Koha::ShibbolethFieldMapping->new_from_api( $c->req->json ); + $mapping->store; + $c->res->headers->location( $c->req->url->to_string . '/' . $mapping->mapping_id ); + + return $c->render( + status => 201, + openapi => $c->objects->to_api($mapping) + ); + } catch { + if ( blessed $_ && $_->isa('Koha::Exceptions::MissingParameter') ) { + return $c->render( + status => 400, + openapi => { error => $_->error } + ); + } + $c->unhandled_exception($_); + }; +} + +=head3 update + +Update an existing mapping + +=cut + +sub update { + my $c = shift->openapi->valid_input or return; + + my $mapping = Koha::ShibbolethFieldMappings->find( $c->param('mapping_id') ); + + return $c->render_resource_not_found('Mapping') unless $mapping; + + return try { + $mapping->set_from_api( $c->req->json )->store; + + return $c->render( + status => 200, + openapi => $c->objects->to_api($mapping) + ); + } catch { + if ( blessed $_ && $_->isa('Koha::Exceptions::MissingParameter') ) { + return $c->render( + status => 400, + openapi => { error => $_->error } + ); + } + $c->unhandled_exception($_); + }; +} + +=head3 delete + +Delete a mapping + +=cut + +sub delete { + my $c = shift->openapi->valid_input or return; + + my $mapping = Koha::ShibbolethFieldMappings->find( $c->param('mapping_id') ); + + return $c->render_resource_not_found('Mapping') unless $mapping; + + return try { + $mapping->delete; + return $c->render_resource_deleted; + } catch { + $c->unhandled_exception($_); + }; +} + +1; diff --git a/api/v1/swagger/definitions/shibboleth_config.yaml b/api/v1/swagger/definitions/shibboleth_config.yaml new file mode 100644 index 00000000000..0f3c0b11b53 --- /dev/null +++ b/api/v1/swagger/definitions/shibboleth_config.yaml @@ -0,0 +1,23 @@ +--- +type: object +properties: + shibboleth_config_id: + type: integer + description: Internal configuration identifier + readOnly: true + force_opac_sso: + type: boolean + description: Enable SSO for OPAC + force_staff_sso: + type: boolean + description: Enable SSO for staff interface + autocreate: + type: boolean + description: Auto create users + sync: + type: boolean + description: Sync user attributes + welcome: + type: boolean + description: Send welcome email +additionalProperties: false diff --git a/api/v1/swagger/definitions/shibboleth_mapping.yaml b/api/v1/swagger/definitions/shibboleth_mapping.yaml new file mode 100644 index 00000000000..7110125db58 --- /dev/null +++ b/api/v1/swagger/definitions/shibboleth_mapping.yaml @@ -0,0 +1,26 @@ +--- +type: object +properties: + mapping_id: + type: integer + description: Internal mapping identifier + readOnly: true + koha_field: + type: string + description: Koha database field + idp_field: + type: + - string + - "null" + description: IdP attribute name + default_content: + type: + - string + - "null" + description: Default value if attribute missing + is_matchpoint: + type: boolean + description: Is this the matchpoint field +additionalProperties: false +required: + - koha_field diff --git a/api/v1/swagger/paths/shibboleth.yaml b/api/v1/swagger/paths/shibboleth.yaml new file mode 100644 index 00000000000..7cca7dcab7b --- /dev/null +++ b/api/v1/swagger/paths/shibboleth.yaml @@ -0,0 +1,221 @@ +--- +/shibboleth/config: + get: + x-mojo-to: ShibbolethConfig#get + operationId: getShibbolethConfig + tags: + - shibboleth + summary: Get Shibboleth configuration + produces: + - application/json + responses: + "200": + description: Shibboleth configuration + schema: + $ref: "../swagger.yaml#/definitions/shibboleth_config" + "400": + description: | + Bad request. Possible `error_code` attribute values: + + * `invalid_query` + schema: + $ref: "../swagger.yaml#/definitions/error" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + parameters: manage_identity_providers + put: + x-mojo-to: ShibbolethConfig#update + operationId: updateShibbolethConfig + tags: + - shibboleth + summary: Update Shibboleth configuration + parameters: + - name: body + in: body + description: Shibboleth configuration object + required: true + schema: + $ref: "../swagger.yaml#/definitions/shibboleth_config" + produces: + - application/json + responses: + "200": + description: Configuration updated + schema: + $ref: "../swagger.yaml#/definitions/shibboleth_config" + "400": + description: Bad request + schema: + $ref: "../swagger.yaml#/definitions/error" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + parameters: manage_identity_providers + +/shibboleth/mappings: + get: + x-mojo-to: ShibbolethFieldMappings#list + operationId: listShibbolethMappings + tags: + - shibboleth + summary: List Shibboleth field mappings + parameters: + - $ref: "../swagger.yaml#/parameters/match" + - $ref: "../swagger.yaml#/parameters/order_by" + - $ref: "../swagger.yaml#/parameters/page" + - $ref: "../swagger.yaml#/parameters/per_page" + - $ref: "../swagger.yaml#/parameters/q_param" + - $ref: "../swagger.yaml#/parameters/q_body" + produces: + - application/json + responses: + "200": + description: List of field mappings + schema: + type: array + items: + $ref: "../swagger.yaml#/definitions/shibboleth_mapping" + "400": + description: | + Bad request. Possible `error_code` attribute values: + + * `invalid_query` + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + parameters: manage_identity_providers + post: + x-mojo-to: ShibbolethFieldMappings#add + operationId: addShibbolethMapping + tags: + - shibboleth + summary: Add new field mapping + parameters: + - name: body + in: body + description: Field mapping object + required: true + schema: + $ref: "../swagger.yaml#/definitions/shibboleth_mapping" + produces: + - application/json + responses: + "201": + description: Mapping created + schema: + $ref: "../swagger.yaml#/definitions/shibboleth_mapping" + "400": + description: Bad request + schema: + $ref: "../swagger.yaml#/definitions/error" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + parameters: manage_identity_providers + +"/shibboleth/mappings/{mapping_id}": + get: + x-mojo-to: ShibbolethFieldMappings#get + operationId: getShibbolethMapping + tags: + - shibboleth + summary: Get field mapping + parameters: + - $ref: "../swagger.yaml#/parameters/mapping_id_pp" + produces: + - application/json + responses: + "200": + description: Field mapping + schema: + $ref: "../swagger.yaml#/definitions/shibboleth_mapping" + "400": + description: Bad request + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: Mapping not found + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + parameters: manage_identity_providers + put: + x-mojo-to: ShibbolethFieldMappings#update + operationId: updateShibbolethMapping + tags: + - shibboleth + summary: Update field mapping + parameters: + - $ref: "../swagger.yaml#/parameters/mapping_id_pp" + - name: body + in: body + description: Updated mapping object + required: true + schema: + $ref: "../swagger.yaml#/definitions/shibboleth_mapping" + produces: + - application/json + responses: + "200": + description: Mapping updated + schema: + $ref: "../swagger.yaml#/definitions/shibboleth_mapping" + "400": + description: Bad request + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: Mapping not found + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + parameters: manage_identity_providers + delete: + x-mojo-to: ShibbolethFieldMappings#delete + operationId: deleteShibbolethMapping + tags: + - shibboleth + summary: Delete field mapping + parameters: + - $ref: "../swagger.yaml#/parameters/mapping_id_pp" + produces: + - application/json + responses: + "204": + description: Mapping deleted + "400": + description: Bad request + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: Mapping not found + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + parameters: manage_identity_providers diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 14e41bd327f..3ba8de90a0d 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -180,6 +180,10 @@ definitions: $ref: ./definitions/rota.yaml search_filter: $ref: ./definitions/search_filter.yaml + shibboleth_config: + $ref: ./definitions/shibboleth_config.yaml + shibboleth_mapping: + $ref: ./definitions/shibboleth_mapping.yaml smtp_server: $ref: ./definitions/smtp_server.yaml file_transport: @@ -591,6 +595,12 @@ paths: $ref: "./paths/rotas.yaml#/~1rotas~1{rota_id}" "/rotas/{rota_id}/stages/{stage_id}/position": $ref: "./paths/rotas.yaml#/~1rotas~1{rota_id}~1stages~1{stage_id}~1position" + /shibboleth/config: + $ref: ./paths/shibboleth.yaml#/~1shibboleth~1config + /shibboleth/mappings: + $ref: ./paths/shibboleth.yaml#/~1shibboleth~1mappings + "/shibboleth/mappings/{mapping_id}": + $ref: "./paths/shibboleth.yaml#/~1shibboleth~1mappings~1{mapping_id}" /suggestions: $ref: ./paths/suggestions.yaml#/~1suggestions "/suggestions/{suggestion_id}": @@ -858,6 +868,12 @@ parameters: name: license_id required: true type: integer + mapping_id_pp: + description: Shibboleth field mapping internal identifier + in: path + name: mapping_id + required: true + type: integer match: description: Matching criteria enum: @@ -1345,6 +1361,9 @@ tags: - description: "Manage file transport configurations\n" name: file_transports x-displayName: File transports + - description: "Manage Shibboleth authentication\n" + name: shibboleth + x-displayName: Shibboleth - description: "Manage tickets\n" name: tickets x-displayName: Tickets -- 2.39.5