| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Data::Transpose::PasswordPolicy; |
|
2
|
|
|
|
|
|
|
|
|
3
|
5
|
|
|
5
|
|
29028
|
use 5.010001; |
|
|
5
|
|
|
|
|
18
|
|
|
4
|
5
|
|
|
5
|
|
69
|
use strict; |
|
|
5
|
|
|
|
|
11
|
|
|
|
5
|
|
|
|
|
157
|
|
|
5
|
5
|
|
|
5
|
|
26
|
use warnings; |
|
|
5
|
|
|
|
|
16
|
|
|
|
5
|
|
|
|
|
185
|
|
|
6
|
|
|
|
|
|
|
# use Data::Dumper; |
|
7
|
|
|
|
|
|
|
|
|
8
|
5
|
|
|
5
|
|
824
|
use Moo; |
|
|
5
|
|
|
|
|
17902
|
|
|
|
5
|
|
|
|
|
37
|
|
|
9
|
|
|
|
|
|
|
extends 'Data::Transpose::Validator::Base'; |
|
10
|
5
|
|
|
5
|
|
5393
|
use MooX::Types::MooseLike::Base qw(:all); |
|
|
5
|
|
|
|
|
9007
|
|
|
|
5
|
|
|
|
|
2367
|
|
|
11
|
5
|
|
|
5
|
|
1068
|
use namespace::clean; |
|
|
5
|
|
|
|
|
14538
|
|
|
|
5
|
|
|
|
|
39
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 NAME |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Data::Transpose::PasswordPolicy - Perl extension to enforce password policy |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
use Data::Transpose::PasswordPolicy; |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
my %credentials = (username => "marco", |
|
24
|
|
|
|
|
|
|
password => "My.very.very.5strong.pzwd" |
|
25
|
|
|
|
|
|
|
); |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
my $pv = Data::Transpose::PasswordPolicy->new(\%credentials) |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
if (my $password = $pv->is_valid) { |
|
30
|
|
|
|
|
|
|
print "$password is OK"; |
|
31
|
|
|
|
|
|
|
} |
|
32
|
|
|
|
|
|
|
else { |
|
33
|
|
|
|
|
|
|
die $pv->error |
|
34
|
|
|
|
|
|
|
} |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
This module enforces the password policy, doing a number of checking. |
|
41
|
|
|
|
|
|
|
The author reccomends to use passphrases instead of password, using |
|
42
|
|
|
|
|
|
|
some special character (like punctuation) as separator, with 4-5 |
|
43
|
|
|
|
|
|
|
words in mixed case and with numbers as a good measure. |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
You can add the policy to the constructor, where C is the |
|
46
|
|
|
|
|
|
|
minimum password length, C is the maximum password and |
|
47
|
|
|
|
|
|
|
C is the minimum number of different characters in the |
|
48
|
|
|
|
|
|
|
password. Read below for C |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
By default all checkings are enabled. If you want to configure the |
|
51
|
|
|
|
|
|
|
policy, pass an hashref assigning to the disabled checking a true |
|
52
|
|
|
|
|
|
|
value. This will leave only the length checks in place, which you can |
|
53
|
|
|
|
|
|
|
tweak with the accessors. For example: |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
my %validate = ( username => "marco", |
|
59
|
|
|
|
|
|
|
password => "ciao", |
|
60
|
|
|
|
|
|
|
minlength => 10, |
|
61
|
|
|
|
|
|
|
maxlength => 50, |
|
62
|
|
|
|
|
|
|
patternlength => 4, |
|
63
|
|
|
|
|
|
|
mindiffchars => 5, |
|
64
|
|
|
|
|
|
|
disabled => { |
|
65
|
|
|
|
|
|
|
digits => 1, |
|
66
|
|
|
|
|
|
|
mixed => 1, |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
my $pv = Data::Transpose::PasswordPolicy->new(\%validate) |
|
69
|
|
|
|
|
|
|
$pv->is_valid ? "OK" : "not OK"; |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
See below for the list of the available checkings. |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
B: the purpose of this module is not to try to crack the |
|
75
|
|
|
|
|
|
|
password provided, but to set a policy for the passwords, which should |
|
76
|
|
|
|
|
|
|
have some minimum standards, and could be used on web services to stop |
|
77
|
|
|
|
|
|
|
users to set trivial password (without keeping the server busy for |
|
78
|
|
|
|
|
|
|
seconds while we check it). Nothing more. |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=cut |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head1 METHODS |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=cut |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=head2 new(%credentials) |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
Create a new Data::Transpose::PasswordPolicy object using the |
|
89
|
|
|
|
|
|
|
credentials provided to the constructor. |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=cut |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
has username => (is => 'rw', |
|
94
|
|
|
|
|
|
|
isa => Str); |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
has password => (is => 'rw', |
|
97
|
|
|
|
|
|
|
isa => Str); |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
around password => \&_strip_space_on_around; |
|
100
|
|
|
|
|
|
|
around username => \&_strip_space_on_around; |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub _strip_space_on_around { |
|
103
|
3550
|
|
|
3550
|
|
74422
|
my $orig = shift; |
|
104
|
3550
|
|
|
|
|
54977
|
my $ret = $orig->(@_); |
|
105
|
3550
|
50
|
|
|
|
18635
|
if (not defined $ret) { |
|
106
|
0
|
|
|
|
|
0
|
return ''; |
|
107
|
|
|
|
|
|
|
} |
|
108
|
|
|
|
|
|
|
else { |
|
109
|
3550
|
|
|
|
|
8758
|
$ret =~ s/^\s*//s; |
|
110
|
3550
|
|
|
|
|
18371
|
$ret =~ s/\s*$//s; |
|
111
|
3550
|
|
|
|
|
23588
|
return $ret; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
has maxlength => (is => 'rw', |
|
117
|
|
|
|
|
|
|
isa => Int, |
|
118
|
|
|
|
|
|
|
default => sub { 255 }, |
|
119
|
|
|
|
|
|
|
); |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
has minlength => (is => 'rw', |
|
122
|
|
|
|
|
|
|
isa => Int, |
|
123
|
|
|
|
|
|
|
default => sub { 12 }, |
|
124
|
|
|
|
|
|
|
); |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
has mindiffchars => (is => 'rw', |
|
128
|
|
|
|
|
|
|
isa => Int, |
|
129
|
|
|
|
|
|
|
default => sub { 6 }, |
|
130
|
|
|
|
|
|
|
); |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
has patternlength => (is => 'rw', |
|
133
|
|
|
|
|
|
|
isa => Int, |
|
134
|
|
|
|
|
|
|
default => sub { 3 }, |
|
135
|
|
|
|
|
|
|
); |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
has disabled => (is => 'rw', |
|
138
|
|
|
|
|
|
|
isa => HashRef, |
|
139
|
|
|
|
|
|
|
default => sub { {} }); |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=head1 ACCESSORS |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=head2 $obj->password($newpassword) |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
Set and return the new password. If no argument is provided, returns |
|
147
|
|
|
|
|
|
|
the current. It will strip leading and trailing spaces. |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head2 $obj->username($username) |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Set and return the new username. If no argument is provided, returns |
|
152
|
|
|
|
|
|
|
the current. It will strip leading and trailing spaces. |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head2 $obj->password_length |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
It returns the length of the password; |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=cut |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub password_length { |
|
161
|
737
|
|
|
737
|
1
|
2828
|
my $self = shift; |
|
162
|
737
|
|
|
|
|
14237
|
return length($self->password); |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=head2 $obj->minlength |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
Returns the minimum length required. If a numeric argument is |
|
168
|
|
|
|
|
|
|
provided, set that limit. Defaults to 255; |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=head2 $obj->maxlength |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
As above, but for the maximum. Defaults to 12; |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=head2 $obj->mindiffchars |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
As above, but set the minimum of different characters (to avoid things like |
|
177
|
|
|
|
|
|
|
00000000000000000ciao00000000000. |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Defaults to 6; |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=head2 $obj->patternlength |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
As above, but set the length of the common patterns we will search in |
|
184
|
|
|
|
|
|
|
the password, like "abcd", or "1234", or "asdf". By default it's 3, so |
|
185
|
|
|
|
|
|
|
a password which merely contains "abc" will be discarded. |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
This option can also be set in the constructor. |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head1 Internal algorithms |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
All the following methods operate on $obj->password and return the |
|
192
|
|
|
|
|
|
|
message of the error if something if not OK, while returning false if |
|
193
|
|
|
|
|
|
|
nothing suspicious was found. |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=head2 password_length_ok |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
Check if the password is in the range of permitted lengths. Return |
|
198
|
|
|
|
|
|
|
undef if the validation passes, otherwise the arrayref with the error |
|
199
|
|
|
|
|
|
|
code and the error string. |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=cut |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub password_length_ok { |
|
205
|
253
|
|
|
253
|
1
|
318
|
my $self = shift; |
|
206
|
253
|
100
|
100
|
|
|
393
|
if (($self->password_length >= $self->minlength) and |
|
207
|
|
|
|
|
|
|
($self->password_length <= $self->maxlength)) { |
|
208
|
245
|
|
|
|
|
2825
|
return undef; |
|
209
|
|
|
|
|
|
|
} else { |
|
210
|
8
|
|
50
|
|
|
2135
|
my $min = $self->minlength || 0; |
|
211
|
8
|
|
50
|
|
|
131
|
my $max = $self->maxlength || 0; |
|
212
|
8
|
|
50
|
|
|
1869
|
my $cur = $self->password_length || 0; |
|
213
|
8
|
100
|
|
|
|
25
|
if ($cur < $min) { |
|
214
|
7
|
|
|
|
|
62
|
return ["length" => "Wrong length (it should be long at least $min characters)"]; |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
else { |
|
217
|
1
|
|
|
|
|
6
|
return ["length" => "Password too long (max allowed $max)"]; |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
my %leetperms = ( |
|
226
|
|
|
|
|
|
|
'a' => qr{[4a]}, |
|
227
|
|
|
|
|
|
|
'b' => qr{[8b]}, |
|
228
|
|
|
|
|
|
|
'c' => "c", |
|
229
|
|
|
|
|
|
|
'd' => "d", |
|
230
|
|
|
|
|
|
|
'e' => qr{[3e]}, |
|
231
|
|
|
|
|
|
|
'f' => "f", |
|
232
|
|
|
|
|
|
|
'g' => "g", |
|
233
|
|
|
|
|
|
|
'h' => "h", |
|
234
|
|
|
|
|
|
|
'i' => qr{[1i]}, |
|
235
|
|
|
|
|
|
|
'j' => "j", |
|
236
|
|
|
|
|
|
|
'k' => "k", |
|
237
|
|
|
|
|
|
|
'l' => qr{[l1]}, |
|
238
|
|
|
|
|
|
|
'm' => "m", |
|
239
|
|
|
|
|
|
|
'n' => "n", |
|
240
|
|
|
|
|
|
|
'o' => qr{[0o]}, |
|
241
|
|
|
|
|
|
|
'p' => "p", |
|
242
|
|
|
|
|
|
|
'q' => "q", |
|
243
|
|
|
|
|
|
|
'r' => "r", |
|
244
|
|
|
|
|
|
|
's' => qr{[5s\$]}, |
|
245
|
|
|
|
|
|
|
't' => "t", |
|
246
|
|
|
|
|
|
|
'u' => "u", |
|
247
|
|
|
|
|
|
|
'v' => "v", |
|
248
|
|
|
|
|
|
|
'w' => "w", |
|
249
|
|
|
|
|
|
|
'x' => "x", |
|
250
|
|
|
|
|
|
|
'y' => "y", |
|
251
|
|
|
|
|
|
|
'z' => "z", |
|
252
|
|
|
|
|
|
|
'0' => qr{[o0]}, |
|
253
|
|
|
|
|
|
|
'1' => qr{[l1]}, |
|
254
|
|
|
|
|
|
|
'2' => "2", |
|
255
|
|
|
|
|
|
|
'3' => qr{[e3]}, |
|
256
|
|
|
|
|
|
|
'4' => qr{[4a]}, |
|
257
|
|
|
|
|
|
|
'5' => qr{[5s]}, |
|
258
|
|
|
|
|
|
|
'6' => "6", |
|
259
|
|
|
|
|
|
|
'7' => qr{[7t]}, |
|
260
|
|
|
|
|
|
|
'8' => qr{[8b]}, |
|
261
|
|
|
|
|
|
|
'9' => "9", |
|
262
|
|
|
|
|
|
|
); |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
my @toppassword = ( 'password', 'link', '1234', 'work', 'god', 'job', |
|
265
|
|
|
|
|
|
|
'angel', 'ilove', 'sex', 'jesus', 'connect', |
|
266
|
|
|
|
|
|
|
'f*ck', 'fu*k', 'monkey', 'master', 'bitch', 'dick', |
|
267
|
|
|
|
|
|
|
'micheal', 'jordan', 'dragon', 'soccer', 'killer', |
|
268
|
|
|
|
|
|
|
'4321', 'pepper', 'career', 'princess' ); |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=head2 password_has_username |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
Check if the password contains the username, even if obfuscated. |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
Disable keyword: C |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=cut |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# check if the password doesn't contain the username |
|
281
|
|
|
|
|
|
|
sub password_has_username { |
|
282
|
220
|
|
|
220
|
1
|
231
|
my $self = shift; |
|
283
|
220
|
50
|
|
|
|
4296
|
return [ username => "Missing username" ] unless $self->username; |
|
284
|
|
|
|
|
|
|
|
|
285
|
220
|
|
|
|
|
4335
|
my $match = _leet_string_match($self->password, $self->username); |
|
286
|
220
|
100
|
|
|
|
422
|
if ($match) { |
|
287
|
2
|
|
|
|
|
11
|
return [ username => "Found username $match in password" ]; |
|
288
|
|
|
|
|
|
|
} else { |
|
289
|
|
|
|
|
|
|
return undef |
|
290
|
218
|
|
|
|
|
676
|
} |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head2 password_has_common_password |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
Check if the password contains, even obfuscated, common password like |
|
296
|
|
|
|
|
|
|
"password" et similia. |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
Disable keyword: C |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# check if the password is in the top ten :-) |
|
304
|
|
|
|
|
|
|
sub password_has_common_password { |
|
305
|
234
|
|
|
234
|
1
|
243
|
my $self = shift; |
|
306
|
234
|
|
|
|
|
193
|
my @found; |
|
307
|
234
|
|
|
|
|
4515
|
my $password = $self->password; |
|
308
|
234
|
|
|
|
|
484
|
foreach my $common (@toppassword) { |
|
309
|
6084
|
100
|
|
|
|
7886
|
if (_leet_string_match($password, $common)) { |
|
310
|
7
|
|
|
|
|
18
|
push @found, $common; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
} |
|
313
|
234
|
100
|
|
|
|
454
|
if (@found) { |
|
314
|
|
|
|
|
|
|
# warn join(" ", @found) . "\n"; |
|
315
|
6
|
|
|
|
|
35
|
return [ common => "Found common password" ]; |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
else { |
|
318
|
228
|
|
|
|
|
879
|
return undef; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
} |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub _leet_string_match { |
|
323
|
6304
|
|
|
6304
|
|
7193
|
my ($string, $match) = @_; |
|
324
|
6304
|
50
|
33
|
|
|
20423
|
return "Missing parameter" unless ($string and $match); |
|
325
|
|
|
|
|
|
|
|
|
326
|
6304
|
|
|
|
|
6660
|
my $lcstring = lc($string); # the password |
|
327
|
6304
|
|
|
|
|
5307
|
my $lcmatch = lc($match); # the check |
|
328
|
6304
|
|
|
|
|
11966
|
my @chars = split(//, $lcmatch); # split the match |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
# for each character we look up the regexp or . |
|
331
|
6304
|
|
|
|
|
4618
|
my @regexps; |
|
332
|
6304
|
|
|
|
|
6688
|
foreach my $c (@chars) { |
|
333
|
32694
|
100
|
|
|
|
37750
|
if (exists $leetperms{$c}) { |
|
334
|
32226
|
|
|
|
|
38104
|
push @regexps, $leetperms{$c}; |
|
335
|
|
|
|
|
|
|
} else { |
|
336
|
468
|
|
|
|
|
505
|
push @regexps, "."; # unknown character |
|
337
|
|
|
|
|
|
|
} |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
# then we join it |
|
340
|
6304
|
|
|
|
|
9826
|
my $re = join("", @regexps); |
|
341
|
|
|
|
|
|
|
# and use it as re against the provided string |
|
342
|
|
|
|
|
|
|
# warn "checking $lcstring against $re\n"; |
|
343
|
6304
|
100
|
|
|
|
69261
|
if ($lcstring =~ m/$re/i) { |
|
344
|
|
|
|
|
|
|
# warn $re . "\n"; |
|
345
|
|
|
|
|
|
|
# return false if the re is present in the string |
|
346
|
9
|
|
|
|
|
42
|
return $lcmatch |
|
347
|
|
|
|
|
|
|
} else { |
|
348
|
6295
|
|
|
|
|
20994
|
return undef; |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=head2 password_has_enough_different_char |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
Check if the password has enough different characters. |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
Disable keyword: C |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=cut |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub password_has_enough_different_char { |
|
364
|
234
|
|
|
234
|
1
|
281
|
my $self = shift; |
|
365
|
234
|
|
|
|
|
226
|
my %found; |
|
366
|
234
|
|
|
|
|
4489
|
my @chars = split //, $self->password; |
|
367
|
234
|
|
|
|
|
280
|
my %consecutives; |
|
368
|
234
|
|
|
|
|
261
|
my $previous = ""; |
|
369
|
234
|
|
|
|
|
347
|
foreach my $c (@chars) { |
|
370
|
9079
|
|
|
|
|
8652
|
$found{$c}++; |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
# check previous char |
|
373
|
9079
|
100
|
|
|
|
12151
|
if ($previous eq $c) { |
|
374
|
305
|
|
|
|
|
383
|
$consecutives{$c}++; |
|
375
|
|
|
|
|
|
|
} |
|
376
|
9079
|
|
|
|
|
7665
|
$previous = $c; |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
# print Dumper(\%found); |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# check the number of chars |
|
381
|
234
|
|
|
|
|
372
|
my $totalchar = scalar(keys(%found)); |
|
382
|
234
|
100
|
|
|
|
4657
|
if ($totalchar <= $self->mindiffchars) { |
|
383
|
8
|
|
|
|
|
2039
|
return [ varchars => "Not enough different characters" ]; |
|
384
|
|
|
|
|
|
|
} |
|
385
|
|
|
|
|
|
|
|
|
386
|
226
|
|
|
|
|
2247
|
my %reportconsec; |
|
387
|
|
|
|
|
|
|
# check the consecutive chars; |
|
388
|
226
|
|
|
|
|
734
|
while (my ($k, $v) = each %consecutives) { |
|
389
|
221
|
100
|
|
|
|
1001
|
if ($v > 2) { |
|
390
|
7
|
|
|
|
|
33
|
$reportconsec{$k} = $v + 1; |
|
391
|
|
|
|
|
|
|
} |
|
392
|
|
|
|
|
|
|
} |
|
393
|
|
|
|
|
|
|
|
|
394
|
226
|
100
|
|
|
|
419
|
if (%reportconsec) { |
|
395
|
|
|
|
|
|
|
# we see if subtracting the number of total repetition, we are |
|
396
|
|
|
|
|
|
|
# still above the minimum chars. |
|
397
|
6
|
|
|
|
|
17
|
my $passwdlen = $self->password_length; |
|
398
|
6
|
|
|
|
|
22
|
foreach my $rep (values %reportconsec) { |
|
399
|
7
|
|
|
|
|
16
|
$passwdlen = $passwdlen - $rep; |
|
400
|
|
|
|
|
|
|
} |
|
401
|
6
|
100
|
|
|
|
159
|
if ($passwdlen < $self->minlength) { |
|
402
|
3
|
|
|
|
|
32
|
my $errstring = "Found too many repetitions, " |
|
403
|
|
|
|
|
|
|
. "lowering the effectivelength: " |
|
404
|
|
|
|
|
|
|
. (join(", ", (keys %reportconsec))); |
|
405
|
3
|
|
|
|
|
27
|
return [ varchars => $errstring ]; |
|
406
|
|
|
|
|
|
|
} |
|
407
|
|
|
|
|
|
|
} |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# given we have enough different characters, we check also there |
|
410
|
|
|
|
|
|
|
# are not some characters which are repeated too many times; |
|
411
|
|
|
|
|
|
|
# max dimension is 1/3 of the password |
|
412
|
223
|
|
|
|
|
448
|
my $maxrepeat = int($self->password_length / 3); |
|
413
|
|
|
|
|
|
|
# now get the hightest value; |
|
414
|
223
|
|
|
|
|
278
|
my $max = 0; |
|
415
|
223
|
|
|
|
|
647
|
foreach my $v (values %found) { |
|
416
|
4726
|
100
|
|
|
|
6949
|
$max = $v if ($v > $max); |
|
417
|
|
|
|
|
|
|
} |
|
418
|
223
|
100
|
|
|
|
373
|
if ($max > $maxrepeat) { |
|
419
|
3
|
|
|
|
|
62
|
return [ varchars => "Found too many repetitions" ]; |
|
420
|
|
|
|
|
|
|
} |
|
421
|
220
|
|
|
|
|
1831
|
return undef; |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=head2 password_has_mixed_chars |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
Check if the password has mixed cases |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
Disable keyword: C |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=cut |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
sub password_has_mixed_chars { |
|
434
|
224
|
|
|
224
|
1
|
229
|
my $self = shift; |
|
435
|
224
|
|
|
|
|
4242
|
my $pass = $self->password; |
|
436
|
224
|
100
|
100
|
|
|
1414
|
if (($pass =~ m/[a-z]/) and ($pass =~ m/[A-Z]/)) { |
|
437
|
|
|
|
|
|
|
return undef |
|
438
|
212
|
|
|
|
|
643
|
} else { |
|
439
|
12
|
|
|
|
|
60
|
return [ mixed => "No mixed case"]; |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
} |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=head2 password_has_specials |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
Check if the password has non-word characters |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
Disable keyword: C |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=cut |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
sub password_has_specials { |
|
453
|
234
|
|
|
234
|
1
|
254
|
my $self = shift; |
|
454
|
234
|
100
|
|
|
|
4557
|
if ($self->password =~ m/[\W_]/) { |
|
455
|
|
|
|
|
|
|
return undef |
|
456
|
224
|
|
|
|
|
682
|
} else { |
|
457
|
10
|
|
|
|
|
46
|
return [ specials => "No special characters" ]; |
|
458
|
|
|
|
|
|
|
} |
|
459
|
|
|
|
|
|
|
} |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head2 password_has_digits |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
Check if the password has digits |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
Disable keyword: C |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=cut |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
sub password_has_digits { |
|
471
|
224
|
|
|
224
|
1
|
229
|
my $self = shift; |
|
472
|
224
|
100
|
|
|
|
4179
|
if ($self->password =~ m/\d/) { |
|
473
|
|
|
|
|
|
|
return undef |
|
474
|
215
|
|
|
|
|
640
|
} else { |
|
475
|
9
|
|
|
|
|
44
|
return [ digits => "No digits in the password" ]; |
|
476
|
|
|
|
|
|
|
} |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=head2 password_has_letters |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Check if the password has letters |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
Disable keyword: C |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
=cut |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
sub password_has_letters { |
|
488
|
234
|
|
|
234
|
1
|
251
|
my $self = shift; |
|
489
|
234
|
100
|
|
|
|
4352
|
if ($self->password =~ m/[a-zA-Z]/) { |
|
490
|
|
|
|
|
|
|
return undef |
|
491
|
231
|
|
|
|
|
650
|
} else { |
|
492
|
3
|
|
|
|
|
13
|
return [letters => "No letters in the password" ]; |
|
493
|
|
|
|
|
|
|
} |
|
494
|
|
|
|
|
|
|
} |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=head2 password_has_patterns |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
Check if the password contains usual patterns like 12345, abcd, or |
|
499
|
|
|
|
|
|
|
asdf (like in the qwerty keyboard). |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
Disable keyword: C |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
=cut |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
my @patterns = ( |
|
506
|
|
|
|
|
|
|
[ qw/1 2 3 4 5 6 7 8 9 0/ ], |
|
507
|
|
|
|
|
|
|
[ ("a" .. "z") ], |
|
508
|
|
|
|
|
|
|
[ qw/q w e r t y u i o p/ ], |
|
509
|
|
|
|
|
|
|
[ qw/a s d f g h j k l/ ], |
|
510
|
|
|
|
|
|
|
[ qw/z x c v b n m/ ]); |
|
511
|
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
sub password_has_patterns { |
|
513
|
237
|
|
|
237
|
1
|
266
|
my $self = shift; |
|
514
|
237
|
|
|
|
|
4412
|
my $password = lc($self->password); |
|
515
|
237
|
|
|
|
|
281
|
my @found; |
|
516
|
237
|
|
|
|
|
4092
|
my $range = $self->patternlength - 1; |
|
517
|
237
|
|
|
|
|
4112
|
foreach my $row (@patterns) { |
|
518
|
1185
|
|
|
|
|
3095
|
my @pat = @$row; |
|
519
|
|
|
|
|
|
|
# we search a pattern of 3 consecutive keys, maybe 4 is reasonable enough |
|
520
|
1185
|
|
|
|
|
2272
|
for (my $i = 0; $i <= ($#pat - $range); $i++) { |
|
521
|
12269
|
|
|
|
|
9658
|
my $to = $i + $range; |
|
522
|
12269
|
|
|
|
|
13943
|
my $substring = join("", @pat[$i..$to]); |
|
523
|
12269
|
100
|
|
|
|
31379
|
if (index($password, $substring) >= 0) { |
|
524
|
11
|
|
|
|
|
30
|
push @found, $substring; |
|
525
|
|
|
|
|
|
|
} |
|
526
|
|
|
|
|
|
|
} |
|
527
|
|
|
|
|
|
|
} |
|
528
|
237
|
100
|
|
|
|
433
|
if (@found) { |
|
529
|
8
|
|
|
|
|
25
|
my $errstring = "Found common patterns: " . join(", ", @found); |
|
530
|
8
|
|
|
|
|
43
|
return [ patterns => $errstring ]; |
|
531
|
|
|
|
|
|
|
} else { |
|
532
|
229
|
|
|
|
|
839
|
return undef; |
|
533
|
|
|
|
|
|
|
} |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=head1 Main methods |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
=head2 $obj->is_valid |
|
540
|
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
Return the password if matches the policy or a false value if not. |
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
For convenience, this method can accept the password to validate as |
|
544
|
|
|
|
|
|
|
argument, which will overwrite the one provided with the C |
|
545
|
|
|
|
|
|
|
method (if it was set). |
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=cut |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
sub is_valid { |
|
552
|
254
|
|
|
254
|
1
|
14550
|
my $self = shift; |
|
553
|
254
|
|
|
|
|
317
|
my $password = shift; |
|
554
|
254
|
100
|
66
|
|
|
764
|
if (defined $password and $password ne "") { |
|
555
|
15
|
|
|
|
|
340
|
$self->password($password); |
|
556
|
|
|
|
|
|
|
} |
|
557
|
254
|
100
|
|
|
|
5143
|
unless ($self->password) { |
|
558
|
1
|
|
|
|
|
7
|
$self->error([missing => "Password is missing"]); |
|
559
|
1
|
|
|
|
|
5
|
return undef; |
|
560
|
|
|
|
|
|
|
} |
|
561
|
|
|
|
|
|
|
# reset the errors, we are going to do the checks anew; |
|
562
|
253
|
|
|
|
|
840
|
$self->reset_errors; |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
# To disable this, set the minimum to 1 and the max |
|
567
|
|
|
|
|
|
|
# to 255, but it makes no sense. |
|
568
|
253
|
|
|
|
|
9994
|
$self->error($self->password_length_ok); |
|
569
|
|
|
|
|
|
|
|
|
570
|
253
|
100
|
|
|
|
687
|
unless ($self->is_disabled("specials")) { |
|
571
|
234
|
|
|
|
|
3629
|
$self->error($self->password_has_specials); |
|
572
|
|
|
|
|
|
|
} |
|
573
|
|
|
|
|
|
|
|
|
574
|
253
|
100
|
|
|
|
626
|
unless ($self->is_disabled("digits")) { |
|
575
|
224
|
|
|
|
|
1461
|
$self->error($self->password_has_digits); |
|
576
|
|
|
|
|
|
|
} |
|
577
|
|
|
|
|
|
|
|
|
578
|
253
|
100
|
|
|
|
693
|
unless ($self->is_disabled("letters")) { |
|
579
|
234
|
|
|
|
|
1600
|
$self->error($self->password_has_letters); |
|
580
|
|
|
|
|
|
|
} |
|
581
|
|
|
|
|
|
|
|
|
582
|
253
|
100
|
|
|
|
554
|
unless ($self->is_disabled("username")) { |
|
583
|
220
|
|
|
|
|
1381
|
$self->error($self->password_has_username); |
|
584
|
|
|
|
|
|
|
} |
|
585
|
|
|
|
|
|
|
|
|
586
|
253
|
100
|
|
|
|
698
|
unless ($self->is_disabled("common")) { |
|
587
|
234
|
|
|
|
|
1567
|
$self->error($self->password_has_common_password); |
|
588
|
|
|
|
|
|
|
} |
|
589
|
|
|
|
|
|
|
|
|
590
|
253
|
100
|
|
|
|
592
|
unless ($self->is_disabled("varchars")) { |
|
591
|
234
|
|
|
|
|
1646
|
$self->error($self->password_has_enough_different_char); |
|
592
|
|
|
|
|
|
|
} |
|
593
|
|
|
|
|
|
|
|
|
594
|
253
|
100
|
|
|
|
639
|
unless ($self->is_disabled("mixed")) { |
|
595
|
224
|
|
|
|
|
1597
|
$self->error($self->password_has_mixed_chars); |
|
596
|
|
|
|
|
|
|
} |
|
597
|
|
|
|
|
|
|
|
|
598
|
253
|
100
|
|
|
|
647
|
unless ($self->is_disabled("patterns")) { |
|
599
|
237
|
|
|
|
|
1557
|
$self->error($self->password_has_patterns) |
|
600
|
|
|
|
|
|
|
} |
|
601
|
|
|
|
|
|
|
|
|
602
|
253
|
100
|
|
|
|
729
|
if ($self->error) { |
|
603
|
28
|
|
|
|
|
158
|
return undef; |
|
604
|
|
|
|
|
|
|
} else { |
|
605
|
225
|
|
|
|
|
4211
|
return $self->password; |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
} |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=head2 $obj->error |
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
With argument, set the error. Without, return the errors found in the |
|
613
|
|
|
|
|
|
|
password. |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
In list context, we pass the array with the error codes and the strings. |
|
616
|
|
|
|
|
|
|
In scalar context, we return the concatenated error strings. |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
Inherited from Data::Transpose::Validator::Base; |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=cut |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head2 error_codes |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
Return a list of the error codes found in the password. The error |
|
625
|
|
|
|
|
|
|
codes match the options. (e.g. C, C). |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
If you want the verbose string, you need the C method. |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=cut |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=head2 $obj->reset_errors |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
Clear the object from previous errors, in case you want to reuse it. |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
=cut |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=head2 $obj->disable("mixed", "letters", "digits", [...]) |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
Disable the checking(s) passed as list of strings. |
|
643
|
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
=cut |
|
645
|
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
sub disable { |
|
647
|
10
|
|
|
10
|
1
|
4227
|
my $self = shift; |
|
648
|
10
|
|
|
|
|
29
|
$self->_enable_or_disable_check("disable", @_); |
|
649
|
|
|
|
|
|
|
} |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=head2 $obj->enable("mixed", "letters", [...]) |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
Same as above, but enable the checking |
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
=cut |
|
656
|
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
sub enable { |
|
659
|
10
|
|
|
10
|
1
|
27
|
my $self = shift; |
|
660
|
10
|
|
|
|
|
21
|
$self->_enable_or_disable_check("enable", @_); |
|
661
|
|
|
|
|
|
|
} |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
sub _enable_or_disable_check { |
|
664
|
20
|
|
|
20
|
|
23
|
my $self = shift; |
|
665
|
20
|
|
|
|
|
19
|
my $action = shift; |
|
666
|
20
|
|
|
|
|
45
|
my @args = @_; |
|
667
|
20
|
|
|
|
|
24
|
my $set = 0; |
|
668
|
20
|
50
|
66
|
|
|
101
|
die "Wrong usage! internal only!\n" unless (($action eq 'enable') or |
|
669
|
|
|
|
|
|
|
($action eq 'disable')); |
|
670
|
|
|
|
|
|
|
|
|
671
|
20
|
50
|
|
|
|
51
|
if (@args) { |
|
672
|
20
|
|
|
|
|
30
|
foreach my $what (@args) { |
|
673
|
20
|
|
|
|
|
41
|
$self->_get_or_set_disable($what, $action); |
|
674
|
|
|
|
|
|
|
} |
|
675
|
|
|
|
|
|
|
} |
|
676
|
|
|
|
|
|
|
} |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=head2 $obj->is_disabled("checking") |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
Return true if the checking is disable. |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
=cut |
|
683
|
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
sub is_disabled { |
|
685
|
2024
|
|
|
2024
|
1
|
2640
|
my $self = shift; |
|
686
|
2024
|
|
|
|
|
1900
|
my $check = shift; |
|
687
|
2024
|
|
|
|
|
2804
|
return $self->_get_or_set_disable($check); |
|
688
|
|
|
|
|
|
|
} |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
sub _get_or_set_disable { |
|
691
|
2044
|
|
|
2044
|
|
2044
|
my ($self, $what, $action) = @_; |
|
692
|
2044
|
50
|
|
|
|
3281
|
return undef unless $what; |
|
693
|
2044
|
100
|
|
|
|
2942
|
unless ($action) { |
|
694
|
2024
|
|
|
|
|
34429
|
return $self->disabled->{$what} |
|
695
|
|
|
|
|
|
|
} |
|
696
|
20
|
100
|
|
|
|
48
|
if ($action eq 'enable') { |
|
|
|
50
|
|
|
|
|
|
|
697
|
10
|
|
|
|
|
214
|
$self->disabled->{$what} = 0; |
|
698
|
|
|
|
|
|
|
} |
|
699
|
|
|
|
|
|
|
elsif ($action eq 'disable') { |
|
700
|
10
|
|
|
|
|
234
|
$self->disabled->{$what} = 1; |
|
701
|
|
|
|
|
|
|
} |
|
702
|
|
|
|
|
|
|
else { |
|
703
|
0
|
|
|
|
|
0
|
die "Wrong action!\n" |
|
704
|
|
|
|
|
|
|
} |
|
705
|
20
|
|
|
|
|
864
|
return $self->disabled->{$what}; |
|
706
|
|
|
|
|
|
|
} |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
|
|
709
|
|
|
|
|
|
|
1; |
|
710
|
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
__END__ |