1 package Koha
::AuthUtils
;
3 # Copyright 2013 Catalyst IT
5 # This file is part of Koha.
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.
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.
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>.
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 use String
::Random
qw( random_string );
31 our @EXPORT_OK = qw(hash_password get_script_name);
35 Koha::AuthUtils - utility routines for authentication
39 use Koha::AuthUtils qw/hash_password/;
40 my $hash = hash_password($password);
44 This module provides utility functions related to managing
51 my $hash = Koha::AuthUtils::hash_password($password, $settings);
55 # Using Bcrypt method for hashing. This can be changed to something else in future, if needed.
58 $password = Encode
::encode
( 'UTF-8', $password )
59 if Encode
::is_utf8
($password);
61 # Generate a salt if one is not passed
63 unless( defined $settings ){ # if there are no settings, we need to create a salt and append settings
64 # Set the cost to 8 and append a NULL
65 $settings = '$2a$08$'.en_base64
(generate_salt
('weak', 16));
68 return bcrypt
($password, $settings);
73 my $salt = Koha::Auth::generate_salt($strength, $length);
79 For general password salting a C<$strength> of C<weak> is recommend,
80 For generating a server-salt a C<$strength> of C<strong> is recommended
82 'strong' uses /dev/random which may block until sufficient entropy is achieved.
83 'weak' uses /dev/urandom and is non-blocking.
87 C<$length> is a positive integer which specifies the desired length of the returned string
94 # the implementation of generate_salt is loosely based on Crypt::Random::Provider::File
96 # strength is 'strong' or 'weak'
97 # length is number of bytes to read, positive integer
98 my ($strength, $length) = @_;
103 die "non-positive strength of '$strength' passed to Koha::AuthUtils::generate_salt\n";
106 if( $strength eq "strong" ){
107 $source = '/dev/random'; # blocking
109 unless( $strength eq 'weak' ){
110 warn "unsuppored strength of '$strength' passed to Koha::AuthUtils::generate_salt, defaulting to 'weak'\n";
112 $source = '/dev/urandom'; # non-blocking
115 sysopen SOURCE
, $source, O_RDONLY
116 or die "failed to open source '$source' in Koha::AuthUtils::generate_salt\n";
118 # $bytes is the bytes just read
119 # $string is the concatenation of all the bytes read so far
120 my( $bytes, $string ) = ("", "");
122 # keep reading until we have $length bytes in $strength
123 while( length($string) < $length ){
124 # return the number of bytes read, 0 (EOF), or -1 (ERROR)
125 my $return = sysread SOURCE
, $bytes, $length - length($string);
127 # if no bytes were read, keep reading (if using /dev/random it is possible there was insufficient entropy so this may block)
130 die "error while reading from $source in Koha::AuthUtils::generate_salt\n";
140 =head2 is_password_valid
142 my ( $is_valid, $error ) = is_password_valid( $password );
144 return $is_valid == 1 if the password match minPasswordLength and RequireStrongPassword conditions
145 otherwise return $is_valid == 0 and $error will contain the error ('too_short' or 'too_weak')
149 sub is_password_valid
{
151 my $minPasswordLength = C4
::Context
->preference('minPasswordLength');
152 $minPasswordLength = 3 if not $minPasswordLength or $minPasswordLength < 3;
153 if ( length($password) < $minPasswordLength ) {
154 return ( 0, 'too_short' );
156 elsif ( C4
::Context
->preference('RequireStrongPassword') ) {
157 return ( 0, 'too_weak' )
158 if $password !~ m
|(?
=.*\d
)(?
=.*[a
-z
])(?
=.*[A
-Z
]).{$minPasswordLength,}|;
160 return ( 0, 'has_whitespaces' ) if $password =~ m
[^\s
|\s
$];
164 =head2 generate_password
166 my password = generate_password();
168 Generate a password according to the minPasswordLength and RequireStrongPassword.
172 sub generate_password
{
173 my $minPasswordLength = C4
::Context
->preference('minPasswordLength');
174 $minPasswordLength = 8 if not $minPasswordLength or $minPasswordLength < 8;
176 my ( $password, $is_valid );
178 $password = random_string
('.' x
$minPasswordLength );
179 ( $is_valid, undef ) = is_password_valid
( $password );
180 } while not $is_valid;
185 =head2 get_script_name
187 This returns the correct script name, for use in redirecting back to the correct page after showing
188 the login screen. It depends on details of the package Plack configuration, and should not be used
189 outside this context.
193 sub get_script_name
{
194 # This is the method about.pl uses to detect Plack; now that two places use it, it MUST be
196 if ( ( any
{ /(^psgi\.|^plack\.)/i } keys %ENV ) && $ENV{SCRIPT_NAME
} =~ m
,^/(intranet
|opac
)(.*), ) {
197 return '/cgi-bin/koha' . $2;
199 return $ENV{SCRIPT_NAME
};
209 Crypt::Eksblowfish::Bcrypt(3)