From 77f294799ed4f5289e2fddda6d34d2597bbc5ead Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Mon, 23 Mar 2015 20:14:23 +0100 Subject: [PATCH] Bug 20568: API keys management in interface 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. 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 | 52 ++++++++++ Koha/Schema/Result/ApiKey.pm | 92 ++++++++++++++++++ installer/data/mysql/kohastructure.sql | 16 +++ .../prog/en/includes/members-toolbar.inc | 9 ++ .../prog/en/modules/members/apikeys.tt | 76 +++++++++++++++ .../bootstrap/en/includes/usermenu.inc | 9 ++ .../bootstrap/en/modules/opac-apikeys.tt | 80 +++++++++++++++ members/apikeys.pl | 96 ++++++++++++++++++ opac/opac-apikeys.pl | 97 +++++++++++++++++++ 10 files changed, 573 insertions(+) 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 0000000000..be4b76e43c --- /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 0000000000..8b25820034 --- /dev/null +++ b/Koha/ApiKeys.pm @@ -0,0 +1,52 @@ +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 Carp; + +use Koha::Database; + +use Koha::Borrower; + +use base qw(Koha::Objects); + +=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'; +} + +1; diff --git a/Koha/Schema/Result/ApiKey.pm b/Koha/Schema/Result/ApiKey.pm new file mode 100644 index 0000000000..6767a568aa --- /dev/null +++ b/Koha/Schema/Result/ApiKey.pm @@ -0,0 +1,92 @@ +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 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 }, + "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.07025 @ 2015-03-24 07:35:30 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dvujXVM5Vfu3SA2UfiVPtw + + +# 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 6842eb580c..fc9d9365f8 100644 --- a/installer/data/mysql/kohastructure.sql +++ b/installer/data/mysql/kohastructure.sql @@ -15,6 +15,22 @@ /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!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 + 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/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc index 467997fd60..138ef92e79 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc @@ -55,10 +55,19 @@ [% ELSE %]
  • Set permissions
  • [% END %] + [% IF CAN_user_borrowers_edit_borrowers && useDischarge %]
  • Discharge
  • [% END %] [% IF CAN_user_borrowers_edit_borrowers %] + + [% 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 0000000000..e5131fc1aa --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/members/apikeys.tt @@ -0,0 +1,76 @@ +[% 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 %] + +
    KeyActiveActions
    [% key.api_key %][% IF key.active %]Yes[% ELSE %]No[% END %] +
    + + + + +
    +
    + + + [% 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 7d27f22d75..d58d4e5131 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 0000000000..c2235b421a --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-apikeys.tt @@ -0,0 +1,80 @@ +[% 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 %] + +
    KeyActiveActions
    [% key.api_key %][% IF key.active %]Yes[% ELSE %]No[% END %] +
    + + + +
    +
    + + [% 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 0000000000..7893ad317f --- /dev/null +++ b/members/apikeys.pl @@ -0,0 +1,96 @@ +#!/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') { + 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/members/apikeys.pl?borrowernumber=' . $borrowernumber); + 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/members/apikeys.pl?borrowernumber=' . $borrowernumber); + 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/members/apikeys.pl?borrowernumber=' . $borrowernumber); + 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/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 0000000000..a2008a90b7 --- /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; -- 2.17.0