File Coverage

blib/lib/Data/Password/passwdqc.pm
Criterion Covered Total %
statement 28 28 100.0
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 37 37 100.0


line stmt bran cond sub pod time code
1             package Data::Password::passwdqc;
2              
3 6     6   322913 use strict;
  6         15  
  6         170  
4 6     6   31 use warnings;
  6         11  
  6         175  
5              
6 6     6   1803 use POSIX qw(INT_MAX);
  6         30636  
  6         28  
7 6     6   8764 use List::MoreUtils qw(none);
  6         40282  
  6         56  
8 6     6   3739 use Carp qw(croak);
  6         14  
  6         337  
9 6     6   2217 use Moose;
  6         2494512  
  6         39  
10 6     6   40526 use Moose::Util::TypeConstraints;
  6         18  
  6         54  
11 6     6   14467 use namespace::autoclean;
  6         39419  
  6         35  
12              
13             our $VERSION = '0.09';
14              
15             require XSLoader;
16             XSLoader::load('Data::Password::passwdqc', $VERSION);
17              
18              
19             subtype 'Data::Password::passwdqc::ArrayRefOfInts',
20             as 'ArrayRef[Int]',
21             where {
22             my @min = @{ $_ };
23             @min == 5 && none(sub { $_ > INT_MAX }, @min) && none (sub { $_ && $min[$_] > $min[$_ - 1] }, 0 .. $#min);
24             };
25              
26             coerce 'Data::Password::passwdqc::ArrayRefOfInts',
27             from 'ArrayRef[Int|Undef]',
28             via { [ map { defined() ? $_ : INT_MAX } @{ $_ } ] };
29              
30             enum 'Data::Password::passwdqc::OneOrZero', [ 1, 0 ];
31              
32             coerce 'Data::Password::passwdqc::OneOrZero',
33             from 'Bool',
34             via { $_ && 1 || 0 };
35              
36             has 'min' => (
37             is => 'rw',
38             isa => 'Data::Password::passwdqc::ArrayRefOfInts',
39             default => sub { [INT_MAX, 24, 11, 8, 7] },
40             trigger => sub { $_[0]->_clear_params },
41             coerce => 1,
42             );
43              
44             has 'max' => (
45             is => 'rw',
46             isa => subtype( 'Int' => where { $_ >= 8 && $_ <= INT_MAX } ),
47             default => 40,
48             trigger => sub { $_[0]->_clear_params },
49             );
50              
51             has 'passphrase_words' => (
52             is => 'rw',
53             isa => subtype( 'Int' => where { $_ <= INT_MAX } ),
54             default => 3,
55             trigger => sub { $_[0]->_clear_params },
56             );
57              
58             has 'match_length' => (
59             is => 'rw',
60             isa => subtype( 'Int' => where { $_ <= INT_MAX } ),
61             default => 4,
62             trigger => sub { $_[0]->_clear_params },
63             );
64              
65             has 'similar_deny' => (
66             is => 'rw',
67             isa => 'Data::Password::passwdqc::OneOrZero',
68             default => 1,
69             trigger => sub { $_[0]->_clear_params },
70             coerce => 1,
71             );
72              
73             has 'random_bits' => (
74             is => 'rw',
75             isa => subtype( 'Int' => where { $_ == 0 || $_ >= 24 && $_ <= 85 } ),
76             default => 47,
77             trigger => sub { $_[0]->_clear_params },
78             );
79              
80             has '_params' => (
81             is => 'rw',
82             clearer => '_clear_params',
83             lazy => 1,
84             builder => '_build_params',
85             init_arg => undef,
86             );
87              
88             has 'reason' => (
89             is => 'rw',
90             clearer => '_clear_reason',
91             init_arg => undef,
92             );
93              
94              
95             sub _build_params {
96 3     3   9 my $self = shift;
97            
98 3         6 my $params = pack 'i*', @{ $self->min }, $self->max,
  3         96  
99             $self->passphrase_words, $self->match_length,
100             $self->similar_deny, $self->random_bits;
101 3         71 return $params;
102             }
103              
104              
105             sub validate_password {
106             my $self = shift;
107              
108             my $reason = password_check($self->_params, @_);
109              
110             if ($reason) {
111             $self->reason($reason);
112             return !1;
113             }
114              
115             return !0;
116             }
117              
118             sub generate_password {
119             my $self = shift;
120              
121             my $pass = password_generate($self->_params);
122             croak 'Failed to generate password' unless defined $pass;
123              
124             return $pass;
125             }
126              
127             before [ qw(validate_password generate_password) ] => sub { $_[0]->_clear_reason };
128              
129             __PACKAGE__->meta->make_immutable;
130              
131             1;
132              
133             __END__
134              
135             =head1 NAME
136              
137             Data::Password::passwdqc - Check password strength and generate password using passwdqc
138              
139             =head1 SYNOPSIS
140              
141             use Data::Password::passwdqc;
142              
143             my $pwdqc = Data::Password::passwdqc->new;
144             print 'OK' if $pwdqc->validate_password('arrive+greece7glove');
145              
146             my $is_valid = $pwdqc->validate_password('new password', '0ld+pas$w0rd');
147             print 'Bad password: ' . $pwdqc->reason if not $is_valid;
148              
149             my $password = $pwdqc->generate_password;
150              
151             =head1 DESCRIPTION
152              
153             Data::Password::passwdqc provides an object oriented Perl interface to
154             Openwall Project's passwdqc. It allows you to check password strength
155             and also lets you generate quality controllable random password.
156              
157             =head1 ATTRIBUTES
158              
159             =over 4
160              
161             =item I<min [Int0, Int1, Int2, Int3, Int4]>
162              
163             Defaults to C<[undef, 24, 11, 8, 7]>.
164              
165             The minimum allowed password lengths for different kinds of passwords
166             and passphrases. C<undef> can be used to disallow passwords of a given
167             kind regardless of their length. Each subsequent number is required to
168             be no larger than the preceding one.
169              
170             Int0 is used for passwords consisting of characters from one character
171             class only. The character classes are: digits, lower-case letters,
172             upper-case letters, and other characters. There is also a special class
173             for non-ASCII characters, which could not be classified, but are assumed
174             to be non-digits.
175              
176             Int1 is used for passwords consisting of characters from two character
177             classes that do not meet the requirements for a passphrase.
178              
179             Int2 is used for passphrases. Note that besides meeting this length
180             requirement, a passphrase must also consist of a sufficient number of
181             words (see the C<passphrase_words> option below).
182              
183             Int3 and Int4 are used for passwords consisting of characters from three
184             and four character classes, respectively.
185              
186             When calculating the number of character classes, upper-case letters
187             used as the first character and digits used as the last character of a
188             password are not counted.
189              
190             In addition to being sufficiently long, passwords are required to contain
191             enough different characters for the character classes and the minimum
192             length they have been checked against.
193              
194             =item I<max Int>
195              
196             Defaults to 40.
197              
198             The maximum allowed password length. This can be used to prevent users
199             from setting passwords that may be too long for some system services.
200              
201             The value 8 is treated specially: with C<max=8>, passwords longer than 8
202             characters will not be rejected, but will be truncated to 8 characters for
203             the strength checks and the user will be warned. This is to be used with
204             the traditional DES-based password hashes, which truncate the password
205             at 8 characters.
206              
207             It is important that you do set C<max=8> if you are using the traditional
208             hashes, or some weak passwords will pass the checks.
209              
210             =item I<passphrase_words Int>
211              
212             Defaults to 3.
213              
214             The number of words required for a passphrase, or 0 to disable the
215             support for user-chosen passphrases.
216              
217             =item I<match_length Int>
218              
219             Defaults to 4.
220              
221             The length of common substring required to conclude that a password is at
222             least partially based on information found in a character string, or 0 to
223             disable the substring search. Note that the password will not be rejected
224             once a weak substring is found; it will instead be subjected to the
225             usual strength requirements with the weak substring partially discounted.
226              
227             The substring search is case-insensitive and is able to detect and remove
228             a common substring spelled backwards.
229              
230             =item I<random_bits Int>
231              
232             Defaults to 47.
233              
234             The size of randomly-generated passphrases in bits (24 to 85), or 0 to
235             disable this feature.
236              
237             =back
238              
239             =head1 METHODS
240              
241             =over 4
242              
243             =item B<validate_password>
244              
245             $is_valid = $pwdqc->validate_password('new password');
246             $is_valid = $pwdqc->validate_password('new password', 'old password');
247             $is_valid = $pwdqc->validate_password('new password', 'old password', 'username');
248             $is_valid = $pwdqc->validate_password('new password', 'old password', 'username', 'real name');
249             print $pwdqc->reason if not $is_valid;
250              
251             Checks passphrase quality. Returns a true value on success. If the check
252             fails, it returns a false value and sets C<reason>.
253              
254             =item B<generate_password>
255              
256             my $password = $pwdqc->generate_password;
257              
258             Generates a random passphrase. Throws an exception if passphrase cannot
259             be generated.
260              
261             =back
262              
263             =head1 AUTHORS
264              
265             Sherwin Daganato E<lt>sherwin@daganato.comE<gt>
266              
267             The copy of passwdqc bundled with this module was written by Solar Designer and Dmitry V. Levin.
268              
269             =head1 CONTRIBUTORS
270              
271             dhardison: Dylan William Hardison <dhardison@cpan.org>
272              
273             srezic: Slaven Rezic <srezic@cpan.org>
274              
275             =head1 LICENSE
276              
277             This library is free software; you can redistribute it and/or modify
278             it under the same terms as Perl itself.
279              
280             =head1 SEE ALSO
281              
282             L<http://www.openwall.com/passwdqc/>
283              
284             =cut