File Coverage

blib/lib/App/Unix/RPasswd/UI/Cli.pm
Criterion Covered Total %
statement 15 124 12.1
branch 0 52 0.0
condition 0 12 0.0
subroutine 5 11 45.4
pod 0 3 0.0
total 20 202 9.9


line stmt bran cond sub pod time code
1             package App::Unix::RPasswd::UI::Cli;
2             # This is an internal module of App::Unix::RPasswd
3              
4 1     1   2423 use feature ':5.10';
  1         3  
  1         97  
5 1     1   6 use Moo;
  1         2  
  1         8  
6 1     1   289 use List::MoreUtils ('uniq');
  1         1  
  1         54  
7 1     1   5 use POSIX qw/strftime/;
  1         2  
  1         8  
8 1     1   1042 use Term::ReadKey;
  1         10954  
  1         2100  
9              
10             our $VERSION = '0.53';
11             our $AUTHOR = 'Claudio Ramirez ';
12              
13             has 'args' => (
14             is => 'ro',
15             # isa => 'HashRef',
16             required => 1,
17             );
18              
19             has 'defaults' => (
20             is => 'ro',
21             # isa => 'HashRef',
22             required => 1,
23             );
24              
25             has 'messages' => (
26             is => 'rw',
27             # isa => 'ArrayRef',
28             default => sub { [] },
29             reader => 'get_messages',
30             lazy => 1,
31             init_arg => undef,
32             );
33              
34             has '_gen_only_options' => (
35             is => 'ro',
36             # isa => 'ArrayRef',
37             default => sub { [ 'base', 'date', 'sessions', 'generate_only', 'servers' ] },
38             init_arg => undef,
39             );
40              
41             sub check_params {
42              
43             # No params
44             # Return success (1) or failure (O)
45 0     0 0   my ( $self, $servers_ref ) = @_;
46 0           my $status = 1;
47              
48 0 0         if ( !scalar @{$servers_ref} > 0 ) {
  0            
49 0           $status = 0;
50 0           push @{ $self->messages }, 'You need at least one server.';
  0            
51             }
52 0 0         return $status if !$status;
53              
54             # gen_only mode
55 0 0         if ( $self->args->{generate_only} ) {
56 0           $status = $self->_check_gen_only;
57             }
58             else {
59 0           $status = $self->_check_main_mode;
60             }
61 0 0         return $status if !$status;
62              
63             # all modes
64 0           $self->args->{ssh} = $self->defaults->{ssh};
65 0 0         if ( defined $self->args->{ssh_args} ) {
66 0           for my $arg ( split( /\s/, $self->args->{ssh_args} ) )
67             { # string to array
68 0           push @{ $self->args->{ssh} }, $arg;
  0            
69             }
70             }
71              
72 0 0         if ( defined $self->args->{base} ) { # Salts are valid for both modes
73 0 0 0       if ( $self->args->{date} and $self->args->{date} !~ /^\d{8}$/ ) {
    0          
74 0           $status = 0;
75 0           push @{ $self->messages },
  0            
76             'Supply parameter date in a YYYYMMDD format (e.g. 20101123).';
77             }
78             elsif ( !defined $self->args->{date} ) {
79 0           $self->args->{date} = strftime "%Y%m%d", localtime;
80             }
81 0 0         if ( $self->args->{base} eq '-' ) {
82 0           $self->args->{base} = $self->_ask_key('base salt');
83             }
84            
85 0           while ( $self->args->{base} eq '' ) {
86 0           say 'Base salt can not be empty.';
87 0           $self->args->{base} = $self->_ask_key('base salt');
88             }
89              
90             }
91             # This is an internal module of App::Unix::RPasswd
92 0           return $status;
93             }
94              
95             sub term_line {
96 0     0 0   my $self = shift;
97 0           my ($wchar) = GetTerminalSize();
98 0           return "_" x $wchar . "\n";
99             }
100              
101             sub show_help {
102 0     0 0   my ( $self, $version_bool ) = @_;
103 0           require File::Basename;
104 0           my $program = File::Basename::basename($0);
105 0           say $program . ', version ' . $VERSION . '.';
106 0 0         return if $version_bool;
107 0           my $reruns = $self->defaults->{runs} + 1;
108 0           say <<"EOL";
109              
110             Change passwords on UNIX and UNIX-like servers on a simple, fast (in parallel)
111             and secure (SSH) way. A salt-based retrievable "random" password generator,
112             tied to the supplied server names and date, is included.
113              
114             Usage:
115             \t$program -u -p
116             \t$program -g -b -date
117              
118             Options:
119             \t--generate_only|-g:\t(re-)generate the salted password.
120             \t--user|-u:\t\tremote user name.
121             \t--password|-p:\t\tnew password for remote user.
122             \t--base|-b:\t\tbase salt for encryption.
123             \t--date|-d:\t\tdate in YYYYMMDD format (defaults to today)*.
124             \t--ssh_args|-a:\t\tsettings for the ssh client (man ssh)*.
125             \t--reruns|-r:\t\treruns for failed targets (defaults to 0)*.
126             \t--sessions|-s:\t\tsimultaneous sessions (defaults to 5)*.
127             \t--timeout|-t:\t\tsession timeout (defaults to 20 seconds)*.
128             \t--debug:\t\tprints debug output*.
129             \t--help|-h:\t\tprints this help screen.
130             \t--version|-v:\t\tprints the version number.
131              
132             \t*: optional
133              
134             The program has two modes. The default mode connects to remote targets and
135             changes the password (optional) of the specified user (mandatory) on the
136             supplied servers (mandatory). Optional valid parameters for this mode are
137             sessions, ssh_args ("-l root" if you don't the application as root), reruns,
138             timeout and debug. The built-in salted password generator can be used to
139             create unique 'random' passwords for each server on the fly. In this case
140             date (optional) and base (mandatory) are valid parameters for this mode.
141              
142             The "generate_only" mode is used to (re-) generate salted passwords. In this
143             mode only date (optional), base (mandatory), sessions (optional) and one of
144             more servers (mandatory) are valid parameters.
145              
146             From a security point of view, it is strongly advised to supply '-' as the base
147             salt or password on the command line. The program will then ask interactively
148             for the base salt or password.
149              
150             $AUTHOR, http://search.cpan.org/dist/App-Unix-RPasswd
151             EOL
152             }
153              
154             sub _ask_key {
155 0     0     my ( $self, $key ) = @_;
156 0           my $ckeys = $key;
157 0           $ckeys =~ s/(\w)(.+)/\U$1\E$2s/; # key -> Keys
158 0           my @msg =
159             ( "Please introduce the $key: ", "\nPlease re-introduce the $key: " );
160 0           my $counter = 0;
161 0           my $first_time = 1;
162 0           print $msg[$counter];
163 0           my @input;
164 0           system( '/bin/stty', '-echo' );
165              
166 0           while () {
167 0           chomp;
168 0           $input[$counter] = $_;
169 0 0         if ( $counter == 1 ) {
170 0 0         if ( $input[0] eq $input[1] ) { last }
  0            
171             else {
172 0           say "\n$ckeys are not the same...";
173 0           $counter = 0;
174             }
175             }
176 0           else { $counter++; $first_time = 0; }
  0            
177              
178 0 0         print $msg[$counter] unless $first_time;
179             }
180 0           system '/bin/stty echo';
181 0           say '';
182 0           return $input[0];
183             }
184              
185             sub _check_gen_only {
186 0     0     my $self = shift;
187 0           my $status = 1;
188 0           my @gen_options_provided =
189             #grep { !( $_ ~~ @{ $self->_gen_only_options } ) }
190 0           grep { !( /^$_$/, @{ $self->_gen_only_options } ) }
  0            
191 0           keys %{ $self->args };
192 0           for my $key (@gen_options_provided) {
193 0 0         if ( defined $self->args->{$key} ) {
194 0           $status = 0;
195 0           push @{ $self->messages },
  0            
196             "Parameter $key is invalid in this mode.";
197             }
198             }
199 0 0         if ( !defined $self->args->{base} ) {
200 0           $status = 0;
201 0           push @{ $self->messages }, 'Parameter base is required in this mode.';
  0            
202             }
203 0 0         if ($status) { # Reruns have no sense in this mode
204 0           $self->args->{reruns} = 0;
205             }
206 0           return $status;
207             }
208              
209             sub _check_main_mode {
210 0     0     my $self = shift;
211 0           my $status = 1;
212              
213 0 0         if ( !defined $self->args->{user} ) {
214 0           $status = 0;
215 0           push @{ $self->messages }, 'Parameter user is mandatory.';
  0            
216             }
217              
218 0 0 0       if ( !defined $self->args->{password}
    0 0        
219             and !defined $self->args->{base} )
220             {
221 0           $status = 0;
222 0           push @{ $self->messages }, 'You need to specify password or base.';
  0            
223             }
224             elsif ( defined $self->args->{password}
225             and defined $self->args->{base} )
226             {
227 0           $status = 0;
228 0           push @{ $self->messages },
  0            
229             'You need to specify password or base, not both.';
230             }
231 0 0         return $status if !$status;
232              
233 0 0         if ( defined $self->args->{password} ) {
234 0 0 0       if ( $self->args->{date} ) {
    0          
235 0           $status = 0;
236 0           push @{ $self->messages },
  0            
237             'Date is only valid in combination with base.';
238             }
239             elsif ( $status == 1 and $self->args->{password} eq '-' ) {
240 0           $self->args->{password} = $self->_ask_key('password');
241             }
242             }
243              
244 0 0         if ( !defined $self->args->{timeout} ) {
245 0           $self->args->{timeout} = $self->defaults->{timeout};
246             }
247              
248 0 0         if ( defined $self->args->{reruns} ) {
249 0 0         if ( $self->args->{reruns} > 98 ) {
250 0           $status = 0;
251 0           push @{ $self->messages },
  0            
252             'Less than 99 retries allowed. Let\'s be raisonable.';
253             }
254             }
255 0           else { $self->args->{reruns} = 0 }
256 0           return $status;
257             }
258              
259             1;