File Coverage

blib/lib/Data/Password/Simple.pm
Criterion Covered Total %
statement 44 64 68.7
branch 12 22 54.5
condition 3 11 27.2
subroutine 10 11 90.9
pod 4 4 100.0
total 73 112 65.1


line stmt bran cond sub pod time code
1             package Data::Password::Simple;
2              
3 3     3   117476 use 5.006;
  3         11  
  3         126  
4 3     3   23 use strict;
  3         4  
  3         215  
5 3     3   15 use warnings FATAL => 'all';
  3         10  
  3         136  
6              
7 3     3   15 use Carp;
  3         5  
  3         2420  
8              
9             =head1 NAME
10              
11             Data::Password::Simple provides a system of checking given strings against
12             password complexity requirements.
13              
14             =head2 Current features:
15              
16             =over
17              
18             =item Case-insensitive dictionary word checking.
19              
20             =item Minimum password length checking.
21              
22             =back
23              
24             =head1 VERSION
25              
26             Version 0.05
27              
28             =cut
29              
30             our $VERSION = '0.05';
31              
32              
33             =head1 SYNOPSIS
34              
35             use Data::Password::Simple;
36              
37             my $checker = Data::Password::Simple->new(
38             length => 6,
39             dictionary => '/usr/share/dict/words'
40             );
41              
42             $is_suitable = $checker->check($password) ? "yes" : "no";
43              
44             =head1 CLASS METHODS
45              
46             =head3 new
47              
48             =head4 Input
49              
50             =over
51              
52             =item length
53              
54             Optional. The minimum password length required. Supply C<0> to disable length
55             checking.
56              
57             Default is 6.
58              
59             =item dictionary
60              
61             Optional. Enables dictionary checking.
62              
63             Accepts either a word list, or a file location. B dictionary checking is
64             case-insensitive.
65              
66             Default is to disable dictionary checking
67              
68             =back
69              
70             =head4 Output
71              
72             =over
73              
74             Returns a Data::Password::Simple object.
75              
76             =back
77              
78             =cut
79              
80             sub new {
81 2     2 1 307 my $package = shift;
82 2         7 my %params = @_;
83 2         5 my %self;
84              
85 2         7 $self{_default_length} = 6;
86              
87 2 100       12 if ($params{dictionary}) {
88 1         5 my $dict = _load_dict( $params{dictionary} );
89 1   50     8 $self{_dictionary} = $dict // undef;
90             }
91              
92 2   66     15 $self{_length} = $params{length} // $self{_default_length};
93              
94 2         12 return bless (\%self, $package);
95             }
96              
97             =head1 OBJECT METHODS
98              
99             =head3 dictionary
100              
101             Set or unset the dictionary used for word checking.
102              
103             =head4 Input
104              
105             =over
106              
107             Expects a either a list, a file location scalar or undef
108              
109             Setting undef disables dictionary checking. Setting a dictionary enables it.
110              
111             =back
112              
113             =head4 Output
114              
115             =over
116              
117             Returns true value if the dictionary is successfully updated, a false value
118             otherwise.
119              
120             =back
121              
122             =cut
123              
124             sub dictionary {
125 0     0 1 0 my $self = shift;
126 0         0 my $replacement = shift;
127              
128             # Return true if dictionary is being set undef
129 0 0       0 if (!$replacement) { return 1 }
  0         0  
130              
131 0         0 $self->{_dictionary} = _load_dict($replacement);
132              
133             # If a dictionary was given,
134 0 0       0 if ( %{ $self->{_dictionary} } ) {
  0         0  
135 0         0 return 1;
136             }
137              
138 0         0 return;
139             }
140              
141             =head3 required_length
142              
143             Access the minimum required password length.
144              
145             =head4 Input
146              
147             =over
148              
149             Optional. The new minimum password length required.
150              
151             =back
152              
153             =head4 Output
154              
155             =over
156              
157             Returns new minimum length value or current value if no new value is suppplied.
158              
159             =back
160              
161             =cut
162              
163             sub required_length {
164 2     2 1 9 my $self = shift;
165 2         3 my $length = shift;
166              
167 2 50       8 if ($length) {
168 0         0 $self->{_length} = $length;
169             }
170              
171 2         14 return $self->{_length};
172             }
173              
174             =head3 check
175              
176             Checks a given password against the specified criteria
177              
178             =head4 Input
179              
180             =over
181              
182             Expects a scalar password
183              
184             =back
185              
186             =head4 Output
187              
188             =over
189              
190             =item OK
191              
192             Returns a true value if the password is okay.
193              
194             =back
195              
196             If called in a list context, also returns:
197              
198             =over
199              
200             =item status
201              
202             =over
203              
204             =item acceptable
205              
206             Supplied with true value if the password meets requirements.
207              
208             =item error
209              
210             Only provided when the password fails to meet requirements.
211              
212             Contains the following:
213              
214             =over
215              
216             =item too_short
217              
218             True value if the password is too short to meet the current length requirement.
219              
220             =item in_dictionary
221              
222             True value if the password matches a dictionary word.
223              
224             =back
225              
226             =back
227              
228             =back
229              
230             =cut
231              
232             sub check {
233 50     50 1 86132 my $self = shift;
234 50         84 my $password = shift;
235              
236             # Create an extensible list of tests
237             my $checks = {
238 50     50   236 too_short => sub { $self->{_length} > length shift },
239 50     50   269 in_dictionary => sub { exists $self->{_dictionary}{lc shift} },
240 50         340 };
241              
242 50         83 my %error;
243 50         68 for my $check ( keys %{$checks} ) {
  50         170  
244 100 100       352 $error{$check} = 1 if $checks->{$check}->($password);
245             }
246              
247 50 100       141 if (%error) {
248             return wantarray
249 23 100       218 ? ( 0, { error => \%error } )
250             : 0
251             ;
252             }
253              
254             return wantarray
255 27 100       257 ? ( 1, { acceptable => 1 } )
256             : 1
257             ;
258             }
259              
260              
261             # Loads and returns a dictionary hash from a file location or an arrayref
262             sub _load_dict {
263 1     1   2 my $source = shift;
264 1         2 my %dict;
265              
266             # Create a dictionary from a given list.
267 1 50       6 if ( ref ($source) eq 'ARRAY' ) {
268 1         2 for my $word ( @{$source} ) {
  1         4  
269 6         17 $dict{$word} = 1;
270             }
271 1         4 return \%dict;
272             }
273              
274             # Or assume we've been given a file name.
275             # Make sure it's actually a file.
276 0 0 0       if ( !-e $source || !-r _ || -d _ ) {
      0        
277 0           carp "$source does not exist, is not readable, or is not a file";
278 0           return;
279             }
280              
281 0 0         open (my $dict_fh, '<', $source) or do {
282 0           carp "Failed to open $source for reading";
283 0           return;
284             };
285              
286 0           while (<$dict_fh>) {
287 0           chomp;
288 0           $dict{$_} = 1;
289             }
290              
291 0           return \%dict;
292             }
293              
294             =head1 AUTHOR
295              
296             Ross Hayes, C<< >>
297              
298             =head1 BUGS
299              
300             Please report any bugs or feature requests to C, or through
301             the web interface at L. I will be notified, and then you'll
302             automatically be notified of progress on your bug as I make changes.
303              
304              
305              
306              
307             =head1 SUPPORT
308              
309             You can find documentation for this module with the perldoc command.
310              
311             perldoc Data::Password::Simple
312              
313              
314             You can also look for information at:
315              
316             =over 4
317              
318             =item * RT: CPAN's request tracker (report bugs here)
319              
320             L
321              
322             =item * AnnoCPAN: Annotated CPAN documentation
323              
324             L
325              
326             =item * CPAN Ratings
327              
328             L
329              
330             =item * Search CPAN
331              
332             L
333              
334             =back
335              
336              
337             =head1 ACKNOWLEDGEMENTS
338              
339              
340             =head1 LICENSE AND COPYRIGHT
341              
342             Copyright 2013 Ross Hayes.
343              
344             This program is free software; you can redistribute it and/or modify it
345             under the terms of the the Artistic License (2.0). You may obtain a
346             copy of the full license at:
347              
348             L
349              
350             Any use, modification, and distribution of the Standard or Modified
351             Versions is governed by this Artistic License. By using, modifying or
352             distributing the Package, you accept this license. Do not use, modify,
353             or distribute the Package, if you do not accept this license.
354              
355             If your Modified Version has been derived from a Modified Version made
356             by someone other than you, you are nevertheless required to ensure that
357             your Modified Version complies with the requirements of this license.
358              
359             This license does not grant you the right to use any trademark, service
360             mark, tradename, or logo of the Copyright Holder.
361              
362             This license includes the non-exclusive, worldwide, free-of-charge
363             patent license to make, have made, use, offer to sell, sell, import and
364             otherwise transfer the Package with respect to any patent claims
365             licensable by the Copyright Holder that are necessarily infringed by the
366             Package. If you institute patent litigation (including a cross-claim or
367             counterclaim) against any party alleging that the Package constitutes
368             direct or contributory patent infringement, then this Artistic License
369             to you shall terminate on the date that such litigation is filed.
370              
371             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
372             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
373             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
374             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
375             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
376             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
377             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
378             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
379              
380              
381             =cut
382              
383             1; # End of Data::Password::Simple