From c94c3a21edc2cebcd7ea632b10060891ce871afc Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Mon, 23 Mar 2015 20:14:23 +0100 Subject: [PATCH] Bug 13920: 7. API Authentication, part 1: API keys management in interface Depends on Buugg 14539. This introduces the concept of API keys for use in the new REST API. A key is a string of 32 alphanumerical characters (32 is purely arbitrary, it can be changed easily). A user can have multiple keys (unlimited at the moment) Keys can be generated automatically, and then we have the possibility to delete or revoke each one individually. ApiKeys can be easily accessed using the Koha::ApiKeys-package. Test plan: 1/ Go to staff interface 2/ Go to a borrower page 3/ In toolbar, click on More -> Manage API keys 4/ Click on "Generate new key" multiple times, check that they are correctly displayed under the button, and they are active by default 5/ Revoke some keys, check that they are not active anymore 6/ Delete some keys, check that they disappear from table 7/ Go to opac interface, log in 8/ In your user account pages, you now have a new tab to the left "your API keys". Click on it. 9/ Repeat steps 4-6 --- Koha/ApiKey.pm | 46 +++++++ Koha/ApiKeys.pm | 148 +++++++++++++++++++++ Koha/Schema/Result/ApiKey.pm | 99 ++++++++++++++ installer/data/mysql/kohastructure.sql | 17 +++ installer/data/mysql/updatedatabase.pl | 67 ++++------ .../prog/en/includes/members-toolbar.inc | 5 + .../prog/en/modules/members/apikeys.tt | 78 +++++++++++ .../opac-tmpl/bootstrap/en/includes/usermenu.inc | 8 ++ .../opac-tmpl/bootstrap/en/modules/opac-apikeys.tt | 82 ++++++++++++ members/apikeys.pl | 82 ++++++++++++ opac/opac-apikeys.pl | 97 ++++++++++++++ 11 files changed, 686 insertions(+), 43 deletions(-) create mode 100644 Koha/ApiKey.pm create mode 100644 Koha/ApiKeys.pm create mode 100644 Koha/Schema/Result/ApiKey.pm create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/apikeys.tt create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-apikeys.tt create mode 100755 members/apikeys.pl create mode 100755 opac/opac-apikeys.pl diff --git a/Koha/ApiKey.pm b/Koha/ApiKey.pm new file mode 100644 index 0000000..be4b76e --- /dev/null +++ b/Koha/ApiKey.pm @@ -0,0 +1,46 @@ +package Koha::ApiKey; + +# Copyright BibLibre 2015 +# +# 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Carp; + +use Koha::Database; + +use base qw(Koha::Object); + +=head1 NAME + +Koha::ApiKey - Koha API Key Object class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub type { + return 'ApiKey'; +} + +1; diff --git a/Koha/ApiKeys.pm b/Koha/ApiKeys.pm new file mode 100644 index 0000000..b9d9838 --- /dev/null +++ b/Koha/ApiKeys.pm @@ -0,0 +1,148 @@ +package Koha::ApiKeys; + +# Copyright BibLibre 2015 +# +# 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; +use Scalar::Util qw(blessed); + +use Koha::Borrowers; + +use base qw(Koha::Objects); + +use Koha::Exception::BadParameter; +use Koha::Exception::UnknownObject; + +=head1 NAME + +Koha::ApiKeys - Koha API Keys Object class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub type { + return 'ApiKey'; +} + +sub object_class { + return 'Koha::ApiKey'; +} + +=head cast + + my $borrower = Koha::ApiKeys->cast("02132ofnsajdvbi24jjabac9a2g36l32"); + my $borrower = Koha::ApiKeys->cast($Koha::ApiKey); + my $borrower = Koha::ApiKeys->cast($Koha::Schema::Result::ApiKey); + +@PARAM1 Scalar, or object. +@RETURNS Koha::ApiKey, possibly already in DB or a completely new one if nothing was + inferred from the DB. +@THROWS Koha::Exception::BadParameter, if no idea what to do with the input. +@THROWS Koha::Exception::UnknownObject, if we cannot find a Borrower with the given input. +=cut + +sub cast { + my ($self, $input) = @_; + + unless ($input) { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."::cast():> No parameter given!"); + } + if (blessed($input) && $input->isa('Koha::ApiKey')) { + return $input; + } + if (blessed($input) && $input->isa('Koha::Schema::Result::ApiKey')) { + return Koha::ApiKey->_new_from_dbic($input); + } + + my $apiKey = Koha::ApiKeys->find({api_key => $input}); + + unless ($apiKey) { + Koha::Exception::UnknownObject->throw(error => "Koha::ApiKeys->cast():> Cannot find an existing ApiKey from api_key '$input'."); + } + + return $apiKey; +} + +=head grant + + my $apiKey = Koha::ApiKey->grant({borrower => $borrower, + apiKey => $apiKey + }); + +Granting an ApiKey should be easy. This creates a new ApiKey for the given Borrower, +or sets the owner of an existing key. +$PARAM1 HASHRef of params, { + borrower => MANDATORY, a Koha::Borrower or something castable to one. + apiKey => OPTIONAL, an existing Koha::ApiKEy to give to somebody else. + not sure why anybody would want to do that, but + provided as a convenience for testing. +} +@THROWS Koha::Exception::BadParameter +=cut + +sub grant { + my ($self, $borrower, $apiKey) = @_; + $borrower = Koha::Borrowers::castToBorrower($borrower); + if ($apiKey) { + $apiKey = Koha::ApiKeys->cast($apiKey); + $apiKey->borrowernumber($borrower->borrowernumber); + } + else { + $apiKey = new Koha::ApiKey; + $apiKey->borrowernumber($borrower->borrowernumber); + $apiKey->api_key(String::Random->new->randregex('[a-zA-Z0-9]{32}')); + } + $apiKey->store; +} + +sub delete { + my ($self, $apiKey) = @_; + $apiKey = Koha::ApiKeys->cast($apiKey); + + if ($apiKey) { + $apiKey->delete; + } +} + +sub revoke { + my ($self, $apiKey) = @_; + $apiKey = Koha::ApiKeys->cast($apiKey); + + if ($apiKey) { + $apiKey->active(0); + $apiKey->store; + } +} + +sub activate { + my ($self, $apiKey) = @_; + $apiKey = Koha::ApiKeys->cast($apiKey); + + if ($apiKey) { + $apiKey->active(1); + $apiKey->store; + } +} + +1; diff --git a/Koha/Schema/Result/ApiKey.pm b/Koha/Schema/Result/ApiKey.pm new file mode 100644 index 0000000..67c1727 --- /dev/null +++ b/Koha/Schema/Result/ApiKey.pm @@ -0,0 +1,99 @@ +use utf8; +package Koha::Schema::Result::ApiKey; + +# Created by DBIx::Class::Schema::Loader +# DO NOT MODIFY THE FIRST PART OF THIS FILE + +=head1 NAME + +Koha::Schema::Result::ApiKey + +=cut + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +=head1 TABLE: C + +=cut + +__PACKAGE__->table("api_keys"); + +=head1 ACCESSORS + +=head2 borrowernumber + + data_type: 'integer' + is_foreign_key: 1 + is_nullable: 0 + +=head2 api_key + + data_type: 'varchar' + is_nullable: 0 + size: 255 + +=head2 last_request_time + + data_type: 'integer' + is_nullable: 1 + +=head2 active + + data_type: 'integer' + default_value: 1 + is_nullable: 1 + +=cut + +__PACKAGE__->add_columns( + "borrowernumber", + { data_type => "integer", is_foreign_key => 1, is_nullable => 0 }, + "api_key", + { data_type => "varchar", is_nullable => 0, size => 255 }, + "last_request_time", + { data_type => "integer", is_nullable => 1 }, + "active", + { data_type => "integer", default_value => 1, is_nullable => 1 }, +); + +=head1 PRIMARY KEY + +=over 4 + +=item * L + +=item * L + +=back + +=cut + +__PACKAGE__->set_primary_key("borrowernumber", "api_key"); + +=head1 RELATIONS + +=head2 borrowernumber + +Type: belongs_to + +Related object: L + +=cut + +__PACKAGE__->belongs_to( + "borrowernumber", + "Koha::Schema::Result::Borrower", + { borrowernumber => "borrowernumber" }, + { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, +); + + +# Created by DBIx::Class::Schema::Loader v0.07039 @ 2015-07-24 12:05:49 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:GkDFK7kq8kqzyR+Kej/DRQ + + +# You can replace this text with custom code or comments, and it will be preserved on regeneration +1; diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index a1c7510..4ecbce8 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -16,6 +16,23 @@ /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- +-- Table structure for table api_keys +-- + +DROP TABLE IF EXISTS api_keys; +CREATE TABLE api_keys ( + borrowernumber INT(11) NOT NULL, -- foreign key to the borrowers table + api_key VARCHAR(255) NOT NULL, -- API key used for API authentication + last_request_time INT(11) default NULL, -- UNIX timestamp of when was the last transaction for this API-key? Used for request replay control. + active INT(1) DEFAULT 1, -- 0 means this API key is revoked + PRIMARY KEY (borrowernumber, api_key), + CONSTRAINT api_keys_fk_borrowernumber + FOREIGN KEY (borrowernumber) + REFERENCES borrowers (borrowernumber) + ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- -- Table structure for table `auth_header` -- diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl index 7f0c585..064d16a 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -10695,49 +10695,6 @@ if ( CheckVersion($DBversion) ) { SetVersion ($DBversion); } -$DBversion = "XXX"; -if(CheckVersion($DBversion)) { - $dbh->do(q{ - DROP TABLE IF EXISTS api_keys; - }); - $dbh->do(q{ - CREATE TABLE api_keys ( - borrowernumber int(11) NOT NULL, - api_key VARCHAR(255) NOT NULL, - active int(1) DEFAULT 1, - PRIMARY KEY (borrowernumber, api_key), - CONSTRAINT api_keys_fk_borrowernumber - FOREIGN KEY (borrowernumber) - REFERENCES borrowers (borrowernumber) - ON DELETE CASCADE ON UPDATE CASCADE - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - }); - - print "Upgrade to $DBversion done (Bug 13920: Add API keys table)\n"; - SetVersion($DBversion); -} - -$DBversion = "XXX"; -if(CheckVersion($DBversion)) { - $dbh->do(q{ - DROP TABLE IF EXISTS api_timestamps; - }); - $dbh->do(q{ - CREATE TABLE api_timestamps ( - borrowernumber int(11) NOT NULL, - timestamp bigint, - PRIMARY KEY (borrowernumber), - CONSTRAINT api_timestamps_fk_borrowernumber - FOREIGN KEY (borrowernumber) - REFERENCES borrowers (borrowernumber) - ON DELETE CASCADE ON UPDATE CASCADE - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; - }); - - print "Upgrade to $DBversion done (Bug 13920: Add API timestamps table)\n"; - SetVersion($DBversion); -} - $DBversion = "3.21.00.008"; if ( CheckVersion($DBversion) ) { $dbh->do(q{ @@ -10773,6 +10730,30 @@ if ( CheckVersion($DBversion) ) { SetVersion ($DBversion); } +$DBversion = "XXX"; +if(CheckVersion($DBversion)) { + $dbh->do(q{ + DROP TABLE IF EXISTS api_keys; + }); + $dbh->do(q{ + CREATE TABLE api_keys ( + borrowernumber int(11) NOT NULL, + api_key VARCHAR(255) NOT NULL, + last_request_time INT(11) default NULL, + active int(1) DEFAULT 1, + PRIMARY KEY (borrowernumber, api_key), + CONSTRAINT api_keys_fk_borrowernumber + FOREIGN KEY (borrowernumber) + REFERENCES borrowers (borrowernumber) + ON DELETE CASCADE ON UPDATE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + }); + + print "Upgrade to $DBversion done (Bug 13920: Add API keys table)\n"; + SetVersion($DBversion); +} + + # DEVELOPER PROCESS, search for anything to execute in the db_update directory # SEE bug 13068 # if there is anything in the atomicupdate, read and execute it. diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc index 70edbc0..620265e 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc @@ -171,6 +171,11 @@ function searchToHold(){
  • Set permissions
  • [% END %] [% IF ( CAN_user_borrowers ) %] +
  • Manage API keys
  • + [% ELSE %] +
  • Manage API keys
  • + [% END %] + [% IF ( CAN_user_borrowers ) %] [% IF ( NorwegianPatronDBEnable == 1 ) %]
  • Delete local
  • Delete remote
  • diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/members/apikeys.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/members/apikeys.tt new file mode 100644 index 0000000..01fbdd0 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/apikeys.tt @@ -0,0 +1,78 @@ +[% USE Koha %] +[% INCLUDE 'doc-head-open.inc' %] +Koha › Patrons [% IF ( searching ) %]› API Keys[% END %] +[% INCLUDE 'doc-head-close.inc' %] + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'patron-search.inc' %] + + + +
    +
    +
    +
    + [% INCLUDE 'members-toolbar.inc' %] + +

    API keys for [% INCLUDE 'patron-title.inc' %]

    +
    +
    + + + +
    +
    + [% IF api_keys.size > 0 %] + + + + + + + + + + + [% FOREACH key IN api_keys %] + + + + + + + [% END %] + +
    KeyActiveLast transactionActions
    [% key.api_key %][% IF key.active %]Yes[% ELSE %]No[% END %][% key.timestamp || '' %] +
    + + + + +
    +
    + + + [% IF key.active %] + + + [% ELSE %] + + + [% END %] +
    +
    + [% END %] +
    +
    +
    + [% INCLUDE 'circ-menu.inc' %] +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc index c5a2298..5e6fefa 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc @@ -1,3 +1,4 @@ +[% USE Koha %] [% IF ( ( Koha.Preference( 'opacuserlogin' ) == 1 ) && loggedinusername ) %] [% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-apikeys.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-apikeys.tt new file mode 100644 index 0000000..a5c4e3c --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-apikeys.tt @@ -0,0 +1,82 @@ +[% INCLUDE 'doc-head-open.inc' %] +[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog › Your library home +[% INCLUDE 'doc-head-close.inc' %] +[% BLOCK cssinclude %][% END %] + +[% INCLUDE 'bodytag.inc' bodyid='opac-user' bodyclass='scrollto' %] +[% INCLUDE 'masthead.inc' %] + +
    + + +
    +
    +
    + +
    +
    +
    +

    Your API keys

    +
    +
    + + +
    +
    + [% IF api_keys.size > 0 %] + + + + + + + + + + + [% FOREACH key IN api_keys %] + + + + + + + [% END %] + +
    KeyActiveLast transactionActions
    [% key.api_key %][% IF key.active %]Yes[% ELSE %]No[% END %][% key.timestamp || '' %] +
    + + + +
    +
    + + [% IF key.active %] + + + [% ELSE %] + + + [% END %] +
    +
    + [% END %] +
    +
    +
    +
    +
    + +[% BLOCK jsinclude %][% END %] +[% INCLUDE 'opac-bottom.inc' %] diff --git a/members/apikeys.pl b/members/apikeys.pl new file mode 100755 index 0000000..b9d00a4 --- /dev/null +++ b/members/apikeys.pl @@ -0,0 +1,82 @@ +#!/usr/bin/env perl + +# Copyright 2015 BibLibre +# +# 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 2 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use CGI; +use String::Random; + +use C4::Auth; +use C4::Members; +use C4::Output; +use Koha::ApiKeys; +use Koha::ApiKey; + +my $cgi = new CGI; + +my ($template, $loggedinuser, $cookie) = get_template_and_user({ + template_name => 'members/apikeys.tt', + query => $cgi, + type => 'intranet', + authnotrequired => 0, + flagsrequired => {borrowers => 1}, +}); + +my $borrowernumber = $cgi->param('borrowernumber'); +my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber); +my $op = $cgi->param('op'); + +if ($op) { + if ($op eq 'generate') { + Koha::ApiKeys->grant($borrower); + print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber); + exit; + } + + if ($op eq 'delete') { + my $key = $cgi->param('key'); + Koha::ApiKeys->delete($key); + print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber); + exit; + } + + if ($op eq 'revoke') { + my $key = $cgi->param('key'); + Koha::ApiKeys->revoke($key); + print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber); + exit; + } + + if ($op eq 'activate') { + my $key = $cgi->param('key'); + Koha::ApiKeys->activate($key); + print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber); + exit; + } +} + +my @api_keys = Koha::ApiKeys->search({borrowernumber => $borrowernumber}); + +$template->param( + api_keys => \@api_keys, + borrower => $borrower, + borrowernumber => $borrowernumber, +); + +output_html_with_http_headers $cgi, $cookie, $template->output; diff --git a/opac/opac-apikeys.pl b/opac/opac-apikeys.pl new file mode 100755 index 0000000..a2008a9 --- /dev/null +++ b/opac/opac-apikeys.pl @@ -0,0 +1,97 @@ +#!/usr/bin/env perl + +# Copyright 2015 BibLibre +# +# 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 2 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use CGI; +use String::Random; + +use C4::Auth; +use C4::Members; +use C4::Output; +use Koha::ApiKeys; +use Koha::ApiKey; + +my $cgi = new CGI; + +my ($template, $loggedinuser, $cookie) = get_template_and_user({ + template_name => 'opac-apikeys.tt', + query => $cgi, + type => 'opac', + authnotrequired => 0, + flagsrequired => {borrow => 1}, +}); + +my $borrowernumber = $loggedinuser; +my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber); +my $op = $cgi->param('op'); + +if ($op) { + if ($op eq 'generate') { + my $apikey = new Koha::ApiKey; + $apikey->borrowernumber($borrowernumber); + $apikey->api_key(String::Random->new->randregex('[a-zA-Z0-9]{32}')); + $apikey->store; + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } + + if ($op eq 'delete') { + my $key = $cgi->param('key'); + my $api_key = Koha::ApiKeys->find({borrowernumber => $borrowernumber, api_key => $key}); + if ($api_key) { + $api_key->delete; + } + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } + + if ($op eq 'revoke') { + my $key = $cgi->param('key'); + my $api_key = Koha::ApiKeys->find({borrowernumber => $borrowernumber, api_key => $key}); + if ($api_key) { + $api_key->active(0); + $api_key->store; + } + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } + + if ($op eq 'activate') { + my $key = $cgi->param('key'); + my $api_key = Koha::ApiKeys->find({borrowernumber => $borrowernumber, api_key => $key}); + if ($api_key) { + $api_key->active(1); + $api_key->store; + } + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } +} + +my @api_keys = Koha::ApiKeys->search({borrowernumber => $borrowernumber}); + +$template->param( + apikeysview => 1, + api_keys => \@api_keys, + borrower => $borrower, + borrowernumber => $borrowernumber, +); + +output_html_with_http_headers $cgi, $cookie, $template->output; -- 1.9.1