Auth_with_ldap: work in progress.
[koha.git] / C4 / Auth_with_ldap.pm
1 # -*- tab-width: 8 -*-
2 # NOTE: This file uses 8-character tabs; do not change the tab size!
3
4 package C4::Auth;
5
6 # Copyright 2000-2002 Katipo Communications
7 #
8 # This file is part of Koha.
9 #
10 # Koha is free software; you can redistribute it and/or modify it under the
11 # terms of the GNU General Public License as published by the Free Software
12 # Foundation; either version 2 of the License, or (at your option) any later
13 # version.
14 #
15 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
16 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License along with
20 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
21 # Suite 330, Boston, MA  02111-1307 USA
22
23 use strict;
24 use Digest::MD5 qw(md5_base64);
25
26 require Exporter;
27 use C4::Context;
28 use C4::Output;    # to get the template
29 use C4::Members;
30
31 use Net::LDAP;
32 # use Net::LDAP qw(:all);
33
34 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
35
36 BEGIN {
37         $VERSION = 3.01;        # set the version for version checking
38         @ISA    = qw(Exporter C4::Auth);
39         @EXPORT = qw(&checkauth &get_template_and_user);
40 }
41
42 =head1 NAME
43
44 C4::Auth - Authenticates Koha users
45
46 =head1 SYNOPSIS
47
48   use CGI;
49   use C4::Auth;
50
51   my $query = new CGI;
52
53   my ($template, $borrowernumber, $cookie) 
54     = get_template_and_user({
55                                 template_name   => "opac-main.tmpl",
56                                 query           => $query,
57                                 type            => "opac",
58                                 authnotrequired => 1,
59                                 flagsrequired   => {circulate => 1},
60                           });
61
62   print $query->header(
63     -type => 'utf-8',
64     -cookie => $cookie
65   ), $template->output;
66
67 =head1 LDAP specific
68
69     This module is specific to LDAP authentification. It requires Net::LDAP package and a working LDAP server.
70         To use it :
71            * move initial Auth.pm elsewhere
72            * Search the string LOCAL
73            * modify the code between LOCAL and /LOCAL to fit your LDAP server parameters & fields
74            * rename this module to Auth.pm
75         That should be enough.
76
77 =head1 FUNCTIONS
78
79 =cut
80
81 # Redefine checkpw
82 # connects to LDAP (anonymous)
83 # retrieves $userid a-login
84 # then compares $password with a-weak
85 # then gets the LDAP entry
86 # and calls the memberadd if necessary
87
88 my %mapping = (
89         firstname     => 'givenName',
90         surname       => 'sn',
91         streetaddress => 'l',
92         branchcode    => 'branch',
93         emailaddress  => 'mail',
94         categorycode  => 'employeeType',
95         city          => 'null',
96         phone         => 'telephoneNumber',
97 );
98
99 sub checkpw {
100     my ($dbh, $userid, $password) = @_;
101     if (   $userid   eq C4::Context->config('user')
102         && $password eq C4::Context->config('pass') )
103     {
104         return 2;       # Koha superuser account
105     }
106     ##################################################
107     ### LOCAL
108     ### Change the code below to match your own LDAP server.
109     ##################################################
110     # LDAP connexion parameters
111     my $ldapserver = 'localhost';
112
113     # Infos to do an anonymous bind
114     my $name = "dc=metavore,dc=com";
115     my $db   = Net::LDAP->new($ldapserver);
116
117     # do an anonymous bind
118     my $res = $db->bind();
119     if ($res->code) {           # auth refused
120         warn "LDAP Auth impossible : server not responding";
121         return 0;
122     }
123         my $userdnsearch = $db->search(
124                 base   => $name,
125                 filter => "(a-login=$userid)",
126         );
127         if ( $userdnsearch->code || !( $userdnsearch->count eq 1 ) ) {
128                 warn "LDAP Auth rejected : user unknown in LDAP";
129                 return 0;
130         }
131
132         my $userldapentry = $userdnsearch->shift_entry;
133         my $cmpmesg = $db->compare( $userldapentry, attr => 'a-weak', value => $password );
134         if($cmpmesg->code != 6) {
135                 warn "LDAP Auth rejected : wrong password";
136                 return 0;
137         }
138
139         # build LDAP hash
140         my %memberhash;
141         my $x = $userldapentry->{asn}{attributes};
142         my $key;
143         foreach my $k (@$x) {
144                 foreach my $k2 ( keys %$k ) {
145                         if ($k2 eq 'type') {
146                                 $key = $$k{$k2};
147                         } else {
148                                 $memberhash{$key} .= map {$_ . " "} @$k{$k2};
149                         }
150                 }
151         }
152
153         # BUILD %borrower to CREATE or MODIFY BORROWER
154         # change $memberhash{'xxx'} to fit your ldap structure.
155         # check twice that mandatory fields are correctly filled
156         #
157         my %borrower;
158         $borrower{cardnumber} = $userid;
159         foreach my $key (%mapping) {
160                 my $data = $memberhash{$mapping{$key}}; 
161                 defined $data or $data = ' ';
162                 $borrower{$key} = ($data ne '') ? $data : ' ' ;
163         }
164         $borrower{initials}   =
165                 substr( $borrower{firstname}, 0, 1 )
166                 . substr( $borrower{surname}, 0, 1 )
167                 . "  ";                                          # MANDATORY FIELD
168 ##################################################
169 ### /LOCAL
170 ##################################################
171 # check if borrower exists (then modify, else add)
172         my $sth =
173         $dbh->prepare("select password from borrowers where cardnumber=?");
174         $sth->execute($userid);
175         if ( $sth->rows ) {
176                 #       warn "MODIFY borrower";
177                 my $sth2 = $dbh->prepare("
178 UPDATE borrowers set firstname=?,surname=?,initials=?,streetaddress=?,city=?,phone=?, categorycode=?,branchcode=?,emailaddress=?,sort1=?
179 WHERE cardnumber=?
180                 ");
181                 $sth2->execute(
182                         $borrower{firstname},    $borrower{surname},
183                         $borrower{initials},     $borrower{streetaddress},
184                         $borrower{city},         $borrower{phone},
185                         $borrower{categorycode}, $borrower{branchcode},
186                         $borrower{emailaddress}, $borrower{sort1},
187                         $userid
188                 );
189         } else {
190                 #       warn "ADD borrower";
191                 my $borrowerid = newmember(%borrower);
192         }
193
194         # CREATE or MODIFY PASSWORD/LOGIN
195         # search borrowerid
196         $sth = $dbh->prepare("SELECT borrowernumber from borrowers WHERE cardnumber=?");
197         $sth->execute($userid);
198         my ($borrowerid) = $sth->fetchrow;
199
200         #               warn "change password for $borrowerid setting $password";
201                 my $digest = md5_base64($password);
202                 changepassword( $userid, $borrowerid, $digest );
203
204         # INTERNAL AUTH
205         $sth = $dbh->prepare("SELECT password,cardnumber from borrowers WHERE userid=?");
206         $sth->execute($userid);
207         if ( $sth->rows ) {
208                 my ( $md5password, $cardnumber ) = $sth->fetchrow;
209         if ( md5_base64($password) eq $md5password ) {
210             return 1, $cardnumber;
211         }
212     }
213     $sth = $dbh->prepare("SELECT password from borrowers WHERE cardnumber=?");
214     $sth->execute($userid);
215     if ($sth->rows) {
216         my ($md5password) = $sth->fetchrow;
217         if ( md5_base64($password) eq $md5password ) {
218             return 1, $userid;
219         }
220     }
221     return 0;
222 }
223
224 END { }    # module clean-up code here (global destructor)
225 1;
226 __END__
227
228 =back
229
230 =head1 SEE ALSO
231
232 CGI(3)
233
234 C4::Output(3)
235
236 Digest::MD5(3)
237
238 =cut