File Coverage

blib/lib/Authen/Prepare.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # $Id$
3             #
4             package Authen::Prepare;
5              
6 3     3   145261 use warnings;
  3         6  
  3         97  
7 3     3   13 use strict;
  3         7  
  3         127  
8              
9             our $VERSION = '0.05';
10              
11             #------------------------------------------------------------------------------
12             # Load Modules
13              
14 3     3   62 use 5.006;
  3         13  
  3         110  
15              
16             # Standard Modules
17 3     3   15 use Carp;
  3         5  
  3         249  
18 3     3   5411 use English qw(-no_match_vars);
  3         12323  
  3         19  
19 3     3   4551 use Readonly;
  3         10727  
  3         199  
20              
21             #use Smart::Comments;
22              
23             # Specific Modules
24 3     3   21 use Fcntl qw(:mode);
  3         6  
  3         920  
25 3     3   276916 use IO::Prompt;
  0            
  0            
26             use Text::Glob qw(match_glob);
27              
28             #------------------------------------------------------------------------------
29             # Class Specification
30              
31             use base qw(Class::Accessor::Fast);
32             __PACKAGE__->mk_accessors(qw(hostname username passfile prefix timeout));
33              
34             #------------------------------------------------------------------------------
35             # Constants
36              
37             Readonly my $DEFAULT_TIMEOUT => 10;
38             Readonly my $EMPTY_STR => q{};
39             Readonly my $FIELD_DELIMITER => q{:};
40             Readonly my $PASSWORD_CHAR => q{*};
41              
42             #------------------------------------------------------------------------------
43             # Methods
44              
45             sub credentials {
46             my ($self) = @_;
47             my $prefix = $self->prefix || $EMPTY_STR;
48              
49             my %cred = (
50             hostname => $self->_prompt_while_empty(
51             $self->hostname, qq|${prefix}Hostname: |
52             ),
53             username => $self->_prompt_while_empty(
54             $self->username, qq|${prefix}Username: |
55             ),
56             );
57              
58             $cred{password} = $self->passfile
59             ? $self->_get_password_for( @cred{qw(hostname username)} )
60             : undef;
61              
62             if ( !defined $cred{password} ) {
63             $cred{password} = $self->_prompt_timed(
64             $self->timeout,
65             qq|${prefix}Password: |,
66             -echo => $PASSWORD_CHAR,
67             -tty
68             );
69             }
70              
71             @cred{qw(host user)} = @cred{qw(hostname username)};
72              
73             return wantarray ? %cred : \%cred;
74             }
75              
76             #------------------------------------------------------------------------------
77             # Internal Methods
78              
79             sub _get_password_for {
80             my ( $self, $hostname, $username ) = @_;
81              
82             $self->_check_passfile();
83              
84             # FIXME - duplicate string
85             my $err_prefix = qq{Unable to use password file $self->passfile: };
86              
87             open my $fh, '<', $self->passfile or croak $err_prefix, $OS_ERROR;
88              
89             LINE:
90             while (<$fh>) {
91             next LINE if /^\s*\#/xms;
92             chomp;
93             my ( $stored_hostname, $stored_username, $stored_password )
94             = split /$FIELD_DELIMITER/;
95              
96             return $stored_password
97             if ( $stored_username eq $username
98             && match_glob( $stored_hostname, $hostname ) );
99             }
100              
101             return;
102             }
103              
104             sub _check_passfile {
105             my ($self) = @_;
106             my $passfile = $self->passfile;
107              
108             croak qq{Unable to read unspecified password file} if !$passfile;
109              
110             # FIXME - duplicate string
111             my $err_prefix = qq{Unable to use password file $passfile: };
112              
113             my $mode = ( stat $passfile )[2] or croak $err_prefix, $OS_ERROR;
114              
115             if ( ( $mode & S_IRWXG ) >> 3 ) {
116             croak $err_prefix, 'Permissions include group';
117             }
118              
119             if ( $mode & S_IRWXO ) {
120             croak $err_prefix, 'Permissions include all users (other)';
121             }
122              
123             return;
124             }
125              
126             sub _prompt_while_empty {
127             my ( $self, $response, $prompt ) = @_;
128             $response = $EMPTY_STR if !defined $response;
129              
130             while ( $response eq $EMPTY_STR ) {
131             $response = $self->_prompt_timed( $self->timeout, $prompt, -tty );
132             }
133              
134             return $response;
135             }
136              
137             sub _prompt_timed {
138             my ( $self, $timeout, @args ) = @_;
139             my $response;
140              
141             $timeout = $DEFAULT_TIMEOUT if !defined $timeout;
142              
143             eval {
144             local $SIG{ALRM} = sub {
145             die qq{Prompt timed out after $timeout seconds\n};
146             };
147              
148             alarm $timeout;
149              
150             eval { $response = prompt @args; };
151              
152             alarm 0;
153             die $EVAL_ERROR if $EVAL_ERROR;
154             };
155              
156             alarm 0;
157             croak $EVAL_ERROR if $EVAL_ERROR;
158              
159             # Stringify response since it is an IO::Prompt object
160             return ( defined $response ) ? qq{$response} : $EMPTY_STR;
161             }
162              
163             #------------------------------------------------------------------------------
164              
165             1; # Magic true value required at end of module
166             __END__