From 155aca1758af4824668034860b379459b6906a3e Mon Sep 17 00:00:00 2001 From: Paul Derscheid Date: Tue, 19 Aug 2025 10:30:48 +0000 Subject: [PATCH] Bug 37025: (follow-up) Use consistent option handling and convert to Perl - Convert from bash to Perl for less headaches regarding quoting - Replace positional arguments with --generate and --delete flags - Add --description, --borrowernumber, and --client-id options - Add POD following Koha standards - Update DocBook XML documentation with new flag-based interface - Add audit logging for all API key operations via C4::Log - Document proper usage via koha-shell wrapper for administrative access Test plan: NOTE: As this tool requires Koha environment access and is installed to /usr/sbin/, it must be run via koha-shell when using sudo: 1. Test help output: sudo koha-shell -c "koha-api-keys --help" instancename 2. Test API key generation: sudo koha-shell -c "koha-api-keys --generate --description 'Test key' --borrowernumber 51" instancename => Should output Client ID and Client Secret in tab-separated format 3. Test interactive deletion (use echo to provide input): echo "CLIENT_ID_FROM_STEP_2" | sudo koha-shell -c "koha-api-keys --delete" instancename => Should delete successfully 4. Test scripted deletion: sudo koha-shell -c "koha-api-keys --delete --client-id CLIENT_ID_FROM_STEP_2" instancename => Should delete without prompting 5. Test error handling: sudo koha-shell -c "koha-api-keys --generate --description test" instancename => Should show error about missing --borrowernumber 6. Test invalid data: sudo koha-shell -c "koha-api-keys --generate --description test --borrowernumber 99999" instancename => Should show error about unknown borrowernumber 7. Verify audit logging: After steps 2 and 4, check action_logs table for APIKEY entries: sudo koha-mysql instancename -e "SELECT timestamp, module, action, object, info FROM action_logs WHERE module='APIKEY' ORDER BY timestamp DESC LIMIT 5;" => Should show logged generation and deletion operations 8. Verify DocBook XML validation: prove -v xt/verify-debian-docbook.t 9. Generate and verify man page: xsltproc /usr/share/xml/docbook/stylesheet/docbook-xsl-ns/manpages/docbook.xsl debian/docs/koha-api-keys.xml => Should generate koha-api-keys.8 without errors --- debian/docs/koha-api-keys.xml | 66 +++++--- debian/scripts/koha-api-keys | 279 +++++++++++++++++++++++++--------- 2 files changed, 246 insertions(+), 99 deletions(-) diff --git a/debian/docs/koha-api-keys.xml b/debian/docs/koha-api-keys.xml index 741dfbf8764..62bb2458f2c 100644 --- a/debian/docs/koha-api-keys.xml +++ b/debian/docs/koha-api-keys.xml @@ -29,11 +29,19 @@ - koha-api-keys - generate | delete - description - borrowernumber - instancename + sudo koha-shell + "koha-api-keys description borrowernumber" + instancename + + + sudo koha-shell + "koha-api-keys [ client_id]" + instancename + + + sudo koha-shell + "koha-api-keys " + instancename @@ -42,46 +50,48 @@ The koha-api-keys script allows administrators to generate or delete API keys associated with specific Koha patrons. These API keys can be used for authorized programmatic access to the Koha system. - - - - Description - The koha-api-keys script allows administrators to generate or delete API keys associated with specific Koha patrons. These API keys can be used for authorized programmatic access to the Koha system. + As this tool requires administrative privileges and access to the Koha environment, it must be run using sudo and the koha-shell wrapper as shown in the synopsis. - Arguments + Options - generate + - Generates a new API key for the specified patron. + Generate a new API key for the specified patron. - delete + - Deletes an existing API key based on its Client ID. + Delete an existing API key (client ID will be prompted if not provided via --client-id). - description + - A description for the API key. + Description for the API key (required for --generate). - borrowernumber + - The borrower number of the patron to associate the API key with. + Patron's borrower number (required for --generate). - instancename + - The name of the Koha instance to operate on. + Client ID to delete (optional for --delete, will be prompted if not provided). + + + + + + Display help message. @@ -89,11 +99,17 @@ Examples - Generate a new API key for patron '12345' with the description 'Patron Access': - $ koha-api-keys generate 'Patron Access' 12345 kohadev + Generate a new API key for patron '123' with the description 'Test API access': + $ sudo koha-shell -c "koha-api-keys --generate --description 'Test API access' --borrowernumber 123" instancename + + Delete an existing API key (interactive): + $ echo "CLIENT_ID" | sudo koha-shell -c "koha-api-keys --delete" instancename + + Delete an existing API key (scripted): + $ sudo koha-shell -c "koha-api-keys --delete --client-id a1b2c3d4-e5f6-7890-abcd-ef1234567890" instancename - Delete an existing API key with Client ID 'abc123def456': - $ koha-api-keys delete abc123def456 kohadev + Display help information: + $ sudo koha-shell -c "koha-api-keys --help" instancename diff --git a/debian/scripts/koha-api-keys b/debian/scripts/koha-api-keys index 79878e84507..53d3234c4a0 100755 --- a/debian/scripts/koha-api-keys +++ b/debian/scripts/koha-api-keys @@ -1,5 +1,5 @@ -#!/bin/bash -# +#!/usr/bin/perl + # koha-api-keys: generate or delete api keys for a given patron and # koha instance. # Copyright 2024 LMSCloud GmbH @@ -17,81 +17,212 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set -e -umask 0077 +use Modern::Perl; +use Getopt::Long qw( GetOptions ); +use Pod::Usage qw( pod2usage ); + +use Koha::Script; +use Koha::ApiKeys; +use Koha::Patrons; +use C4::Log qw( logaction ); + +my $generate; +my $delete; +my $help; +my $description; +my $borrowernumber; +my $client_id; + +GetOptions( + 'generate' => \$generate, + 'delete' => \$delete, + 'help|h' => \$help, + 'description=s' => \$description, + 'borrowernumber=i' => \$borrowernumber, + 'client-id=s' => \$client_id, +) or pod2usage(2); + +if ($help) { + pod2usage(1); +} -# include helper functions -if [ -f "/usr/share/koha/bin/koha-functions.sh" ]; then - . "/usr/share/koha/bin/koha-functions.sh" -else - echo "Error: /usr/share/koha/bin/koha-functions.sh not present." 1>&2 - exit 1 -fi +if ( !$generate && !$delete ) { + pod2usage( + { + -message => "Error: No operation specified. Use --generate or --delete.", + -exitval => 1 + } + ); +} -usage() { - local scriptname=$0 - cat < "Error: Cannot specify both --generate and --delete.", + -exitval => 1 + } + ); +} + +if ($generate) { + if ( !$description ) { + pod2usage( + { + -message => "Error: --generate requires --description.", + -exitval => 1 + } + ); + } + if ( !$borrowernumber ) { + pod2usage( + { + -message => "Error: --generate requires --borrowernumber.", + -exitval => 1 + } + ); + } + + my $patron = Koha::Patrons->find($borrowernumber); + if ( !$patron ) { + die "Error: Unknown borrowernumber: '$borrowernumber'\n"; + } + + my $api_key = Koha::ApiKey->new( + { + patron_id => $borrowernumber, + description => $description, + } + )->store; + + # Log the API key generation for audit purposes + logaction( + 'APIKEY', 'GENERATE', $borrowernumber, + "Generated API key for patron $borrowernumber with description '$description'" + ); + + printf "Client ID:\t%s\nClient Secret:\t%s\n", $api_key->client_id, $api_key->plain_text_secret; + exit 0; +} -Usage: $scriptname generate|delete [ description ] [ borrowernumber ] [ instancename ] +if ($delete) { + if ( !$client_id ) { + print "Client ID to delete: "; + $client_id = ; + chomp $client_id; + } -EOF + if ( !$client_id ) { + die "Error: No client ID provided.\n"; + } + + my $api_key = Koha::ApiKeys->find($client_id); + if ( !$api_key ) { + die "Error: Unknown client ID: '$client_id'\n"; + } + + # Store patron_id for logging before deletion + my $patron_id = $api_key->patron_id; + + $api_key->delete; + + # Log the API key deletion for audit purposes + logaction( + 'APIKEY', 'DELETE', $patron_id, + "Deleted API key $client_id for patron $patron_id" + ); + + print "API key deleted successfully.\n"; + exit 0; } -op="$1" - -[ "$#" -ge 2 ] || ( - usage - die 'Missing required arguments.' -) - -if [ "$op" == 'generate' ]; then - if [ "$#" -eq 3 ]; then - description=$(openssl rand -hex 16) - borrowernumber="$2" - instance="$3" - fi - - if [ "$#" -eq 4 ]; then - description="$2" - borrowernumber="$3" - instance="$4" - fi - - is_instance "$instance" || ( - usage - die "Unknown instance '$instance'" - ) - if ! koha-shell -c "perl -MKoha::Patrons -e \"Koha::Patrons->find($borrowernumber) or die;\"" "$instance" 2>/dev/null; then - die "Unknown borrowernumber: '$borrowernumber'" - fi - - { - read -r client_id - read -r secret - } < <( - koha-shell -c "perl -MKoha::ApiKeys -e \"my \\\$key = Koha::ApiKey->new( { patron_id => $borrowernumber, description => '$description' } )->store; printf \\\"%s\\n%s\\n\\\", \\\$key->client_id, \\\$key->plain_text_secret;\"" "$instance" - ) - - printf "Client ID:\t%s\nClient Secret:\t%s\n" "$client_id" "$secret" - - exit 0 -fi - -if [ "$op" == 'delete' ]; then - instance="$2" - is_instance "$instance" || ( - usage - die "Unknown instance '$instance'" - ) - - read -rp "Client ID to delete: " client_id - if ! koha-shell -c "perl -MKoha::ApiKeys -e \"Koha::ApiKeys->find(\\\"$client_id\\\")->delete or die;\"" "$instance" 2>/dev/null; then - die "Unknown client id: '$client_id'" - fi - - exit 0 -fi - -usage -die "Invalid operation: '$op'" +__END__ + +=head1 NAME + +koha-api-keys - Generate or delete API keys for specified patrons + +=head1 SYNOPSIS + +sudo koha-shell -c "koha-api-keys --generate --description DESCRIPTION --borrowernumber BORROWERNUMBER" instancename + +sudo koha-shell -c "koha-api-keys --delete [--client-id CLIENT_ID]" instancename + +sudo koha-shell -c "koha-api-keys --help" instancename + +=head1 OPTIONS + +=over 8 + +=item B<--generate> + +Generate a new API key for the specified patron. + +=item B<--delete> + +Delete an API key (client ID will be prompted if not provided via --client-id). + +=item B<--description DESCRIPTION> + +Description for the API key (required for --generate). + +=item B<--borrowernumber BORROWERNUMBER> + +Patron's borrower number (required for --generate). + +=item B<--client-id CLIENT_ID> + +Client ID to delete (optional for --delete, will be prompted if not provided). + +=item B<--help> + +Display this help message. + +=back + +=head1 DESCRIPTION + +This script lets you generate or delete API keys for specified patrons in Koha. + +As this tool requires administrative privileges and access to the Koha environment, +it must be run using sudo and koha-shell wrapper as shown in the synopsis. + +For generating keys, you must provide both a description and a borrower number. +For deleting keys, you can provide the client ID via --client-id or be prompted interactively. + +=head1 EXAMPLES + +Generate an API key: + sudo koha-shell -c "koha-api-keys --generate --description 'Test API access' --borrowernumber 123" instancename + +Delete an API key (interactive): + echo "CLIENT_ID" | sudo koha-shell -c "koha-api-keys --delete" instancename + +Delete an API key (scripted): + sudo koha-shell -c "koha-api-keys --delete --client-id a1b2c3d4-e5f6-7890-abcd-ef1234567890" instancename + +=head1 AUTHOR + +Koha Development Team + +=head1 COPYRIGHT + +Copyright LMSCloud GmbH + +=head1 LICENSE + +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 . + +=cut -- 2.39.5