Bug 16911: Koha::Patrons - Move ExtendMemberSubscriptionTo to ->extend_subscription
[koha.git] / Koha / AuthUtils.pm
1 package Koha::AuthUtils;
2
3 # Copyright 2013 Catalyst IT
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21 use Crypt::Eksblowfish::Bcrypt qw(bcrypt en_base64);
22 use Encode qw( encode is_utf8 );
23 use Fcntl qw/O_RDONLY/; # O_RDONLY is used in generate_salt
24 use List::MoreUtils qw/ any /;
25
26 use base 'Exporter';
27
28 our @EXPORT_OK   = qw(hash_password get_script_name);
29
30 =head1 NAME
31
32 Koha::AuthUtils - utility routines for authentication
33
34 =head1 SYNOPSIS
35
36     use Koha::AuthUtils qw/hash_password/;
37     my $hash = hash_password($password);
38
39 =head1 DESCRIPTION
40
41 This module provides utility functions related to managing
42 user passwords.
43
44 =head1 FUNCTIONS
45
46 =head2 hash_password
47
48     my $hash = Koha::AuthUtils::hash_password($password, $settings);
49
50 =cut
51
52 # Using Bcrypt method for hashing. This can be changed to something else in future, if needed.
53 sub hash_password {
54     my $password = shift;
55     $password = Encode::encode( 'UTF-8', $password )
56       if Encode::is_utf8($password);
57
58     # Generate a salt if one is not passed
59     my $settings = shift;
60     unless( defined $settings ){ # if there are no settings, we need to create a salt and append settings
61     # Set the cost to 8 and append a NULL
62         $settings = '$2a$08$'.en_base64(generate_salt('weak', 16));
63     }
64     # Encrypt it
65     return bcrypt($password, $settings);
66 }
67
68 =head2 generate_salt
69
70     my $salt = Koha::Auth::generate_salt($strength, $length);
71
72 =over
73
74 =item strength
75
76 For general password salting a C<$strength> of C<weak> is recommend,
77 For generating a server-salt a C<$strength> of C<strong> is recommended
78
79 'strong' uses /dev/random which may block until sufficient entropy is acheived.
80 'weak' uses /dev/urandom and is non-blocking.
81
82 =item length
83
84 C<$length> is a positive integer which specifies the desired length of the returned string
85
86 =back
87
88 =cut
89
90
91 # the implementation of generate_salt is loosely based on Crypt::Random::Provider::File
92 sub generate_salt {
93     # strength is 'strong' or 'weak'
94     # length is number of bytes to read, positive integer
95     my ($strength, $length) = @_;
96
97     my $source;
98
99     if( $length < 1 ){
100         die "non-positive strength of '$strength' passed to Koha::AuthUtils::generate_salt\n";
101     }
102
103     if( $strength eq "strong" ){
104         $source = '/dev/random'; # blocking
105     } else {
106         unless( $strength eq 'weak' ){
107             warn "unsuppored strength of '$strength' passed to Koha::AuthUtils::generate_salt, defaulting to 'weak'\n";
108         }
109         $source = '/dev/urandom'; # non-blocking
110     }
111
112     sysopen SOURCE, $source, O_RDONLY
113         or die "failed to open source '$source' in Koha::AuthUtils::generate_salt\n";
114
115     # $bytes is the bytes just read
116     # $string is the concatenation of all the bytes read so far
117     my( $bytes, $string ) = ("", "");
118
119     # keep reading until we have $length bytes in $strength
120     while( length($string) < $length ){
121         # return the number of bytes read, 0 (EOF), or -1 (ERROR)
122         my $return = sysread SOURCE, $bytes, $length - length($string);
123
124         # if no bytes were read, keep reading (if using /dev/random it is possible there was insufficient entropy so this may block)
125         next unless $return;
126         if( $return == -1 ){
127             die "error while reading from $source in Koha::AuthUtils::generate_salt\n";
128         }
129
130         $string .= $bytes;
131     }
132
133     close SOURCE;
134     return $string;
135 }
136
137 =head2 get_script_name
138
139 This returns the correct script name, for use in redirecting back to the correct page after showing
140 the login screen. It depends on details of the package Plack configuration, and should not be used
141 outside this context.
142
143 =cut
144
145 sub get_script_name {
146     # This is the method about.pl uses to detect Plack; now that two places use it, it MUST be
147     # right.
148     if ( ( any { /(^psgi\.|^plack\.)/i } keys %ENV ) && $ENV{SCRIPT_NAME} =~ m,^/(intranet|opac)(.*), ) {
149         return '/cgi-bin/koha' . $2;
150     } else {
151         return $ENV{SCRIPT_NAME};
152     }
153 }
154
155 1;
156
157 __END__
158
159 =head1 SEE ALSO
160
161 Crypt::Eksblowfish::Bcrypt(3)
162
163 =cut