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

(-)a/C4/Auth.pm (-1 / +10 lines)
Lines 34-40 use POSIX qw/strftime/; Link Here
34
use List::MoreUtils qw/ any /;
34
use List::MoreUtils qw/ any /;
35
35
36
# use utf8;
36
# use utf8;
37
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $ldap $cas $caslogout);
37
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $debug $googleoauth2 $ldap $cas $caslogout);
38
38
39
BEGIN {
39
BEGIN {
40
    sub psgi_env { any { /^psgi\./ } keys %ENV }
40
    sub psgi_env { any { /^psgi\./ } keys %ENV }
Lines 52-57 BEGIN { Link Here
52
                      ParseSearchHistorySession SetSearchHistorySession
52
                      ParseSearchHistorySession SetSearchHistorySession
53
                   );
53
                   );
54
    %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] );
54
    %EXPORT_TAGS = ( EditPermissions => [qw(get_all_subpermissions get_user_subpermissions)] );
55
    $googleoauth2= C4::Context->preference('GoogleOAuth2');
55
    $ldap        = C4::Context->config('useldapserver') || 0;
56
    $ldap        = C4::Context->config('useldapserver') || 0;
56
    $cas         = C4::Context->preference('casAuthentication');
57
    $cas         = C4::Context->preference('casAuthentication');
57
    $caslogout   = C4::Context->preference('casLogout');
58
    $caslogout   = C4::Context->preference('casLogout');
Lines 394-399 sub get_template_and_user { Link Here
394
            BranchesLoop              => GetBranchesLoop($opac_name),
395
            BranchesLoop              => GetBranchesLoop($opac_name),
395
            BranchCategoriesLoop      => GetBranchCategories( 'searchdomain', 1, $opac_name ),
396
            BranchCategoriesLoop      => GetBranchCategories( 'searchdomain', 1, $opac_name ),
396
            CalendarFirstDayOfWeek    => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1,
397
            CalendarFirstDayOfWeek    => (C4::Context->preference("CalendarFirstDayOfWeek") eq "Sunday")?0:1,
398
            GoogleOAuth2              => (C4::Context->preference("GoogleOAuth2")),
397
            LibraryName               => "" . C4::Context->preference("LibraryName"),
399
            LibraryName               => "" . C4::Context->preference("LibraryName"),
398
            LibraryNameTitle          => "" . $LibraryNameTitle,
400
            LibraryNameTitle          => "" . $LibraryNameTitle,
399
            LoginBranchname           => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
401
            LoginBranchname           => C4::Context->userenv?C4::Context->userenv->{"branchname"}:"",
Lines 1104-1109 sub checkauth { Link Here
1104
        );
1106
        );
1105
    }
1107
    }
1106
1108
1109
    if ($googleoauth2) {
1110
        if ($query->param("OAuth2Failed")) {
1111
            my $reason = $query->param('OAuth2Failed');
1112
            $template->param(invalidOAuth2Login => $reason);
1113
        }
1114
    }
1115
1107
    my $self_url = $query->url( -absolute => 1 );
1116
    my $self_url = $query->url( -absolute => 1 );
1108
    $template->param(
1117
    $template->param(
1109
        url         => $self_url,
1118
        url         => $self_url,
(-)a/installer/data/mysql/atomicupdate/bug_10988_add_GoogleOauth.sql (+2 lines)
Line 0 Link Here
1
-- Adding GoogleOAuth2 support
2
INSERT INTO `systempreferences` (`variable`, `value`, `options`, `explanation`, `type`) VALUES ('GoogleOAuth2', '0', NULL, 'if ON, allows the use of Google OAuth2 for login', 'YesNo'), ('GoogleOAuth2ClientID', '', NULL, 'Client ID for the web app registered with google', 'Free'), ('GoogleOAuth2ClientSecret', '', NULL, 'Client Secret for the web app registered with google', 'Free'), ('GoogleOAuth2Domain', '', NULL, 'Restrict OAuth2 to this domain. Leave blank for all google domains', 'Free');
(-)a/installer/data/mysql/sysprefs.sql (+4 lines)
Lines 119-124 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
119
('FRBRizeEditions','0','','If ON, Koha will query one or more ISBN web services for associated ISBNs and display an Editions tab on the details pages','YesNo'),
119
('FRBRizeEditions','0','','If ON, Koha will query one or more ISBN web services for associated ISBNs and display an Editions tab on the details pages','YesNo'),
120
('gist','0','','Default Goods and Services tax rate NOT in %, but in numeric form (0.12 for 12%), set to 0 to disable GST','Integer'),
120
('gist','0','','Default Goods and Services tax rate NOT in %, but in numeric form (0.12 for 12%), set to 0 to disable GST','Integer'),
121
('GoogleJackets','0',NULL,'if ON, displays jacket covers from Google Books API','YesNo'),
121
('GoogleJackets','0',NULL,'if ON, displays jacket covers from Google Books API','YesNo'),
122
('GoogleOAuth2', '0', NULL, 'if ON, allows the use of Google OAuth2 for login', 'YesNo'),
123
('GoogleOAuth2ClientID', '', NULL, 'Client ID for the web app registered with google', 'Free'),
124
('GoogleOAuth2ClientSecret', '', NULL, 'Client Secret for the web app registered with google', 'Free'),
125
('GoogleOAuth2Domain', '', NULL, 'Restrict OAuth2 to this domain. Leave blank for all google domains', 'Free'),
122
('hidelostitems','0','','If ON, disables display of\"lost\" items in OPAC.','YesNo'),
126
('hidelostitems','0','','If ON, disables display of\"lost\" items in OPAC.','YesNo'),
123
('HidePatronName','0','','If this is switched on, patron\'s cardnumber will be shown instead of their name on the holds and catalog screens','YesNo'),
127
('HidePatronName','0','','If this is switched on, patron\'s cardnumber will be shown instead of their name on the holds and catalog screens','YesNo'),
124
('hide_marc','0',NULL,'If ON, disables display of MARC fields, subfield codes & indicators (still shows data)','YesNo'),
128
('hide_marc','0',NULL,'If ON, disables display of MARC fields, subfield codes & indicators (still shows data)','YesNo'),
(-)a/installer/data/mysql/updatedatabase.pl (+7 lines)
Lines 8012-8017 if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) { Link Here
8012
    SetVersion($DBversion);
8012
    SetVersion($DBversion);
8013
}
8013
}
8014
8014
8015
$DBversion = "3.15.00.020";
8016
if ( CheckVersion($DBversion) ) {
8017
        $dbh->do("INSERT INTO systempreferences (variable,value,explanation,options,type) VALUES ('GoogleOAuth2', '0', NULL, 'if ON, allows the use of Google OAuth2 for login', 'YesNo'), ('GoogleOAuth2ClientID', '', NULL, 'Client ID for the web app registered with google', 'Free'),('GoogleOAuth2ClientSecret', '', NULL, 'Client Secret for the web app registered with google', 'Free'),('GoogleOAuth2Domain', '', NULL, 'Restrict OAuth2 to this domain. Leave blank for all google domains', 'Free')");
8018
        print "Upgrade to $DBversion done (Bug 10988: Allow login via Google OAuth2)\n";
8019
        SetVersion($DBversion);
8020
}
8021
8015
=head1 FUNCTIONS
8022
=head1 FUNCTIONS
8016
8023
8017
=head2 TableExists($table)
8024
=head2 TableExists($table)
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/admin.pref (+17 lines)
Lines 113-115 Administration: Link Here
113
                Solr: Solr
113
                Solr: Solr
114
                Zebra: Zebra
114
                Zebra: Zebra
115
            - is the search engine used.
115
            - is the search engine used.
116
    Google OAuth2:
117
        -
118
            - pref: GoogleOAuth2
119
              choices:
120
                yes: Use
121
                no: "Don't Use"
122
            - Google OAuth2 login.
123
            - You will need to select OAuth2 when creating an app in the google cloud console, and set the web origin to your_opac_url and the redirect url to your_opac_url/cgi-bin/koha/svc/oauthlogin .
124
        -
125
            - Google OAuth2 Client ID
126
            - pref: GoogleOAuth2ClientID
127
        -
128
            - Google OAuth2 Client Secret
129
            - pref: GoogleOAuth2ClientSecret
130
        -
131
            - Google OAuth2 Restrict to domain
132
            - pref: GoogleOAuth2Domain
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc (-2 / +12 lines)
Lines 58-63 Link Here
58
                                                <li role="presentation"><a href="/cgi-bin/koha/opac-shelves.pl?display=privateshelves" tabindex="-1" role="menuitem" class="listmenulink">New list</a></li>
58
                                                <li role="presentation"><a href="/cgi-bin/koha/opac-shelves.pl?display=privateshelves" tabindex="-1" role="menuitem" class="listmenulink">New list</a></li>
59
                                            [% END %]
59
                                            [% END %]
60
                                        [% ELSE %]
60
                                        [% ELSE %]
61
					    [% IF ( GoogleOAuth2 ) %]
62
                                               <li role="presentation"><a href="/cgi-bin/koha/svc/googleoauth2" tabindex="-1" class="menu-inactive" role="menuitem">Log in to create your own lists</a></li>
63
                                            [% ELSE %]
64
                                               <li role="presentation"><a href="/cgi-bin/koha/opac-user.pl" tabindex="-1" class="menu-inactive loginModal-trigger" role="menuitem">Log in to create your own lists</a></li>
65
				            [% END %]
66
61
                                            <li role="presentation"><a href="/cgi-bin/koha/opac-user.pl" tabindex="-1" class="menu-inactive loginModal-trigger" role="menuitem">Log in to create your own lists</a></li>
67
                                            <li role="presentation"><a href="/cgi-bin/koha/opac-user.pl" tabindex="-1" class="menu-inactive loginModal-trigger" role="menuitem">Log in to create your own lists</a></li>
62
                                        [% END # / IF loggedinusername %]
68
                                        [% END # / IF loggedinusername %]
63
                                    [% END # / IF opacuserlogin %]
69
                                    [% END # / IF opacuserlogin %]
Lines 71-77 Link Here
71
                        <div id="members">
77
                        <div id="members">
72
                            <ul class="nav pull-right">
78
                            <ul class="nav pull-right">
73
                                [% UNLESS ( loggedinusername ) %]
79
                                [% UNLESS ( loggedinusername ) %]
74
                                    <li><a href="/cgi-bin/koha/opac-user.pl" class="loginModal-trigger" role="button" data-toggle="modal">Log in to your account</a></li>
80
				    [% IF ( GoogleOAuth2 ) %]
81
			                <li><a href="/cgi-bin/koha/svc/googleoauth2" role="button" data-toggle="modal">Log in to your account</a></li>
82
				    [% ELSE %]
83
				           <li><a href="/cgi-bin/koha/opac-user.pl"  class="loginModal-trigger" role="button" data-toggle="modal">Log in to your account</a></li>
84
				    [% END %]
75
                                [% END %]
85
                                [% END %]
76
                                [% IF ( loggedinusername ) %]
86
                                [% IF ( loggedinusername ) %]
77
                                    <li><p class="members navbar-text">Welcome, <a href="/cgi-bin/koha/opac-user.pl"><span class="loggedinusername">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</span></a></p></li>
87
                                    <li><p class="members navbar-text">Welcome, <a href="/cgi-bin/koha/opac-user.pl"><span class="loggedinusername">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</span></a></p></li>
Lines 284-287 Link Here
284
                <a href="#" data-dismiss="modal" aria-hidden="true" class="cancel">Cancel</a>
294
                <a href="#" data-dismiss="modal" aria-hidden="true" class="cancel">Cancel</a>
285
            </div>
295
            </div>
286
        </form> <!-- /#auth -->
296
        </form> <!-- /#auth -->
287
    </div>  <!-- /#modalAuth  -->
297
    </div>  <!-- /#modalAuth  -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt (+9 lines)
Lines 84-89 Link Here
84
84
85
                            [% END # / IF casAuthentication %]
85
                            [% END # / IF casAuthentication %]
86
86
87
			    [% IF ( invalidOAuth2Login ) %]
88
				<h4>Automatic Login</h4>
89
				<p>Sorry, your automatic login failed. <span class="error">[% invalidOAuth2Login %]</span></p>
90
				<p>Please note that automatic login will only work if you are using the email address registered with this library.</p>
91
				<p>If you want to, you can try to <a href="/cgi-bin/koha/svc/googleoauth2?reauthenticate=select_account">login using a differen account</a>
92
				<h4>Local login</h4>
93
				<p>If you can't login automatically, you can still login in manually: </p>
94
			    [% END %]
95
87
                            <form action="[% url %]" name="auth" id="auth" method="post">
96
                            <form action="[% url %]" name="auth" id="auth" method="post">
88
                                <input type="hidden" name="koha_login_context" value="opac" />
97
                                <input type="hidden" name="koha_login_context" value="opac" />
89
                                <fieldset class="brief">
98
                                <fieldset class="brief">
(-)a/koha-tmpl/opac-tmpl/ccsr/en/includes/top-bar.inc (-1 / +5 lines)
Lines 55-61 Link Here
55
      [% IF ( opacuserlogin ) %]
55
      [% IF ( opacuserlogin ) %]
56
        <ul>
56
        <ul>
57
    [% UNLESS ( loggedinusername ) %]
57
    [% UNLESS ( loggedinusername ) %]
58
                   <li><a href="/cgi-bin/koha/opac-user.pl">Log in to your account</a></li>[% END %]
58
        [% IF ( GoogleOAuth2 ) %]
59
               <li><a href="/cgi-bin/koha/svc/googleoauth2">Log in to your account</a></li>
60
        [% ELSE %]
61
               <li><a href="/cgi-bin/koha/opac-user.pl">Log in to your account</a></li>
62
        [% END %]
59
                [% IF ( loggedinusername ) %]
63
                [% IF ( loggedinusername ) %]
60
                    <li><span class="members">Welcome, <a href="/cgi-bin/koha/opac-user.pl"><span class="loggedinusername">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</span></a></span></li>
64
                    <li><span class="members">Welcome, <a href="/cgi-bin/koha/opac-user.pl"><span class="loggedinusername">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</span></a></span></li>
61
65
(-)a/koha-tmpl/opac-tmpl/prog/en/includes/masthead.inc (-1 / +5 lines)
Lines 2-8 Link Here
2
  [% IF ( opacuserlogin ) %]
2
  [% IF ( opacuserlogin ) %]
3
	<ul>
3
	<ul>
4
[% UNLESS ( loggedinusername ) %]
4
[% UNLESS ( loggedinusername ) %]
5
               <li><a href="/cgi-bin/koha/opac-user.pl">Log in to your account</a></li>[% END %]
5
        [% IF ( GoogleOAuth2 ) %]
6
               <li><a href="/cgi-bin/koha/svc/googleoauth2">Log in to your account</a></li>
7
        [% ELSE %]
8
               <li><a href="/cgi-bin/koha/opac-user.pl">Log in to your account</a></li>
9
        [% END %]
6
            [% IF ( loggedinusername ) %]
10
            [% IF ( loggedinusername ) %]
7
                <li><span class="members">Welcome, <a href="/cgi-bin/koha/opac-user.pl"><span class="loggedinusername">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</span></a></span></li>
11
                <li><span class="members">Welcome, <a href="/cgi-bin/koha/opac-user.pl"><span class="loggedinusername">[% FOREACH USER_INF IN USER_INFO %][% USER_INF.title %] [% USER_INF.firstname %] [% USER_INF.surname %][% END %]</span></a></span></li>
8
12
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-auth.tt (+9 lines)
Lines 73-78 please choose against which one you would like to authenticate: </p> Link Here
73
73
74
[% END %]
74
[% END %]
75
75
76
[% IF ( invalidOAuth2Login ) %]
77
<h4>Automatic Login</h4>
78
<p>Sorry, your automatic login failed. <span class="error">[% invalidOAuth2Login %]</span></p>
79
<p>Please note that automatic login will only work if you are using the email address registered with this library.</p>
80
<p>If you want to, you can try to <a href="/cgi-bin/koha/svc/googleoauth2?reauthenticate=select_account">login using a differen account</a>
81
<h4>Local login</h4>
82
<p>If you can't login automatically, you can still login in manually: </p>
83
[% END %]
84
76
<form action="[% url %]" name="auth" id="auth" method="post">
85
<form action="[% url %]" name="auth" id="auth" method="post">
77
  <input type="hidden" name="koha_login_context" value="opac" />
86
  <input type="hidden" name="koha_login_context" value="opac" />
78
<fieldset class="brief">[% FOREACH INPUT IN INPUTS %]
87
<fieldset class="brief">[% FOREACH INPUT IN INPUTS %]
(-)a/opac/svc/googleoauth2 (-1 / +131 lines)
Line 0 Link Here
0
- 
1
#!/bin/perl -w
2
# Copyright chris@bigballofwax.co.nz 2013
3
# 
4
# This file is part of Koha.
5
# 
6
# Koha is free software; you can redistribute it and/or modify it under the
7
# terms of the GNU General Public License as published by the Free Software
8
# Foundation; either version 3 of the License, or (at your option) any later
9
# version.
10
# 
11
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
12
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14
# 
15
# You should have received a copy of the GNU General Public License along
16
# with Koha; if not, write to the Free Software Foundation, Inc.,
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
#
19
#
20
# Basic OAuth2 authentication for google goes like this
21
# First: get your clientid, clientsecret from google. At this stage, tell google that
22
# your redirect url is /cgi-bin/koha/svc/oauthlogin
23
#
24
# The first thing that happens when this script is called is that one gets redirected
25
# to an authentication url from google
26
#
27
# If successful, that then redirects back to this script, setting a CODE parameter
28
# which we use to look up a json authentication token. This token includes an encrypted 
29
# json id_token, which we round-trip back to google to decrypt. Finally, we can extract
30
# the email address from this.
31
#
32
# There is LOTS of room for improvement here.
33
# 1. move the clientid, clientsecret to systempreferences
34
# 2. use the OPACBaseURL preference to compute the redirecturl
35
# 4. Google recommends verifying and decrypting the id_token locally, which means caching
36
# some information and updating it daily. But that would make things a lot faster
37
38
use strict;
39
use warnings;
40
use CGI qw/escape/;
41
use C4::Auth;
42
use C4::Context;
43
use C4::Output;
44
45
use LWP::UserAgent; 
46
use HTTP::Request::Common qw{ POST };
47
use JSON;
48
49
my $scope="openid email";
50
my $host = C4::Context->preference('OPACBaseURL');
51
my $restricttodomain = C4::Context->preference('GoogleOAuth2Domain');
52
my $redirecturl = 'http://' . $host . '/cgi-bin/koha/svc/googleoauth2';
53
my $issuer = 'accounts.google.com';
54
my $clientid = C4::Context->preference('GoogleOAuth2ClientID');
55
my $clientsecret = C4::Context->preference('GoogleOAuth2ClientSecret');
56
57
my $query = new CGI;
58
59
sub loginfailed {
60
  my $query = shift;
61
  my $reason = shift;
62
  $query->delete('code');
63
  $query->param('OAuth2Failed'=>$reason);
64
  my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
65
    {
66
        template_name   => 'opac-user.tmpl',
67
        query           => $query,
68
        type            => 'opac',
69
        authnotrequired => 0,
70
        flagsrequired   => { borrow => 1 },
71
    }
72
  );
73
  $template->param( 'invalidOAuth2Login' => $reason );
74
  $template->param( 'loginprompt' => 1);
75
  output_html_with_http_headers $query, $cookie, $template->output;
76
}
77
#die $query->param('code');
78
if (defined $query->param('error')) {
79
	loginfailed($query, 'An authentication error occurred. (Error:' . $query->param('error') . ')');
80
} elsif (defined $query->param('code')) {
81
	my $code=$query->param('code');
82
	my $ua = LWP::UserAgent->new();
83
	my $request = POST('https://accounts.google.com/o/oauth2/token', [
84
                        code => $code,
85
                        client_id => $clientid,
86
                        client_secret => $clientsecret,
87
    	            	redirect_uri => $redirecturl,
88
                		grant_type => 'authorization_code',
89
                        $scope => $scope
90
    ]);
91
	my $response =  $ua->request($request)->decoded_content ;
92
	my $json = decode_json ( $response);
93
	if ( exists($json->{'id_token'}) ) {
94
		$request = POST('https://www.googleapis.com/oauth2/v1/tokeninfo', [
95
			id_token => $json->{'id_token'}
96
		]);
97
		$response = $ua->request($request)->decoded_content;
98
		$json = decode_json ( $response);
99
		# Confirm (as google suggests) that the issuer and audience are what we expect them to be
100
		if (($json->{'issuer'} eq $issuer) && ($json->{'audience'} eq $clientid) && exists($json->{'email'})) {
101
			my $email = $json->{'email'};
102
			my ( $userid, $cookie, $sessionID ) =
103
                            checkauth( $query, 1,  { borrow => 1 }, 'opac', $email );
104
			if ($userid) { # A valid user has logged in
105
				print $query->redirect( -uri => '/cgi-bin/koha/opac-user.pl', -cookie => $cookie );
106
			} else {
107
				loginfailed($query, 'The email address you are trying to use is not associated with a borrower in this library.');
108
			}
109
		} else { # something went wrong with getting appropriate credentials
110
			loginfailed($query, 'Failed to get proper credentials from google.');
111
		}
112
	} else { # Failed to get ID Token
113
		loginfailed($query, 'An authentication error occurred. (Error: No ID Token was supplied)');
114
	}	
115
116
} else {
117
    my  $prompt = '';
118
    $prompt = $query->param('reauthenticate') unless not (defined $query->param('reauthenticate'));
119
120
    my $authorisationurl =  'https://accounts.google.com/o/oauth2/auth?' .
121
                            'response_type=code&' .
122
                            'redirect_uri=' . escape ( $redirecturl ) . '&' .
123
                            'client_id=' . escape ( $clientid ) . '&' .
124
                            'scope=' . escape ( $scope ) . '&';
125
   if ( $restricttodomain ne '' ) {
126
      $authorisationurl .= 'hd=' . $restricttodomain . '&';
127
   }
128
    $authorisationurl .=    'prompt=' . escape ( $query->param('reauthenticate') );
129
	print $query->redirect($authorisationurl);
130
}
131

Return to bug 10988