From 6e265c479881d5917991ea8750039b0bca93ee74 Mon Sep 17 00:00:00 2001 From: Olli-Antti Kivilahti Date: Tue, 11 Aug 2015 11:24:23 +0300 Subject: [PATCH] Bug 13920: API Authentication, part 1: API keys management in interface Depends on Bug 14539 and Bug 7174. 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. Includes unit tests and selenium integration tests for Intra and OPAC. 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 | 121 ++++++++++++++++++++ Koha/Borrower.pm | 32 ++++++ installer/data/mysql/kohastructure.sql | 19 ++++ installer/data/mysql/updatedatabase.pl | 27 +++++ .../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 | 81 ++++++++++++++ t/db_dependent/Koha/ApiKeys.t | 124 +++++++++++++++++++++ 12 files changed, 705 insertions(+) create mode 100644 Koha/ApiKey.pm create mode 100644 Koha/ApiKeys.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 create mode 100644 t/db_dependent/Koha/ApiKeys.t 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..6a24111 --- /dev/null +++ b/Koha/ApiKeys.pm @@ -0,0 +1,121 @@ +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 Koha::ApiKey; + +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'; +} + +sub _get_castable_unique_columns { + return ['api_key_id', 'api_key']; +} + +=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->cast($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; + return $apiKey; +} + +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; + } + return $apiKey; +} + +sub activate { + my ($self, $apiKey) = @_; + $apiKey = Koha::ApiKeys->cast($apiKey); + + if ($apiKey) { + $apiKey->active(1); + $apiKey->store; + } + return $apiKey; +} + +1; diff --git a/Koha/Borrower.pm b/Koha/Borrower.pm index 93426bf..7c7a26b 100644 --- a/Koha/Borrower.pm +++ b/Koha/Borrower.pm @@ -43,6 +43,38 @@ sub type { return 'Borrower'; } +=head getApiKeys + + my @apiKeys = $borrower->getApiKeys( $activeOnly ); + +=cut + +sub getApiKeys { + my ($self, $activeOnly) = @_; + + my @dbix_objects = $self->_result()->api_keys({active => 1}); + for (my $i=0 ; $i_new_from_dbic($dbix_objects[$i]); + } + + return \@dbix_objects; +} + +=head getApiKey + + my $apiKey = $borrower->getApiKeys( $activeOnly ); + +=cut + +sub getApiKey { + my ($self, $activeOnly) = @_; + + my $dbix_object = $self->_result()->api_keys({active => 1})->next(); + my $object = Koha::ApiKey->_new_from_dbic($dbix_object); + + return $object; +} + =head1 AUTHOR Kyle M Hall diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql index e919349..07acfdf 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -16,6 +16,25 @@ /*!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 ( + api_key_id INT(11) NOT NULL auto_increment, + 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 0, -- 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 (api_key_id), + UNIQUE KEY apk_bornumkey_idx (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 3317c3c..dc49456 100755 --- a/installer/data/mysql/updatedatabase.pl +++ b/installer/data/mysql/updatedatabase.pl @@ -10776,6 +10776,33 @@ if ( CheckVersion($DBversion) ) { SetVersion ($DBversion); } +$DBversion = "XXX"; +if(CheckVersion($DBversion)) { + $dbh->do(q{ + CREATE TABLE api_keys ( + api_key_id INT(11) NOT NULL auto_increment, + 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 0, -- 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 (api_key_id), + UNIQUE KEY apk_bornumkey_idx (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; + }); + + use Koha::Auth::PermissionManager; + my $pm = Koha::Auth::PermissionManager->new(); + $pm->addPermission({module => 'borrowers', code => 'manage_api_keys', description => "Manage Borrowers' REST API keys"}); + + 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..a3155cb 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc @@ -170,6 +170,11 @@ function searchToHold(){ [% ELSE %]
  • Set permissions
  • [% END %] + [% IF ( CAN_user_borrowers_manage_api_keys ) %] +
  • Manage API keys
  • + [% ELSE %] +
  • Manage API keys
  • + [% END %] [% IF ( CAN_user_borrowers ) %] [% IF ( NorwegianPatronDBEnable == 1 ) %]
  • Delete local
  • 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..7fdd416 --- /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 › API Keys +[% 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.last_request_time || '' %] +
    + + + + +
    +
    + + + [% 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..297aa2a --- /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 › Your API keys +[% 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.last_request_time || '' %] +
    + + + +
    +
    + + [% 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..ac358be --- /dev/null +++ b/members/apikeys.pl @@ -0,0 +1,82 @@ +#!/usr/bin/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 => 'manage_api_keys'}, +}); + +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..72cc70c --- /dev/null +++ b/opac/opac-apikeys.pl @@ -0,0 +1,81 @@ +#!/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; + +my $cgi = new CGI; + +my ($template, $loggedinuser, $cookie) = get_template_and_user({ + template_name => 'opac-apikeys.tt', + query => $cgi, + type => 'opac', + authnotrequired => 0, +}); + +my $borrowernumber = $loggedinuser; +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/opac-apikeys.pl'); + exit; + } + + if ($op eq 'delete') { + my $key = $cgi->param('key'); + Koha::ApiKeys->delete($key); + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } + + if ($op eq 'revoke') { + my $key = $cgi->param('key'); + Koha::ApiKeys->revoke($key); + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } + + if ($op eq 'activate') { + my $key = $cgi->param('key'); + Koha::ApiKeys->activate($key); + 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; diff --git a/t/db_dependent/Koha/ApiKeys.t b/t/db_dependent/Koha/ApiKeys.t new file mode 100644 index 0000000..19fcd70 --- /dev/null +++ b/t/db_dependent/Koha/ApiKeys.t @@ -0,0 +1,124 @@ +#!/usr/bin/perl + +# Copyright 2015 Vaara-kirjastot +# +# 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::More; +use Try::Tiny; +use Scalar::Util qw(blessed); + +use Koha::Auth::PermissionManager; +use Koha::ApiKeys; + +use t::lib::TestObjects::ObjectFactory; +use t::lib::TestObjects::BorrowerFactory; +use t::lib::Page::Members::Moremember; +use t::lib::Page::Opac::OpacMain; + +##Setting up the test context +my $testContext = {}; + +my $password = '1234'; +my $borrowerFactory = t::lib::TestObjects::BorrowerFactory->new(); +my $borrowers = $borrowerFactory->createTestGroup([ + {firstname => 'Olli-Antti', + surname => 'Kivi', + cardnumber => '1A01', + branchcode => 'CPL', + password => $password, + }, + {firstname => 'Alli-Ontti', + surname => 'Ivik', + cardnumber => '1A02', + branchcode => 'CPL', + password => $password, + }, + ], undef, $testContext); +my $borrowerKivi = $borrowers->{'1A01'}; +my $borrowerIvik = $borrowers->{'1A02'}; +my $permissionManager = Koha::Auth::PermissionManager->new(); +$permissionManager->grantPermission($borrowerKivi, 'borrowers', 'manage_api_keys'); + + +##Test context set, starting testing: +eval { #run in a eval-block so we don't die without tearing down the test context +subtest "ApiKeys API Unit tests" => sub { + my $borrowerKivi = $borrowers->{'1A01'}; + my $borrowerIvik = $borrowers->{'1A02'}; + + + my $apiKey = Koha::ApiKeys->grant($borrowerKivi); + is($apiKey->borrowernumber, $borrowerKivi->borrowernumber, "ApiKey granted"); + + Koha::ApiKeys->revoke($apiKey); + is($apiKey->active, 0, "ApiKey revoked"); + + Koha::ApiKeys->activate($apiKey); + is($apiKey->active, 1, "ApiKey activated"); + + Koha::ApiKeys->grant($borrowerIvik, $apiKey); + is($apiKey->borrowernumber, $borrowerIvik->borrowernumber, "ApiKey granted to another Borrower"); + + Koha::ApiKeys->delete($apiKey); + $apiKey = Koha::ApiKeys->find({api_key_id => $apiKey->api_key_id}); + ok(not($apiKey), "ApiKey deleted"); +} +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +eval { +subtest "ApiKeys Intra Integration tests" => sub { + my $agent = t::lib::Page::Members::Moremember->new({borrowernumber => $borrowerKivi->borrowernumber}); + $agent->doPasswordLogin($borrowerKivi->userid, $password)->navigateManageApiKeys()->generateNewApiKey(); + my @apiKeys = Koha::ApiKeys->search({borrowernumber => $borrowerKivi->borrowernumber}); + $agent->revokeApiKey($apiKeys[0]->api_key)->deleteApiKey($apiKeys[0]->api_key) + ->quit(); +} +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +eval { +subtest "ApiKeys OPAC Integration tests" => sub { + my $agent = t::lib::Page::Opac::OpacMain->new(); + $agent->doPasswordLogin($borrowerKivi->userid, $password)->navigateYourAPIKeys()->generateNewApiKey(); + my @apiKeys = Koha::ApiKeys->search({borrowernumber => $borrowerKivi->borrowernumber}); + $agent->revokeApiKey($apiKeys[0]->api_key)->deleteApiKey($apiKeys[0]->api_key) + ->quit(); +} +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +##All tests done, tear down test context +tearDown(); +done_testing; + +sub tearDown { + t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); +} \ No newline at end of file -- 1.9.1