line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package App::Genpass; |
2
|
|
|
|
|
|
|
# ABSTRACT: Quickly and easily create secure passwords |
3
|
|
|
|
|
|
|
$App::Genpass::VERSION = '2.34'; |
4
|
5
|
|
|
5
|
|
3981
|
use Carp; |
|
5
|
|
|
|
|
8
|
|
|
5
|
|
|
|
|
427
|
|
5
|
5
|
|
|
5
|
|
5334
|
use Moo; |
|
5
|
|
|
|
|
503728
|
|
|
5
|
|
|
|
|
40
|
|
6
|
5
|
|
|
5
|
|
16246
|
use Sub::Quote 'quote_sub'; |
|
5
|
|
|
|
|
22336
|
|
|
5
|
|
|
|
|
360
|
|
7
|
5
|
|
|
5
|
|
5677
|
use MooX::Types::MooseLike::Base qw/Int Str Bool ArrayRef/; |
|
5
|
|
|
|
|
41542
|
|
|
5
|
|
|
|
|
640
|
|
8
|
5
|
|
|
5
|
|
6773
|
use Getopt::Long qw/:config no_ignore_case/; |
|
5
|
|
|
|
|
79454
|
|
|
5
|
|
|
|
|
34
|
|
9
|
5
|
|
|
5
|
|
1033
|
use File::Spec; |
|
5
|
|
|
|
|
9
|
|
|
5
|
|
|
|
|
101
|
|
10
|
5
|
|
|
5
|
|
4392
|
use Config::Any; |
|
5
|
|
|
|
|
69203
|
|
|
5
|
|
|
|
|
194
|
|
11
|
5
|
|
|
5
|
|
5164
|
use File::HomeDir; |
|
5
|
|
|
|
|
39122
|
|
|
5
|
|
|
|
|
633
|
|
12
|
5
|
|
|
5
|
|
4914
|
use List::AllUtils qw( any none shuffle ); |
|
5
|
|
|
|
|
15315
|
|
|
5
|
|
|
|
|
21604
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
has uppercase => ( |
15
|
|
|
|
|
|
|
is => 'ro', |
16
|
|
|
|
|
|
|
isa => ArrayRef, |
17
|
|
|
|
|
|
|
default => quote_sub( q{ [ 'A' .. 'Z' ] } ), |
18
|
|
|
|
|
|
|
); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
has lowercase => ( |
21
|
|
|
|
|
|
|
is => 'ro', |
22
|
|
|
|
|
|
|
isa => ArrayRef, |
23
|
|
|
|
|
|
|
default => quote_sub( q{ [ 'a' .. 'z' ] } ), |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
has numerical => ( |
27
|
|
|
|
|
|
|
is => 'ro', |
28
|
|
|
|
|
|
|
isa => ArrayRef, |
29
|
|
|
|
|
|
|
default => quote_sub( q{ [ '0' .. '9' ] } ), |
30
|
|
|
|
|
|
|
); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
has unreadable => ( |
33
|
|
|
|
|
|
|
is => 'ro', |
34
|
|
|
|
|
|
|
isa => ArrayRef, |
35
|
|
|
|
|
|
|
default => quote_sub( q{ [ split //sm, q{oO0l1I} ] } ), |
36
|
|
|
|
|
|
|
); |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
has specials => ( |
39
|
|
|
|
|
|
|
is => 'ro', |
40
|
|
|
|
|
|
|
isa => ArrayRef, |
41
|
|
|
|
|
|
|
default => quote_sub( q{ [ split //sm, q{!@#$%^&*()} ] } ), |
42
|
|
|
|
|
|
|
); |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
has number => ( |
45
|
|
|
|
|
|
|
is => 'ro', |
46
|
|
|
|
|
|
|
isa => Int, |
47
|
|
|
|
|
|
|
default => quote_sub( q{1} ), |
48
|
|
|
|
|
|
|
); |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
has readable => ( |
51
|
|
|
|
|
|
|
is => 'ro', |
52
|
|
|
|
|
|
|
isa => Bool, |
53
|
|
|
|
|
|
|
default => quote_sub( q{1} ), |
54
|
|
|
|
|
|
|
); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
has verify => ( |
57
|
|
|
|
|
|
|
is => 'ro', |
58
|
|
|
|
|
|
|
isa => Bool, |
59
|
|
|
|
|
|
|
default => quote_sub( q{1} ), |
60
|
|
|
|
|
|
|
); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
has length => ( |
63
|
|
|
|
|
|
|
is => 'ro', |
64
|
|
|
|
|
|
|
isa => Int, |
65
|
|
|
|
|
|
|
); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
has minlength => ( |
68
|
|
|
|
|
|
|
is => 'rw', |
69
|
|
|
|
|
|
|
isa => Int, |
70
|
|
|
|
|
|
|
default => quote_sub( q{8} ), |
71
|
|
|
|
|
|
|
); |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
has maxlength => ( |
74
|
|
|
|
|
|
|
is => 'rw', |
75
|
|
|
|
|
|
|
isa => Int, |
76
|
|
|
|
|
|
|
default => quote_sub( q{10} ), |
77
|
|
|
|
|
|
|
); |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub parse_opts { |
80
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
81
|
0
|
|
|
|
|
0
|
my %opts = (); |
82
|
|
|
|
|
|
|
|
83
|
0
|
0
|
|
|
|
0
|
GetOptions( |
84
|
|
|
|
|
|
|
'configfile=s' => \$opts{'configfile'}, |
85
|
|
|
|
|
|
|
'lowercase=s@' => \$opts{'lowercase'}, |
86
|
|
|
|
|
|
|
'uppercase=s@' => \$opts{'uppercase'}, |
87
|
|
|
|
|
|
|
'numerical=i@' => \$opts{'numerical'}, |
88
|
|
|
|
|
|
|
'unreadable=s@' => \$opts{'unreadable'}, |
89
|
|
|
|
|
|
|
'specials=s@' => \$opts{'specials'}, |
90
|
|
|
|
|
|
|
'n|number=i' => \$opts{'number'}, |
91
|
|
|
|
|
|
|
'r|readable!' => \$opts{'readable'}, |
92
|
|
|
|
|
|
|
'v|verify!' => \$opts{'verify'}, |
93
|
|
|
|
|
|
|
'l|length=i' => \$opts{'length'}, |
94
|
|
|
|
|
|
|
'm|minlength=i' => \$opts{'minlength'}, |
95
|
|
|
|
|
|
|
'x|maxlength=i' => \$opts{'maxlength'}, |
96
|
|
|
|
|
|
|
) or croak q{Can't get options.}; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# remove undefined keys |
99
|
0
|
|
|
|
|
0
|
foreach my $key ( keys %opts ) { |
100
|
0
|
0
|
|
|
|
0
|
defined $opts{$key} or delete $opts{$key}; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
0
|
|
|
|
|
0
|
return %opts; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub new_with_options { |
107
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
108
|
0
|
|
|
|
|
0
|
my %opts = $class->parse_opts; |
109
|
0
|
|
|
|
|
0
|
my @configs = ( |
110
|
|
|
|
|
|
|
File::Spec->catfile( File::HomeDir->my_home, '.genpass.yaml' ), |
111
|
|
|
|
|
|
|
'/etc/genpass.yaml', |
112
|
|
|
|
|
|
|
); |
113
|
|
|
|
|
|
|
|
114
|
0
|
0
|
|
|
|
0
|
if ( ! exists $opts{'configfile'} ) { |
115
|
0
|
|
|
|
|
0
|
foreach my $file (@configs) { |
116
|
0
|
0
|
0
|
|
|
0
|
if ( -e $file && -r $file ) { |
117
|
0
|
|
|
|
|
0
|
$opts{'configfile'} = $file; |
118
|
0
|
|
|
|
|
0
|
last; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
0
|
0
|
|
|
|
0
|
if ( exists $opts{'configfile'} ) { |
124
|
0
|
|
|
|
|
0
|
%opts = ( |
125
|
|
|
|
|
|
|
%opts, |
126
|
0
|
|
|
|
|
0
|
%{ $class->get_config_from_file( $opts{'configfile'} ) }, |
127
|
|
|
|
|
|
|
); |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
|
|
0
|
my $self = $class->new( %opts, @_ ); |
131
|
|
|
|
|
|
|
|
132
|
0
|
|
|
|
|
0
|
return $self; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub get_config_from_file { |
136
|
0
|
|
|
0
|
1
|
0
|
my ($class, $file) = @_; |
137
|
|
|
|
|
|
|
|
138
|
0
|
0
|
|
|
|
0
|
$file = $file->() if ref $file eq 'CODE'; |
139
|
0
|
0
|
|
|
|
0
|
my $files_ref = ref $file eq 'ARRAY' ? $file : [$file]; |
140
|
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
0
|
my $can_config_any_args = $class->can('config_any_args'); |
142
|
0
|
0
|
|
|
|
0
|
my $extra_args = $can_config_any_args ? |
143
|
|
|
|
|
|
|
$can_config_any_args->($class, $file) : {}; |
144
|
|
|
|
|
|
|
; |
145
|
0
|
|
|
|
|
0
|
my $raw_cfany = Config::Any->load_files({ |
146
|
|
|
|
|
|
|
%$extra_args, |
147
|
|
|
|
|
|
|
use_ext => 1, |
148
|
|
|
|
|
|
|
files => $files_ref, |
149
|
|
|
|
|
|
|
flatten_to_hash => 1, |
150
|
|
|
|
|
|
|
} ); |
151
|
|
|
|
|
|
|
|
152
|
0
|
|
|
|
|
0
|
my %raw_config; |
153
|
0
|
|
|
|
|
0
|
foreach my $file_tested ( reverse @{$files_ref} ) { |
|
0
|
|
|
|
|
0
|
|
154
|
0
|
0
|
|
|
|
0
|
if ( ! exists $raw_cfany->{$file_tested} ) { |
155
|
0
|
|
|
|
|
0
|
warn qq{Specified configfile '$file_tested' does not exist, } . |
156
|
|
|
|
|
|
|
qq{is empty, or is not readable\n}; |
157
|
0
|
|
|
|
|
0
|
next; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
0
|
my $cfany_hash = $raw_cfany->{$file_tested}; |
161
|
0
|
0
|
0
|
|
|
0
|
die "configfile must represent a hash structure in file: $file_tested" |
|
|
|
0
|
|
|
|
|
162
|
|
|
|
|
|
|
unless $cfany_hash && ref $cfany_hash && ref $cfany_hash eq 'HASH'; |
163
|
|
|
|
|
|
|
|
164
|
0
|
|
|
|
|
0
|
%raw_config = ( %raw_config, %{$cfany_hash} ); |
|
0
|
|
|
|
|
0
|
|
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
0
|
|
|
|
|
0
|
\%raw_config; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub _get_chars { |
171
|
5010
|
|
|
5010
|
|
17609
|
my $self = shift; |
172
|
5010
|
|
|
|
|
11703
|
my @all_types = qw( lowercase uppercase numerical specials ); |
173
|
5010
|
|
|
|
|
5790
|
my @chars = (); |
174
|
5010
|
|
|
|
|
5582
|
my @types = (); |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# adding all the combinations |
177
|
5010
|
|
|
|
|
7525
|
foreach my $type (@all_types) { |
178
|
20040
|
50
|
|
|
|
53277
|
if ( my $ref = $self->$type ) { |
179
|
20040
|
|
|
|
|
19369
|
push @chars, @{$ref}; |
|
20040
|
|
|
|
|
88468
|
|
180
|
20040
|
|
|
|
|
42355
|
push @types, $type; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# removing the unreadable chars |
185
|
5010
|
100
|
|
|
|
14228
|
if ( $self->readable ) { |
186
|
5007
|
|
|
|
|
9945
|
my @remove_chars = ( |
187
|
5007
|
|
|
|
|
23315
|
@{ $self->unreadable }, |
188
|
5007
|
|
|
|
|
5702
|
@{ $self->specials }, |
189
|
|
|
|
|
|
|
); |
190
|
|
|
|
|
|
|
|
191
|
360368
|
|
|
|
|
485963
|
@chars = grep { |
192
|
5007
|
|
|
|
|
8989
|
local $a = $_; |
193
|
360368
|
|
|
5165216
|
|
1038611
|
none { $a eq $_ } @remove_chars; |
|
5165216
|
|
|
|
|
5932493
|
|
194
|
|
|
|
|
|
|
} @chars; |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# removing specials |
197
|
5007
|
|
|
|
|
25979
|
pop @types; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# make both refs |
201
|
5010
|
|
|
|
|
118026
|
return [ \@types, @chars ]; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub generate { |
205
|
5008
|
|
|
5008
|
1
|
82764
|
my ( $self, $number ) = @_; |
206
|
|
|
|
|
|
|
|
207
|
5008
|
|
|
|
|
6032
|
my $length; |
208
|
5008
|
|
|
|
|
9734
|
my $verify = $self->verify; |
209
|
5008
|
|
|
|
|
7048
|
my @passwords = (); |
210
|
5008
|
|
|
|
|
5593
|
my @verifications = (); |
211
|
5008
|
|
|
|
|
5795
|
my $EMPTY = q{}; |
212
|
|
|
|
|
|
|
|
213
|
5008
|
|
|
|
|
4711
|
my ( $char_types, @chars ) = @{ $self->_get_chars }; |
|
5008
|
|
|
|
|
10149
|
|
214
|
|
|
|
|
|
|
|
215
|
5008
|
|
|
|
|
27692
|
my @char_types = @{$char_types}; |
|
5008
|
|
|
|
|
11268
|
|
216
|
5008
|
|
|
|
|
7457
|
my $num_of_types = scalar @char_types; |
217
|
|
|
|
|
|
|
|
218
|
5008
|
50
|
66
|
|
|
169579
|
if ( (defined($self->length) && $num_of_types > $self->length) |
|
|
|
33
|
|
|
|
|
219
|
|
|
|
|
|
|
|| ($num_of_types > $self->minlength) ) { |
220
|
0
|
0
|
|
|
|
0
|
$length = defined($self->length) ? $self->length : $self->minlength.' minimum'; |
221
|
0
|
|
|
|
|
0
|
croak <<"_DIE_MSG"; |
222
|
|
|
|
|
|
|
You wanted a shorter password that the variety of characters you've selected. |
223
|
|
|
|
|
|
|
You requested $num_of_types types of characters but only have $length length. |
224
|
|
|
|
|
|
|
_DIE_MSG |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
5008
|
100
|
|
|
|
149191
|
if ($self->minlength > $self->maxlength) { |
228
|
1
|
|
|
|
|
212
|
carp "minlength > maxlength, so I'm switching them"; |
229
|
1
|
|
|
|
|
152
|
my $min = $self->maxlength; |
230
|
1
|
|
|
|
|
22
|
$self->maxlength($self->minlength); |
231
|
1
|
|
|
|
|
91
|
$self->minlength($min); |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
5008
|
|
66
|
|
|
225500
|
$length = $self->length |
235
|
|
|
|
|
|
|
|| $self->minlength + int(rand(abs($self->maxlength - $self->minlength) + 1)); |
236
|
|
|
|
|
|
|
|
237
|
5008
|
|
66
|
|
|
203894
|
$number ||= $self->number; |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# each password iteration needed |
240
|
5008
|
|
|
|
|
10189
|
foreach my $pass_iter ( 1 .. $number ) { |
241
|
5044
|
|
|
|
|
6593
|
my $password = $EMPTY; |
242
|
5044
|
|
|
|
|
7143
|
my $char_type = shift @char_types; |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# generating the password |
245
|
5044
|
|
|
|
|
10791
|
while ( $length > length $password ) { |
246
|
42292
|
|
|
|
|
75066
|
my $char = $chars[ int rand @chars ]; |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# for verifying, we just check that it has small capital letters |
249
|
|
|
|
|
|
|
# if that doesn't work, we keep asking it to get a new random one |
250
|
|
|
|
|
|
|
# the check if it has large capital letters and so on |
251
|
42292
|
100
|
66
|
|
|
152704
|
if ( $verify && $char_type && @{ $self->$char_type } ) { |
|
15143
|
|
66
|
|
|
73464
|
|
252
|
|
|
|
|
|
|
# verify $char_type |
253
|
15143
|
50
|
|
|
|
14123
|
if ( @{ $self->$char_type } ) { |
|
15143
|
|
|
|
|
37104
|
|
254
|
15143
|
|
|
821971
|
|
41144
|
while ( ! any { $_ eq $char } @{ $self->$char_type } ) { |
|
821971
|
|
|
|
|
912624
|
|
|
59057
|
|
|
|
|
187631
|
|
255
|
43914
|
|
|
|
|
177887
|
$char = $chars[ int rand @chars ]; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
$char_type = |
260
|
15143
|
100
|
|
|
|
51183
|
scalar @char_types > 0 ? shift @char_types : $EMPTY; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
42292
|
|
|
|
|
83457
|
$password .= $char; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# since the verification process creates a situation of ordered types |
267
|
|
|
|
|
|
|
# (lowercase, uppercase, numerical, special) |
268
|
|
|
|
|
|
|
# we need to shuffle the string |
269
|
5044
|
|
|
|
|
25173
|
$password = join $EMPTY, shuffle( split //sm, $password ); |
270
|
|
|
|
|
|
|
|
271
|
5044
|
100
|
|
|
|
50640
|
$number == 1 && return $password; |
272
|
|
|
|
|
|
|
|
273
|
40
|
|
|
|
|
54
|
push @passwords, $password; |
274
|
|
|
|
|
|
|
|
275
|
40
|
|
|
|
|
40
|
@char_types = @{$char_types}; |
|
40
|
|
|
|
|
102
|
|
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
4
|
100
|
|
|
|
51
|
return wantarray ? @passwords : \@passwords; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
1; |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
__END__ |