File Coverage

blib/lib/Net/SSH/AuthorizedKey/Base.pm
Criterion Covered Total %
statement 159 174 91.3
branch 54 68 79.4
condition 5 6 83.3
subroutine 19 20 95.0
pod 1 11 9.0
total 238 279 85.3


line stmt bran cond sub pod time code
1             ###########################################
2             package Net::SSH::AuthorizedKey::Base;
3             ###########################################
4 11     11   51 use strict;
  11         20  
  11         360  
5 11     11   45 use warnings;
  11         14  
  11         331  
6 11     11   47 use Log::Log4perl qw(:easy);
  11         16  
  11         56  
7 11     11   8158 use Text::ParseWords;
  11         7353  
  11         877  
8 11     11   70 use Digest::MD5 qw(md5_hex);
  11         19  
  11         1141  
9              
10             # Accessors common for both ssh1 and ssh2 keys
11             our @accessors = qw(key type error email comment);
12             __PACKAGE__->make_accessor( $_ ) for @accessors;
13              
14             # Some functions must be implemented in the subclass
15             do {
16 11     11   66 no strict qw(refs);
  11         17  
  11         15532  
17              
18             *{__PACKAGE__ . "::$_"} = sub {
19 0     0   0 die "Whoa! '$_' in the virtual base class has to be ",
20             " implemented by a real subclass.";
21             };
22              
23             } for qw(option_type as_string);
24              
25             # Options accepted by all keys
26             our %VALID_OPTIONS = (
27             "no-port-forwarding" => 1,
28             "no-agent-forwarding" => 1,
29             "no-x11-forwarding" => 1,
30             "no-pty" => 1,
31             "no-user-rc" => 1,
32             command => "s",
33             environment => "s",
34             from => "s",
35             permitopen => "s",
36             tunnel => "s",
37             );
38              
39             ###########################################
40             sub new {
41             ###########################################
42 50     50 0 107 my($class, %options) = @_;
43              
44 50         176 my $self = {
45             error => "(no error)",
46             option_order => [],
47             %options,
48             };
49              
50 50         132 bless $self, $class;
51 50         130 return $self;
52             }
53              
54             ###########################################
55             sub option_type_global {
56             ###########################################
57 70     70 0 72 my($self, $key) = @_;
58              
59 70 50       156 if(exists $VALID_OPTIONS{ $key }) {
60 70         125 return $VALID_OPTIONS{ $key };
61             }
62              
63             # Maybe the subclass knows about it
64 0         0 return $self->option_type($key);
65             }
66              
67             ###########################################
68             sub options {
69             ###########################################
70 2     2 1 11 my($self) = @_;
71              
72             return {
73 2         3 map { $_ => $self->option( $_ ) }
74 2         4 keys %{ $self->{ options } }
  2         6  
75             };
76             }
77              
78             ###########################################
79             sub option {
80             ###########################################
81 70     70 0 152 my($self, $key, $value, $append) = @_;
82              
83 70         92 $key = lc $key;
84              
85 70         139 my $option_type = $self->option_type_global($key);
86              
87 70 50       121 if(! defined $option_type) {
88 0         0 LOGWARN "Illegal option '$key'";
89 0         0 return undef;
90             }
91              
92 70 100       117 if(defined $value) {
93              
94 57 100       73 if( $append ) {
95 45 100 100     135 if( $self->{options}->{$key} and
96             ref($self->{options}->{$key}) ne "ARRAY" ) {
97 3         9 $self->{options}->{$key} = [ $self->{options}->{$key} ];
98             }
99             } else {
100 12         32 $self->option_delete( $key );
101             }
102              
103 57 100       89 if($option_type eq "s") {
104 35 100 66     100 if( $self->{options}->{$key} and
105             ref($self->{options}->{$key}) eq "ARRAY" ) {
106 5         14 DEBUG "Adding option $key to $value";
107 5         21 push @{ $self->{options}->{$key} }, $value;
  5         13  
108             } else {
109 30         88 DEBUG "Setting option $key to $value";
110 30         176 $self->{options}->{$key} = $value;
111             }
112             } else {
113 22         72 $self->{options}->{$key} = undef;
114             }
115 57         55 push @{ $self->{option_order} }, $key;
  57         92  
116             }
117              
118 70 100       142 if( "$option_type" eq "1" ) {
119 25         68 return exists $self->{options}->{$key};
120             }
121              
122 45         124 return $self->{options}->{$key};
123             }
124              
125             ###########################################
126             sub option_delete {
127             ###########################################
128 15     15 0 18 my($self, $key) = @_;
129              
130 15         17 $key = lc $key;
131              
132 15         44 @{ $self->{option_order} } =
  67         70  
133 15         19 grep { $_ ne $key } @{ $self->{option_order} };
  15         28  
134              
135 15         32 delete $self->{options}->{$key};
136             }
137              
138             ###########################################
139             sub options_as_string {
140             ###########################################
141 67     67 0 62 my($self) = @_;
142              
143 67         84 my $string = "";
144 67         73 my @parts = ();
145              
146 67         60 for my $option ( @{ $self->{option_order} } ) {
  67         138  
147 76 100       122 if(defined $self->{options}->{$option}) {
148 46 100       82 if(ref($self->{options}->{$option}) eq "ARRAY") {
149 10         12 for (@{ $self->{options}->{$option} }) {
  10         17  
150 28         34 push @parts, option_quote($option, $_);
151             }
152             } else {
153 36         75 push @parts, option_quote($option, $self->{options}->{$option});
154             }
155             } else {
156 30         40 push @parts, $option;
157             }
158             }
159 67         205 return join(',', @parts);
160             }
161              
162             ###########################################
163             sub option_quote {
164             ###########################################
165 64     64 0 73 my($option, $text) = @_;
166              
167 64         73 $text =~ s/([\\"])/\\$1/g;
168 64         207 return "$option=\"" . $text . "\"";
169             }
170              
171             ###########################################
172             sub parse {
173             ###########################################
174 124     124 0 1646 my($class, $string) = @_;
175              
176 124         284 DEBUG "Parsing line '$string'";
177              
178             # Clean up leading whitespace
179 124         661 $string =~ s/^\s+//;
180 124         149 $string =~ s/^#.*//;
181            
182 124 50       242 if(! length $string) {
183 0         0 DEBUG "Nothing to parse";
184 0         0 return;
185             }
186              
187 124 100       332 if(my $key = $class->key_read( $string ) ) {
188             # We found a key without options
189 32         66 $key->{options} = {};
190 32         799 DEBUG "Found ", $key->type(), " key: ", $key->as_string();
191 32         219 return $key;
192             }
193              
194             # No key found. Probably there are options in front of the key.
195             # By the way: the openssh-5.x parser doesn't allow escaped
196             # backslashes (\\), so we don't either.
197 92         934 my $rc = (
198             (my $key_string = $string) =~
199             s/^((?:
200             (?:"(?:\\"|.)*?)"|
201             \S
202             )+
203             )
204             //x );
205 92 50       503 my $options_string = ($rc ? $1 : "");
206 92         268 $key_string =~ s/^\s+//;
207              
208 92         243 DEBUG "Trying line with options stripped: [$key_string]";
209              
210 92 100       555 if(my $key = $class->key_read( $key_string ) ) {
211             # We found a key with options
212 18         46 $key->{options} = {};
213 18         66 $key->options_parse( $options_string );
214 18         541 DEBUG "Found ", $key->type(), " key: ", $key->as_string();
215 18         143 return $key;
216             }
217              
218 74         196 DEBUG "$class cannot parse line: $string";
219              
220 74         366 return undef;
221             }
222              
223             ###########################################
224             sub options_parse {
225             ###########################################
226 18     18 0 25 my($self, $string) = @_;
227              
228 18         58 DEBUG "Parsing options: [$string]";
229 18         175 my @options = parse_line(qr/\s*,\s*/, 0, $string);
230              
231             # delete empty/undefined fields
232 18 100       2428 @options = grep { defined $_ and length $_ } @options;
  48         186  
233              
234 18         32 DEBUG "Parsed options: ", join(' ', map { "[$_]" } @options);
  45         125  
235              
236 18         111 for my $option (@options) {
237 45         102 my($key, $value) = split /=/, $option, 2;
238 45 100       88 $value = 1 unless defined $value;
239 45         63 $value =~ s/^"(.*)"$/$1/; # remove quotes
240              
241 45         97 $self->option($key, $value, 1);
242             }
243             }
244              
245             ###########################################
246             sub fingerprint {
247             ###########################################
248 1     1 0 1 my($self) = @_;
249              
250 1         5 my $data = $self->options();
251              
252 1         4 my $string = join '', map { $_ => $data->{$_} } sort keys %$data;
  0         0  
253 1         29 $string .= $self->key();
254              
255 1         9 return md5_hex($string);
256             }
257              
258             ##################################################
259             # Poor man's Class::Struct
260             ##################################################
261             sub make_accessor {
262             ##################################################
263 88     88 0 138 my($package, $name) = @_;
264              
265 11     11   70 no strict qw(refs);
  11         57  
  11         1366  
266              
267 88         287 my $code = <
268             *{"$package\\::$name"} = sub {
269             my(\$self, \$value) = \@_;
270              
271             if(defined \$value) {
272             \$self->{$name} = \$value;
273             }
274             if(exists \$self->{$name}) {
275             return (\$self->{$name});
276             } else {
277             return "";
278             }
279             }
280             EOT
281 88 50       77 if(! defined *{"$package\::$name"}) {
  88         1494  
282 88 50   29   10481 eval $code or die "$@";
  29 100       68  
  29 50       63  
  27 100       51  
  29 50       50  
  29 100       57  
  0 50       0  
  37 100       61  
  37 50       118  
  33 100       76  
  37 50       85  
  37 100       91  
  0 50       0  
  50 100       119  
  50 50       106  
  35 100       57  
  50 50       102  
  50         123  
  0         0  
  25         40  
  25         53  
  19         42  
  25         47  
  25         57  
  0         0  
  56         214  
  56         118  
  14         27  
  56         108  
  56         180  
  0         0  
  34         48  
  34         68  
  23         70  
  34         65  
  34         78  
  0         0  
  42         620  
  42         86  
  32         63  
  42         79  
  42         98  
  0         0  
  53         219  
  53         104  
  34         72  
  53         94  
  53         145  
  0            
283             }
284             }
285              
286             1;
287              
288             __END__