[Koha-patches] [PATCH] bug_16034 Koha::ExternalContent::OverDrive - a wrapper around WebService::ILS::Overdrive::Patron
Srdjan
srdjan at catalyst.net.nz
Thu Mar 10 05:30:53 CET 2016
* Using the upstream module for all the heavy lifting
* opac/external/overdrive/auth.pl - 3-legged authentication handler
---
Koha/ExternalContent.pm | 88 ++++++++++
Koha/ExternalContent/OverDrive.pm | 206 ++++++++++++++++++++++++
Koha/Schema/Result/Borrower.pm | 2 +
installer/data/mysql/atomicupdate/overdrive.sql | 1 +
installer/data/mysql/kohastructure.sql | 1 +
opac/external/overdrive/auth.pl | 56 +++++++
t/Koha_ExternalContent_OverDrive.t | 38 +++++
7 files changed, 392 insertions(+)
create mode 100644 Koha/ExternalContent.pm
create mode 100644 Koha/ExternalContent/OverDrive.pm
create mode 100644 installer/data/mysql/atomicupdate/overdrive.sql
create mode 100755 opac/external/overdrive/auth.pl
create mode 100755 t/Koha_ExternalContent_OverDrive.t
diff --git a/Koha/ExternalContent.pm b/Koha/ExternalContent.pm
new file mode 100644
index 0000000..e459188
--- /dev/null
+++ b/Koha/ExternalContent.pm
@@ -0,0 +1,88 @@
+# Copyright 2014 Catalyst
+#
+# 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.
+
+package Koha::ExternalContent;
+
+use Modern::Perl;
+use Carp;
+use base qw(Class::Accessor);
+
+use Koha;
+use Koha::Patrons;
+use C4::Auth;
+
+__PACKAGE__->mk_accessors(qw(client koha_session_id koha_patron));
+
+=head1 NAME
+
+Koha::ExternalContent
+
+=head1 SYNOPSIS
+
+ use Koha::ExternalContent;
+ my $externalcontent = Koha::ExternalContent->new();
+
+=head1 METHODS
+
+=cut
+
+sub agent_string {
+ return 'Koha/'.Koha::version();
+}
+
+sub new {
+ my $class = shift;
+ my $params = shift || {};
+ return bless $params, $class;
+}
+
+sub _koha_session {
+ my $self = shift;
+ my $session_id = $self->koha_session_id or return;
+ return C4::Auth::get_session($session_id);
+}
+
+sub get_from_koha_session {
+ my $self = shift;
+ my $key = shift or croak "No key";
+ my $session = $self->_koha_session or return;
+ return $session->param($key);
+}
+
+sub set_in_koha_session {
+ my $self = shift;
+ my $key = shift or croak "No key";
+ my $value = shift;
+ my $session = $self->_koha_session or croak "No Koha session";
+ return $session->param($key, $value);
+}
+
+sub koha_patron {
+ my $self = shift;
+
+ if (my $patron = $self->_koha_patron_accessor) {
+ return $patron;
+ }
+
+ my $id = $self->get_from_koha_session('number')
+ or die "No patron number in session";
+ my $patron = Koha::Patrons->find($id)
+ or die "Invalid patron number in session";
+ return $self->_koha_patron_accessor($patron);
+}
+
+1;
diff --git a/Koha/ExternalContent/OverDrive.pm b/Koha/ExternalContent/OverDrive.pm
new file mode 100644
index 0000000..00f4eaf
--- /dev/null
+++ b/Koha/ExternalContent/OverDrive.pm
@@ -0,0 +1,206 @@
+# Copyright 2014 Catalyst
+#
+# 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.
+
+package Koha::ExternalContent::OverDrive;
+
+use Modern::Perl;
+use Carp;
+
+use base qw(Koha::ExternalContent);
+use WebService::ILS::OverDrive::Patron;
+use C4::Context;
+use Koha::Logger;
+
+use constant logger => Koha::Logger->get();
+
+=head1 NAME
+
+Koha::ExternalContent::OverDrive
+
+=head1 SYNOPSIS
+
+ use Koha::ExternalContent::OverDrive;
+ my $od_client = Koha::ExternalContent::OverDrive->new();
+ my $od_auth_url = $od_client->auth_url();
+
+=head1 DESCRIPTION
+
+A (very) thin wrapper around C<WebService::ILS::OverDrive::Patron>
+
+Takes "OverDrive*" Koha preferences
+
+=cut
+
+sub new {
+ my $class = shift;
+ my $params = shift || {};
+ $params->{koha_session_id} or croak "No koha_session_id";
+
+ my $self = $class->SUPER::new($params);
+ unless ($params->{client}) {
+ my $client_key = C4::Context->preference('OverDriveClientKey')
+ or croak("OverDriveClientKey pref not set");
+ my $client_secret = C4::Context->preference('OverDriveClientSecret')
+ or croak("OverDriveClientSecret pref not set");
+ my $library_id = C4::Context->preference('OverDriveLibraryID')
+ or croak("OverDriveLibraryID pref not set");
+ my ($token, $token_type) = $self->get_token_from_koha_session();
+ $self->client( WebService::ILS::OverDrive::Patron->new(
+ client_id => $client_key,
+ client_secret => $client_secret,
+ library_id => $library_id,
+ access_token => $token,
+ access_token_type => $token_type,
+ user_agent_params => { agent => $class->agent_string }
+ ) );
+ }
+ return $self;
+}
+
+=head1 METHODS
+
+L<WebService::ILS::OverDrive::Patron> methods used without mods:
+
+=over 4
+
+=item C<error_message()>
+
+=back
+
+C<WebService::ILS::OverDrive::Patron> methods with slightly moded interfaces:
+
+=head2 auth_url($base_url)
+
+=head2 auth_by_code($code, $base_url)
+
+=cut
+
+use constant AUTH_RETURN_HANDLER => "/cgi-bin/koha/external/overdrive/auth.pl";
+sub _return_url {
+ my $self = shift;
+ my $page_url = shift or croak "Page url not provided";
+
+ my ($base_url, $page) = ($page_url =~ m!^(https?://[^/]+)(.*)!);
+ my $return_url = $base_url.AUTH_RETURN_HANDLER;
+
+ return wantarray ? ($return_url, $page) : $return_url;
+}
+
+use constant RETURN_PAGE_SESSION_KEY => "overdrive.return_page";
+sub get_return_page_from_koha_session {
+ my $self = shift;
+ my $return_page = $self->get_from_koha_session(RETURN_PAGE_SESSION_KEY) || "";
+ $self->logger->debug("get_return_page_from_koha_session: $return_page");
+ return $return_page;
+}
+sub set_return_page_in_koha_session {
+ my $self = shift;
+ my $return_page = shift || "";
+ $self->logger->debug("set_return_page_in_koha_session: $return_page");
+ return $self->set_in_koha_session( RETURN_PAGE_SESSION_KEY, $return_page );
+}
+
+use constant ACCESS_TOKEN_SESSION_KEY => "overdrive.access_token";
+my $ACCESS_TOKEN_DELIMITER = ":";
+sub get_token_from_koha_session {
+ my $self = shift;
+ my ($token, $token_type)
+ = split $ACCESS_TOKEN_DELIMITER, $self->get_from_koha_session(ACCESS_TOKEN_SESSION_KEY) || "";
+ $self->logger->debug("get_token_from_koha_session: ".($token || "(none)"));
+ return ($token, $token_type);
+}
+sub set_token_in_koha_session {
+ my $self = shift;
+ my $token = shift || "";
+ my $token_type = shift || "";
+ $self->logger->debug("set_token_in_koha_session: $token $token_type");
+ return $self->set_in_koha_session(
+ ACCESS_TOKEN_SESSION_KEY,
+ join($ACCESS_TOKEN_DELIMITER, $token, $token_type)
+ );
+}
+
+sub is_logged_in {
+ my $self = shift;
+ my ($token, $token_type) = $self->get_token_from_koha_session();
+ $token ||= $self->auth_by_saved_token;
+ return $token;
+}
+
+sub auth_url {
+ my $self = shift;
+ my $page_url = shift or croak "Page url not provided";
+
+ my ($return_url, $page) = $self->_return_url($page_url);
+ $self->set_return_page_in_koha_session($page);
+ return $self->client->auth_url($return_url);
+}
+
+sub auth_by_code {
+ my $self = shift;
+ my $code = shift or croak "OverDrive auth code not provided";
+ my $base_url = shift or croak "App base url not provided";
+
+ my ($access_token, $access_token_type, $auth_token)
+ = $self->client->auth_by_code($code, $self->_return_url($base_url));
+ $access_token or die "Invalid OverDrive code returned";
+ $self->set_token_in_koha_session($access_token, $access_token_type);
+
+ $self->koha_patron->set({overdrive_auth_token => $auth_token})->store;
+ return $self->get_return_page_from_koha_session;
+}
+
+sub auth_by_saved_token {
+ my $self = shift;
+
+ my $koha_patron = $self->koha_patron;
+ if (my $auth_token = $koha_patron->overdrive_auth_token) {
+ my ($access_token, $access_token_type, $new_auth_token)
+ = $self->client->auth_by_token($auth_token);
+ $self->set_token_in_koha_session($access_token, $access_token_type);
+ $koha_patron->set({overdrive_auth_token => $new_auth_token})->store;
+ return $access_token;
+ }
+
+ return;
+}
+
+sub forget {
+ my $self = shift;
+
+ $self->set_token_in_koha_session("", "");
+ $self->koha_patron->update({overdrive_auth_token => undef});
+}
+
+use vars qw{$AUTOLOAD};
+sub AUTOLOAD {
+ my $self = shift;
+ (my $method = $AUTOLOAD) =~ s/.*:://;
+ my $od = $self->client;
+ local $@;
+ my $ret = eval { $od->$method(@_) };
+ if ($@) {
+ if ( $od->is_access_token_error($@) && $self->auth_by_saved_token ) {
+ return $od->$method(@_);
+ }
+ die $@;
+ }
+ return $ret;
+}
+sub DESTROY { }
+
+1;
diff --git a/Koha/Schema/Result/Borrower.pm b/Koha/Schema/Result/Borrower.pm
index 39880aa..6e434e1 100644
--- a/Koha/Schema/Result/Borrower.pm
+++ b/Koha/Schema/Result/Borrower.pm
@@ -576,6 +576,8 @@ __PACKAGE__->add_columns(
{ data_type => "integer", default_value => 1, is_nullable => 0 },
"privacy_guarantor_checkouts",
{ data_type => "tinyint", default_value => 0, is_nullable => 0 },
+ "overdrive_auth_token",
+ { data_type => "text", is_nullable => 1 },
);
=head1 PRIMARY KEY
diff --git a/installer/data/mysql/atomicupdate/overdrive.sql b/installer/data/mysql/atomicupdate/overdrive.sql
new file mode 100644
index 0000000..2ca5e8e
--- /dev/null
+++ b/installer/data/mysql/atomicupdate/overdrive.sql
@@ -0,0 +1 @@
+ALTER TABLE borrowers ADD overdrive_auth_token text default NULL AFTER privacy_guarantor_checkouts;
diff --git a/installer/data/mysql/kohastructure.sql b/installer/data/mysql/kohastructure.sql
index 63fefe7..b3d0d29 100644
--- a/installer/data/mysql/kohastructure.sql
+++ b/installer/data/mysql/kohastructure.sql
@@ -266,6 +266,7 @@ CREATE TABLE `borrowers` ( -- this table includes information about your patrons
`sms_provider_id` int(11) DEFAULT NULL, -- the provider of the mobile phone number defined in smsalertnumber
`privacy` integer(11) DEFAULT '1' NOT NULL, -- patron/borrower's privacy settings related to their reading history
`privacy_guarantor_checkouts` tinyint(1) NOT NULL DEFAULT '0', -- controls if relatives can see this patron's checkouts
+ overdrive_auth_token text default NULL, -- persist OverDrive auth token
UNIQUE KEY `cardnumber` (`cardnumber`),
PRIMARY KEY `borrowernumber` (`borrowernumber`),
KEY `categorycode` (`categorycode`),
diff --git a/opac/external/overdrive/auth.pl b/opac/external/overdrive/auth.pl
new file mode 100755
index 0000000..6ae7d17
--- /dev/null
+++ b/opac/external/overdrive/auth.pl
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+
+# script to log clicks on links to external urls
+
+# Copyright 2015 Catalyst IT
+# 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 <http://www.gnu.org/licenses>.
+
+use Modern::Perl;
+use CGI qw ( -utf8 );
+use URI;
+use URI::Escape;
+use C4::Auth qw(checkauth);
+use Koha::Logger;
+use Koha::ExternalContent::OverDrive;
+
+my $logger = Koha::Logger->get({ interface => 'opac' });
+my $cgi = new CGI;
+
+my ( $user, $cookie, $sessionID, $flags ) = checkauth( $cgi, 1, {}, 'opac' );
+my ($redirect_page, $error);
+if ($user && $sessionID) {
+ my $od = Koha::ExternalContent::OverDrive->new({ koha_session_id => $sessionID });
+ if ( my $auth_code = $cgi->param('code') ) {
+ my $base_url = $cgi->url(-base => 1);
+ local $@;
+ $redirect_page = eval { $od->auth_by_code($auth_code, $base_url) };
+ if ($@) {
+ $logger->error($@);
+ $error = $od->error_message($@);
+ }
+ }
+ else {
+ $error = "Missing OverDrive auth code";
+ }
+ $redirect_page ||= $od->get_return_page_from_koha_session;
+}
+else {
+ $error = "User not logged in";
+}
+$redirect_page ||= "/cgi-bin/koha/opac-user.pl";
+my $uri = URI->new($redirect_page);
+$uri->query_form( $uri->query_form, overdrive_tab => 1, overdrive_error => uri_escape($error || "") );
+print $cgi->redirect($redirect_page);
diff --git a/t/Koha_ExternalContent_OverDrive.t b/t/Koha_ExternalContent_OverDrive.t
new file mode 100755
index 0000000..bc6b133
--- /dev/null
+++ b/t/Koha_ExternalContent_OverDrive.t
@@ -0,0 +1,38 @@
+use Modern::Perl;
+
+use t::lib::Mocks;
+use Test::More tests => 5; # last test to print
+use C4::Context;
+
+my $context = C4::Context->new();
+local $@;
+eval { require WebService::ILS::OverDrive::Patron; }
+ or diag($@);
+SKIP: {
+ skip "cannot filnd WebService::ILS::OverDrive::Patron", 5 if $@;
+
+ use_ok('Koha::ExternalContent::OverDrive');
+
+ t::lib::Mocks::mock_preference('OverDriveClientKey', 'DUMMY');
+ t::lib::Mocks::mock_preference('OverDriveClientSecret', 'DUMMY');
+ t::lib::Mocks::mock_preference('OverDriveLibraryID', 'DUMMY');
+
+ my $client = Koha::ExternalContent::OverDrive->new();
+
+ my $user_agent_string = $client->user_agent->agent();
+ ok ($user_agent_string =~ m/^Koha/, 'User Agent string is set')
+ or diag("User Agent string: $user_agent_string");
+
+ my $base_url = "http://mykoha.org";
+ ok ($client->auth_url($base_url), 'auth_url()');
+ local $@;
+ eval { $client->auth_by_code("blah", $base_url) };
+ ok($@ =~ m/Invalid Access Token/i, "auth_by_code()")
+ or diag("Died: ".($@ || "nooooot"));
+ SKIP: {
+ skip "No exception", 1 unless $@;
+ my $error_message = $client->error_message($@);
+ ok($error_message =~ m/Invalid Access Token/i, "error_message()")
+ or diag("Original:\n$@\nTurned into:\n$error_message");
+ }
+}
--
1.9.1
More information about the Koha-patches
mailing list