Increment version for 3.22.17
[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 $VERSION = '1.01';
29 our @EXPORT_OK   = qw(hash_password get_script_name);
30
31 =head1 NAME
32
33 Koha::AuthUtils - utility routines for authentication
34
35 =head1 SYNOPSIS
36
37     use Koha::AuthUtils qw/hash_password/;
38     my $hash = hash_password($password);
39
40 =head1 DESCRIPTION
41
42 This module provides utility functions related to managing
43 user passwords.
44
45 =head1 FUNCTIONS
46
47 =head2 hash_password
48
49     my $hash = Koha::AuthUtils::hash_password($password, $settings);
50
51 =cut
52
53 # Using Bcrypt method for hashing. This can be changed to something else in future, if needed.
54 sub hash_password {
55     my $password = shift;
56     $password = Encode::encode( 'UTF-8', $password )
57       if Encode::is_utf8($password);
58
59     # Generate a salt if one is not passed
60     my $settings = shift;
61     unless( defined $settings ){ # if there are no settings, we need to create a salt and append settings
62     # Set the cost to 8 and append a NULL
63         $settings = '$2a$08$'.en_base64(generate_salt('weak', 16));
64     }
65     # Encrypt it
66     return bcrypt($password, $settings);
67 }
68
69 =head2 generate_salt
70
71     my $salt = Koha::Auth::generate_salt($strength, $length);
72
73 =over
74
75 =item strength
76
77 For general password salting a C<$strength> of C<weak> is recommend,
78 For generating a server-salt a C<$strength> of C<strong> is recommended
79
80 'strong' uses /dev/random which may block until sufficient entropy is acheived.
81 'weak' uses /dev/urandom and is non-blocking.
82
83 =item length
84
85 C<$length> is a positive integer which specifies the desired length of the returned string
86
87 =back
88
89 =cut
90
91
92 # the implementation of generate_salt is loosely based on Crypt::Random::Provider::File
93 sub generate_salt {
94     # strength is 'strong' or 'weak'
95     # length is number of bytes to read, positive integer
96     my ($strength, $length) = @_;
97
98     my $source;
99
100     if( $length < 1 ){
101         die "non-positive strength of '$strength' passed to Koha::AuthUtils::generate_salt\n";
102     }
103
104     if( $strength eq "strong" ){
105         $source = '/dev/random'; # blocking
106     } else {
107         unless( $strength eq 'weak' ){
108             warn "unsuppored strength of '$strength' passed to Koha::AuthUtils::generate_salt, defaulting to 'weak'\n";
109         }
110         $source = '/dev/urandom'; # non-blocking
111     }
112
113     sysopen SOURCE, $source, O_RDONLY
114         or die "failed to open source '$source' in Koha::AuthUtils::generate_salt\n";
115
116     # $bytes is the bytes just read
117     # $string is the concatenation of all the bytes read so far
118     my( $bytes, $string ) = ("", "");
119
120     # keep reading until we have $length bytes in $strength
121     while( length($string) < $length ){
122         # return the number of bytes read, 0 (EOF), or -1 (ERROR)
123         my $return = sysread SOURCE, $bytes, $length - length($string);
124
125         # if no bytes were read, keep reading (if using /dev/random it is possible there was insufficient entropy so this may block)
126         next unless $return;
127         if( $return == -1 ){
128             die "error while reading from $source in Koha::AuthUtils::generate_salt\n";
129         }
130
131         $string .= $bytes;
132     }
133
134     close SOURCE;
135     return $string;
136 }
137
138 =head2 get_script_name
139
140 This returns the correct script name, for use in redirecting back to the correct page after showing
141 the login screen. It depends on details of the package Plack configuration, and should not be used
142 outside this context.
143
144 =cut
145
146 sub get_script_name {
147     # This is the method about.pl uses to detect Plack; now that two places use it, it MUST be
148     # right.
149     if ( ( any { /(^psgi\.|^plack\.)/i } keys %ENV ) && $ENV{SCRIPT_NAME} =~ m,^/(intranet|opac)(.*), ) {
150         return '/cgi-bin/koha' . $2;
151     } else {
152         return $ENV{SCRIPT_NAME};
153     }
154 }
155
156 1;
157
158 __END__
159
160 =head1 SEE ALSO
161
162 Crypt::Eksblowfish::Bcrypt(3)
163
164 =cut