View | Details | Raw Unified | Return to bug 20568
Collapse All | Expand All

(-)a/Koha/ApiKey.pm (-3 / +38 lines)
Lines 22-27 use Modern::Perl; Link Here
22
use Carp;
22
use Carp;
23
23
24
use Koha::Database;
24
use Koha::Database;
25
use Koha::Exceptions::Object;
26
27
use UUID;
25
28
26
use base qw(Koha::Object);
29
use base qw(Koha::Object);
27
30
Lines 31-45 Koha::ApiKey - Koha API Key Object class Link Here
31
34
32
=head1 API
35
=head1 API
33
36
34
=head2 Class Methods
37
=head2 Class methods
38
39
=head3 store
40
41
    my $api_key = Koha::ApiKey->new({ patron_id => $patron_id })->store;
42
43
Overloaded I<store> method.
44
45
=cut
46
47
sub store {
48
    my ($self) = @_;
49
50
    if (!$self->value) {
51
52
        my ( $uuid, $uuidstring );
53
54
        UUID::generate($uuid);
55
        UUID::unparse( $uuid, $uuidstring );
56
        $self->value( $uuidstring );
57
58
        while ( Koha::ApiKeys->search({ value => $self->value })->count > 0 ) {
59
            # Make sure $value is unique
60
            UUID::generate($uuid);
61
            UUID::unparse( $uuid, $uuidstring );
62
            $self->value( $uuidstring );
63
        }
64
    }
65
66
    return $self->SUPER::store();
67
}
68
69
=head2 Internal methods
35
70
36
=cut
71
=cut
37
72
38
=head3 type
73
=head3 _type
39
74
40
=cut
75
=cut
41
76
42
sub type {
77
sub _type {
43
    return 'ApiKey';
78
    return 'ApiKey';
44
}
79
}
45
80
(-)a/Koha/ApiKeys.pm (-5 / +8 lines)
Lines 22-29 use Modern::Perl; Link Here
22
use Carp;
22
use Carp;
23
23
24
use Koha::Database;
24
use Koha::Database;
25
25
use Koha::ApiKey;
26
use Koha::Borrower;
27
26
28
use base qw(Koha::Objects);
27
use base qw(Koha::Objects);
29
28
Lines 33-50 Koha::ApiKeys - Koha API Keys Object class Link Here
33
32
34
=head1 API
33
=head1 API
35
34
36
=head2 Class Methods
35
=head2 Internal methods
37
36
38
=cut
37
=cut
39
38
40
=head3 type
39
=head3 _type
41
40
42
=cut
41
=cut
43
42
44
sub type {
43
sub _type {
45
    return 'ApiKey';
44
    return 'ApiKey';
46
}
45
}
47
46
47
=head3 object_class
48
49
=cut
50
48
sub object_class {
51
sub object_class {
49
    return 'Koha::ApiKey';
52
    return 'Koha::ApiKey';
50
}
53
}
(-)a/Koha/Schema/Result/ApiKey.pm (-15 / +46 lines)
Lines 23-35 __PACKAGE__->table("api_keys"); Link Here
23
23
24
=head1 ACCESSORS
24
=head1 ACCESSORS
25
25
26
=head2 borrowernumber
26
=head2 id
27
28
  data_type: 'integer'
29
  is_auto_increment: 1
30
  is_nullable: 0
31
32
=head2 patron_id
27
33
28
  data_type: 'integer'
34
  data_type: 'integer'
29
  is_foreign_key: 1
35
  is_foreign_key: 1
30
  is_nullable: 0
36
  is_nullable: 0
31
37
32
=head2 api_key
38
=head2 value
39
40
  data_type: 'varchar'
41
  is_nullable: 0
42
  size: 191
43
44
=head2 description
33
45
34
  data_type: 'varchar'
46
  data_type: 'varchar'
35
  is_nullable: 0
47
  is_nullable: 0
Lines 37-74 __PACKAGE__->table("api_keys"); Link Here
37
49
38
=head2 active
50
=head2 active
39
51
40
  data_type: 'integer'
52
  data_type: 'tinyint'
41
  default_value: 1
53
  default_value: 1
42
  is_nullable: 1
54
  is_nullable: 0
43
55
44
=cut
56
=cut
45
57
46
__PACKAGE__->add_columns(
58
__PACKAGE__->add_columns(
47
  "borrowernumber",
59
  "id",
60
  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
61
  "patron_id",
48
  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
62
  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
49
  "api_key",
63
  "value",
64
  { data_type => "varchar", is_nullable => 0, size => 191 },
65
  "description",
50
  { data_type => "varchar", is_nullable => 0, size => 255 },
66
  { data_type => "varchar", is_nullable => 0, size => 255 },
51
  "active",
67
  "active",
52
  { data_type => "integer", default_value => 1, is_nullable => 1 },
68
  { data_type => "tinyint", default_value => 1, is_nullable => 0 },
53
);
69
);
54
70
55
=head1 PRIMARY KEY
71
=head1 PRIMARY KEY
56
72
57
=over 4
73
=over 4
58
74
59
=item * L</borrowernumber>
75
=item * L</id>
60
76
61
=item * L</api_key>
77
=back
78
79
=cut
80
81
__PACKAGE__->set_primary_key("id");
82
83
=head1 UNIQUE CONSTRAINTS
84
85
=head2 C<value>
86
87
=over 4
88
89
=item * L</value>
62
90
63
=back
91
=back
64
92
65
=cut
93
=cut
66
94
67
__PACKAGE__->set_primary_key("borrowernumber", "api_key");
95
__PACKAGE__->add_unique_constraint("value", ["value"]);
68
96
69
=head1 RELATIONS
97
=head1 RELATIONS
70
98
71
=head2 borrowernumber
99
=head2 patron
72
100
73
Type: belongs_to
101
Type: belongs_to
74
102
Lines 77-92 Related object: L<Koha::Schema::Result::Borrower> Link Here
77
=cut
105
=cut
78
106
79
__PACKAGE__->belongs_to(
107
__PACKAGE__->belongs_to(
80
  "borrowernumber",
108
  "patron",
81
  "Koha::Schema::Result::Borrower",
109
  "Koha::Schema::Result::Borrower",
82
  { borrowernumber => "borrowernumber" },
110
  { borrowernumber => "patron_id" },
83
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
111
  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
84
);
112
);
85
113
86
114
87
# Created by DBIx::Class::Schema::Loader v0.07025 @ 2015-03-24 07:35:30
115
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2018-04-13 16:02:05
88
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dvujXVM5Vfu3SA2UfiVPtw
116
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:cIWj7NAP1+CYQ1LA6gWd6w
89
117
118
__PACKAGE__->add_columns(
119
    '+active' => { is_boolean => 1 }
120
);
90
121
91
# You can replace this text with custom code or comments, and it will be preserved on regeneration
122
# You can replace this text with custom code or comments, and it will be preserved on regeneration
92
1;
123
1;
(-)a/Koha/Schema/Result/Borrower.pm (-2 / +17 lines)
Lines 710-715 __PACKAGE__->has_many( Link Here
710
  { cascade_copy => 0, cascade_delete => 0 },
710
  { cascade_copy => 0, cascade_delete => 0 },
711
);
711
);
712
712
713
=head2 api_keys
714
715
Type: has_many
716
717
Related object: L<Koha::Schema::Result::ApiKey>
718
719
=cut
720
721
__PACKAGE__->has_many(
722
  "api_keys",
723
  "Koha::Schema::Result::ApiKey",
724
  { "foreign.patron_id" => "self.borrowernumber" },
725
  { cascade_copy => 0, cascade_delete => 0 },
726
);
727
713
=head2 aqbasketusers
728
=head2 aqbasketusers
714
729
715
Type: has_many
730
Type: has_many
Lines 1386-1393 Composing rels: L</aqorder_users> -> ordernumber Link Here
1386
__PACKAGE__->many_to_many("ordernumbers", "aqorder_users", "ordernumber");
1401
__PACKAGE__->many_to_many("ordernumbers", "aqorder_users", "ordernumber");
1387
1402
1388
1403
1389
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2018-02-16 17:54:53
1404
# Created by DBIx::Class::Schema::Loader v0.07042 @ 2018-04-11 19:53:27
1390
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wM40W+toV8ca5LFwinkHxA
1405
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:/vLIMxDv4RcJOqKj6Mfg6w
1391
1406
1392
__PACKAGE__->belongs_to(
1407
__PACKAGE__->belongs_to(
1393
    "guarantor",
1408
    "guarantor",
(-)a/installer/data/mysql/atomicupdate/bug_20568_api_keys.perl (+27 lines)
Line 0 Link Here
1
$DBversion = "XXX";
2
if(CheckVersion($DBversion)) {
3
4
    $dbh->do(q{
5
        DROP TABLE IF EXISTS api_keys;
6
    });
7
8
    $dbh->do(q{
9
        CREATE TABLE `api_keys` (
10
            `id`          INT(11) NOT NULL AUTO_INCREMENT,
11
            `patron_id`   INT(11) NOT NULL,
12
            `value`       VARCHAR(191) NOT NULL UNIQUE,
13
            `description` VARCHAR(255) NOT NULL,
14
            `active`      TINYINT(1) DEFAULT 1 NOT NULL,
15
            PRIMARY KEY (`id`),
16
            UNIQUE KEY `value` (`value`),
17
            KEY `patron_id` (`patron_id`),
18
            CONSTRAINT `api_keys_fk_patron_id`
19
              FOREIGN KEY (`patron_id`)
20
              REFERENCES `borrowers` (`borrowernumber`)
21
              ON DELETE CASCADE ON UPDATE CASCADE
22
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
23
    });
24
25
    print "Upgrade to $DBversion done (Bug 20568 - Add API key management interface for patrons)\n";
26
    SetVersion($DBversion);
27
}
(-)a/installer/data/mysql/kohastructure.sql (-16 / +20 lines)
Lines 15-36 Link Here
15
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
15
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
16
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17
17
18
--
19
-- Table structure for table api_keys
20
--
21
22
DROP TABLE IF EXISTS api_keys;
23
CREATE TABLE api_keys (
24
    borrowernumber int(11) NOT NULL, -- foreign key to the borrowers table
25
    api_key VARCHAR(255) NOT NULL, -- API key used for API authentication
26
    active int(1) DEFAULT 1, -- 0 means this API key is revoked
27
    PRIMARY KEY (borrowernumber, api_key),
28
    CONSTRAINT api_keys_fk_borrowernumber
29
      FOREIGN KEY (borrowernumber)
30
      REFERENCES borrowers (borrowernumber)
31
      ON DELETE CASCADE ON UPDATE CASCADE
32
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
33
34
--
18
--
35
-- Table structure for table `auth_header`
19
-- Table structure for table `auth_header`
36
--
20
--
Lines 1729-1734 CREATE TABLE borrower_sync ( Link Here
1729
  CONSTRAINT borrower_sync_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE
1713
  CONSTRAINT borrower_sync_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE
1730
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1714
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1731
1715
1716
--
1717
-- Table structure for table api_keys
1718
--
1719
1720
DROP TABLE IF EXISTS `api_keys`;
1721
CREATE TABLE `api_keys` (
1722
    `id`          INT(11) NOT NULL AUTO_INCREMENT, -- API key internal identifier
1723
    `patron_id`   INT(11) NOT NULL,                -- Foreign key to the borrowers table
1724
    `value`       VARCHAR(191) NOT NULL,           -- API key used for API authentication
1725
    `description` VARCHAR(255) NOT NULL,           -- API key description
1726
    `active`      TINYINT(1) DEFAULT 1 NOT NULL,   -- 0 means this API key is revoked
1727
    PRIMARY KEY (`id`),
1728
    KEY `patron_id` (`patron_id`),
1729
    UNIQUE KEY `value` (`value`),
1730
    CONSTRAINT `api_keys_fk_patron_id`
1731
      FOREIGN KEY (`patron_id`)
1732
      REFERENCES `borrowers` (`borrowernumber`)
1733
      ON DELETE CASCADE ON UPDATE CASCADE
1734
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
1735
1732
--
1736
--
1733
-- Table structure for table `issues`
1737
-- Table structure for table `issues`
1734
--
1738
--
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc (-3 / +2 lines)
Lines 59-68 Link Here
59
                [% IF CAN_user_borrowers_edit_borrowers && useDischarge %]
59
                [% IF CAN_user_borrowers_edit_borrowers && useDischarge %]
60
                    <li><a href="/cgi-bin/koha/members/discharge.pl?borrowernumber=[% patron.borrowernumber %]">Discharge</a></li>
60
                    <li><a href="/cgi-bin/koha/members/discharge.pl?borrowernumber=[% patron.borrowernumber %]">Discharge</a></li>
61
                [% END %]
61
                [% END %]
62
                [% IF CAN_user_borrowers_edit_borrowers %]
63
62
64
                [% IF ( CAN_user_borrowers ) %]
63
                [% IF CAN_user_borrowers_edit_borrowers %]
65
                    <li><a id="apikeys" href="/cgi-bin/koha/members/apikeys.pl?borrowernumber=[% borrowernumber %]">Manage API keys</a></li>
64
                    <li><a id="apikeys" href="/cgi-bin/koha/members/apikeys.pl?patron_id=[% borrowernumber %]">Manage API keys</a></li>
66
                [% ELSE %]
65
                [% ELSE %]
67
                    <li class="disabled"><a data-toggle="tooltip" data-placement="left" title="You are not authorized to manage API keys" id="apikeys" href="#">Manage API keys</a></li>
66
                    <li class="disabled"><a data-toggle="tooltip" data-placement="left" title="You are not authorized to manage API keys" id="apikeys" href="#">Manage API keys</a></li>
68
                [% END %]
67
                [% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/apikeys.tt (-8 / +19 lines)
Lines 24-38 Link Here
24
        <h1>API keys for [% INCLUDE 'patron-title.inc' %]</h1>
24
        <h1>API keys for [% INCLUDE 'patron-title.inc' %]</h1>
25
        <div>
25
        <div>
26
          <form action="/cgi-bin/koha/members/apikeys.pl" method="post">
26
          <form action="/cgi-bin/koha/members/apikeys.pl" method="post">
27
            <input type="hidden" name="borrowernumber" value="[% borrowernumber %]">
27
            <input type="hidden" name="patron_id" value="[% patron.id %]">
28
            <input type="hidden" name="op" value="generate">
28
            <input type="hidden" name="op" value="generate">
29
            <input type="submit" value="Generate new key">
29
            <label for="description">Description: </label>
30
            <input type="text" name="description">
31
            <button class="btn btn-default btn-sm" type="submit"><i class="fa fa-plus"></i> Generate a new key</span></button>
30
          </form>
32
          </form>
31
        </div>
33
        </div>
32
        [% IF api_keys.size > 0 %]
34
35
        <br/>
36
37
        <div id="keys">
38
        [% IF api_keys && api_keys.size > 0 %]
33
          <table>
39
          <table>
34
            <thead>
40
            <thead>
35
              <tr>
41
              <tr>
42
                <th>Description</th>
36
                <th>Key</th>
43
                <th>Key</th>
37
                <th>Active</th>
44
                <th>Active</th>
38
                <th>Actions</th>
45
                <th>Actions</th>
Lines 41-58 Link Here
41
            <tbody>
48
            <tbody>
42
              [% FOREACH key IN api_keys %]
49
              [% FOREACH key IN api_keys %]
43
                <tr>
50
                <tr>
44
                  <td>[% key.api_key %]</td>
51
                  <td>[% key.description %]</td>
52
                  <td>[% key.value %]</td>
45
                  <td>[% IF key.active %]Yes[% ELSE %]No[% END %]</td>
53
                  <td>[% IF key.active %]Yes[% ELSE %]No[% END %]</td>
46
                  <td>
54
                  <td>
47
                    <form action="/cgi-bin/koha/members/apikeys.pl" method="post">
55
                    <form action="/cgi-bin/koha/members/apikeys.pl" method="post">
48
                      <input type="hidden" name="borrowernumber" value="[% borrowernumber %]">
56
                      <input type="hidden" name="patron_id" value="[% patron.id %]">
49
                      <input type="hidden" name="key" value="[% key.api_key %]">
57
                      <input type="hidden" name="key" value="[% key.value %]">
50
                      <input type="hidden" name="op" value="delete">
58
                      <input type="hidden" name="op" value="delete">
51
                      <input type="submit" value="Delete">
59
                      <input type="submit" value="Delete">
52
                    </form>
60
                    </form>
53
                    <form action="/cgi-bin/koha/members/apikeys.pl" method="post">
61
                    <form action="/cgi-bin/koha/members/apikeys.pl" method="post">
54
                      <input type="hidden" name="borrowernumber" value="[% borrowernumber %]">
62
                      <input type="hidden" name="patron_id" value="[% patron.id %]">
55
                      <input type="hidden" name="key" value="[% key.api_key %]">
63
                      <input type="hidden" name="key" value="[% key.value %]">
56
                      [% IF key.active %]
64
                      [% IF key.active %]
57
                        <input type="hidden" name="op" value="revoke">
65
                        <input type="hidden" name="op" value="revoke">
58
                        <input type="submit" value="Revoke">
66
                        <input type="submit" value="Revoke">
Lines 66-72 Link Here
66
              [% END %]
74
              [% END %]
67
            </tbody>
75
            </tbody>
68
          </table>
76
          </table>
77
        [% ELSE %]
78
            <span class="warn">No keys defined for the current patron.</span>
69
        [% END %]
79
        [% END %]
80
        </div>
70
      </div>
81
      </div>
71
    </div>
82
    </div>
72
    <div class="yui-b">
83
    <div class="yui-b">
(-)a/members/apikeys.pl (-40 / +45 lines)
Lines 25-96 use String::Random; Link Here
25
use C4::Auth;
25
use C4::Auth;
26
use C4::Members;
26
use C4::Members;
27
use C4::Output;
27
use C4::Output;
28
28
use Koha::ApiKeys;
29
use Koha::ApiKeys;
29
use Koha::ApiKey;
30
use Koha::Patrons;
30
31
31
my $cgi = new CGI;
32
my $cgi = new CGI;
32
33
33
my ($template, $loggedinuser, $cookie) = get_template_and_user({
34
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
34
    template_name => 'members/apikeys.tt',
35
    {   template_name   => 'members/apikeys.tt',
35
    query => $cgi,
36
        query           => $cgi,
36
    type => 'intranet',
37
        type            => 'intranet',
37
    authnotrequired => 0,
38
        authnotrequired => 0,
38
    flagsrequired => {borrowers => 1},
39
        flagsrequired   => { borrowers => 1 },
39
});
40
    }
41
);
42
43
my $patron_id = $cgi->param('patron_id') // '';
44
my $api_key   = $cgi->param('key')       // '';
45
my $patron    = Koha::Patrons->find($patron_id);
40
46
41
my $borrowernumber = $cgi->param('borrowernumber');
42
my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber);
43
my $op = $cgi->param('op');
47
my $op = $cgi->param('op');
44
48
45
if ($op) {
49
if ($op) {
46
    if ($op eq 'generate') {
50
    if ( $op eq 'generate' ) {
47
        my $apikey = new Koha::ApiKey;
51
        my $description = $cgi->param('description') // '';
48
        $apikey->borrowernumber($borrowernumber);
52
        my $api_key = Koha::ApiKey->new(
49
        $apikey->api_key(String::Random->new->randregex('[a-zA-Z0-9]{32}'));
53
            {   patron_id   => $patron_id,
50
        $apikey->store;
54
                description => $description
51
        print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber);
55
            }
56
        );
57
        $api_key->store;
58
        print $cgi->redirect( '/cgi-bin/koha/members/apikeys.pl?patron_id=' . $patron_id );
52
        exit;
59
        exit;
53
    }
60
    }
54
61
55
    if ($op eq 'delete') {
62
    if ( $op eq 'delete' ) {
56
        my $key = $cgi->param('key');
63
        my $api_key = $cgi->param('key');
57
        my $api_key = Koha::ApiKeys->find({borrowernumber => $borrowernumber, api_key => $key});
64
        my $key = Koha::ApiKeys->find({ patron_id => $patron_id, value => $api_key });
58
        if ($api_key) {
65
        if ($key) {
59
            $api_key->delete;
66
            $key->delete;
60
        }
67
        }
61
        print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber);
68
        print $cgi->redirect( '/cgi-bin/koha/members/apikeys.pl?patron_id=' . $patron_id );
62
        exit;
69
        exit;
63
    }
70
    }
64
71
65
    if ($op eq 'revoke') {
72
    if ( $op eq 'revoke' ) {
66
        my $key = $cgi->param('key');
73
        my $api_key = $cgi->param('key');
67
        my $api_key = Koha::ApiKeys->find({borrowernumber => $borrowernumber, api_key => $key});
74
        my $key = Koha::ApiKeys->find({ patron_id => $patron_id, value => $api_key });
68
        if ($api_key) {
75
        if ($key) {
69
            $api_key->active(0);
76
            $key->active(0);
70
            $api_key->store;
77
            $key->store;
71
        }
78
        }
72
        print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber);
79
        print $cgi->redirect( '/cgi-bin/koha/members/apikeys.pl?patron_id=' . $patron_id );
73
        exit;
80
        exit;
74
    }
81
    }
75
82
76
    if ($op eq 'activate') {
83
    if ( $op eq 'activate' ) {
77
        my $key = $cgi->param('key');
84
        my $api_key = $cgi->param('key');
78
        my $api_key = Koha::ApiKeys->find({borrowernumber => $borrowernumber, api_key => $key});
85
        my $key = Koha::ApiKeys->find({ patron_id => $patron_id, value => $api_key });
79
        if ($api_key) {
86
        if ($key) {
80
            $api_key->active(1);
87
            $key->active(1);
81
            $api_key->store;
88
            $key->store;
82
        }
89
        }
83
        print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber);
90
        print $cgi->redirect( '/cgi-bin/koha/members/apikeys.pl?patron_id=' . $patron_id );
84
        exit;
91
        exit;
85
    }
92
    }
86
}
93
}
87
94
88
my @api_keys = Koha::ApiKeys->search({borrowernumber => $borrowernumber});
95
my @api_keys = Koha::ApiKeys->search({ patron_id => $patron_id });
89
96
90
$template->param(
97
$template->param(
91
    api_keys => \@api_keys,
98
    api_keys => \@api_keys,
92
    borrower => $borrower,
99
    patron   => $patron
93
    borrowernumber => $borrowernumber,
94
);
100
);
95
101
96
output_html_with_http_headers $cgi, $cookie, $template->output;
102
output_html_with_http_headers $cgi, $cookie, $template->output;
97
- 

Return to bug 20568