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   54 use strict;
  11         17  
  11         336  
5 11     11   53 use warnings;
  11         16  
  11         284  
6 11     11   52 use Log::Log4perl qw(:easy);
  11         26  
  11         64  
7 11     11   10596 use Text::ParseWords;
  11         6010  
  11         809  
8 11     11   60 use Digest::MD5 qw(md5_hex);
  11         18  
  11         1042  
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   56 no strict qw(refs);
  11         16  
  11         17917  
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 49     49 0 118 my($class, %options) = @_;
43              
44 49         224 my $self = {
45             error => "(no error)",
46             option_order => [],
47             %options,
48             };
49              
50 49         135 bless $self, $class;
51 49         168 return $self;
52             }
53              
54             ###########################################
55             sub option_type_global {
56             ###########################################
57 70     70 0 89 my($self, $key) = @_;
58              
59 70 50       164 if(exists $VALID_OPTIONS{ $key }) {
60 70         153 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         8  
75             };
76             }
77              
78             ###########################################
79             sub option {
80             ###########################################
81 70     70 0 169 my($self, $key, $value, $append) = @_;
82              
83 70         95 $key = lc $key;
84              
85 70         187 my $option_type = $self->option_type_global($key);
86              
87 70 50       152 if(! defined $option_type) {
88 0         0 LOGWARN "Illegal option '$key'";
89 0         0 return undef;
90             }
91              
92 70 100       130 if(defined $value) {
93              
94 57 100       106 if( $append ) {
95 45 100 100     186 if( $self->{options}->{$key} and
96             ref($self->{options}->{$key}) ne "ARRAY" ) {
97 3         11 $self->{options}->{$key} = [ $self->{options}->{$key} ];
98             }
99             } else {
100 12         40 $self->option_delete( $key );
101             }
102              
103 57 100       293 if($option_type eq "s") {
104 35 100 66     123 if( $self->{options}->{$key} and
105             ref($self->{options}->{$key}) eq "ARRAY" ) {
106 5         22 DEBUG "Adding option $key to $value";
107 5         27 push @{ $self->{options}->{$key} }, $value;
  5         13  
108             } else {
109 30         157 DEBUG "Setting option $key to $value";
110 30         222 $self->{options}->{$key} = $value;
111             }
112             } else {
113 22         189 $self->{options}->{$key} = undef;
114             }
115 57         52 push @{ $self->{option_order} }, $key;
  57         122  
116             }
117              
118 70 100       179 if( "$option_type" eq "1" ) {
119 25         80 return exists $self->{options}->{$key};
120             }
121              
122 45         154 return $self->{options}->{$key};
123             }
124              
125             ###########################################
126             sub option_delete {
127             ###########################################
128 15     15 0 27 my($self, $key) = @_;
129              
130 15         25 $key = lc $key;
131              
132 15         50 @{ $self->{option_order} } =
  67         125  
133 15         17 grep { $_ ne $key } @{ $self->{option_order} };
  15         31  
134              
135 15         61 delete $self->{options}->{$key};
136             }
137              
138             ###########################################
139             sub options_as_string {
140             ###########################################
141 66     66 0 91 my($self) = @_;
142              
143 66         77 my $string = "";
144 66         104 my @parts = ();
145              
146 66         69 for my $option ( @{ $self->{option_order} } ) {
  66         153  
147 76 100       154 if(defined $self->{options}->{$option}) {
148 46 100       99 if(ref($self->{options}->{$option}) eq "ARRAY") {
149 10         9 for (@{ $self->{options}->{$option} }) {
  10         23  
150 28         37 push @parts, option_quote($option, $_);
151             }
152             } else {
153 36         81 push @parts, option_quote($option, $self->{options}->{$option});
154             }
155             } else {
156 30         50 push @parts, $option;
157             }
158             }
159 66         273 return join(',', @parts);
160             }
161              
162             ###########################################
163             sub option_quote {
164             ###########################################
165 64     64 0 87 my($option, $text) = @_;
166              
167 64         90 $text =~ s/([\\"])/\\$1/g;
168 64         191 return "$option=\"" . $text . "\"";
169             }
170              
171             ###########################################
172             sub parse {
173             ###########################################
174 122     122 0 1758 my($class, $string) = @_;
175              
176 122         397 DEBUG "Parsing line '$string'";
177              
178             # Clean up leading whitespace
179 122         768 $string =~ s/^\s+//;
180 122         174 $string =~ s/^#.*//;
181            
182 122 50       258 if(! length $string) {
183 0         0 DEBUG "Nothing to parse";
184 0         0 return;
185             }
186              
187 122 100       413 if(my $key = $class->key_read( $string ) ) {
188             # We found a key without options
189 31         94 $key->{options} = {};
190 31         1027 DEBUG "Found ", $key->type(), " key: ", $key->as_string();
191 31         261 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 91         1066 my $rc = (
198             (my $key_string = $string) =~
199             s/^((?:
200             (?:"(?:\\"|.)*?)"|
201             \S
202             )+
203             )
204             //x );
205 91 50       342 my $options_string = ($rc ? $1 : "");
206 91         361 $key_string =~ s/^\s+//;
207              
208 91         302 DEBUG "Trying line with options stripped: [$key_string]";
209              
210 91 100       628 if(my $key = $class->key_read( $key_string ) ) {
211             # We found a key with options
212 18         52 $key->{options} = {};
213 18         77 $key->options_parse( $options_string );
214 18         686 DEBUG "Found ", $key->type(), " key: ", $key->as_string();
215 18         151 return $key;
216             }
217              
218 73         276 DEBUG "$class cannot parse line: $string";
219              
220 73         500 return undef;
221             }
222              
223             ###########################################
224             sub options_parse {
225             ###########################################
226 18     18 0 30 my($self, $string) = @_;
227              
228 18         66 DEBUG "Parsing options: [$string]";
229 18         187 my @options = parse_line(qr/\s*,\s*/, 0, $string);
230              
231             # delete empty/undefined fields
232 18 100       2616 @options = grep { defined $_ and length $_ } @options;
  48         309  
233              
234 18         58 DEBUG "Parsed options: ", join(' ', map { "[$_]" } @options);
  45         142  
235              
236 18         126 for my $option (@options) {
237 45         111 my($key, $value) = split /=/, $option, 2;
238 45 100       98 $value = 1 unless defined $value;
239 45         66 $value =~ s/^"(.*)"$/$1/; # remove quotes
240              
241 45         108 $self->option($key, $value, 1);
242             }
243             }
244              
245             ###########################################
246             sub fingerprint {
247             ###########################################
248 1     1 0 3 my($self) = @_;
249              
250 1         7 my $data = $self->options();
251              
252 1         8 my $string = join '', map { $_ => $data->{$_} } sort keys %$data;
  0         0  
253 1         38 $string .= $self->key();
254              
255 1         11 return md5_hex($string);
256             }
257              
258             ##################################################
259             # Poor man's Class::Struct
260             ##################################################
261             sub make_accessor {
262             ##################################################
263 88     88 0 151 my($package, $name) = @_;
264              
265 11     11   82 no strict qw(refs);
  11         17  
  11         1570  
266              
267 88         309 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       100 if(! defined *{"$package\::$name"}) {
  88         662  
282 88 50   27   11351 eval $code or die "$@";
  27 100       61  
  27 50       69  
  22 100       51  
  27 50       76  
  27 100       76  
  0 50       0  
  17 100       29  
  17 50       41  
  10 100       26  
  17 50       39  
  17 100       65  
  0 50       0  
  50 100       163  
  50 50       136  
  37 100       108  
  50 50       115  
  50         170  
  0         0  
  66         930  
  66         142  
  46         92  
  66         145  
  66         207  
  0         0  
  55         159  
  55         128  
  17         40  
  55         127  
  55         230  
  0         0  
  51         265  
  51         119  
  44         220  
  51         116  
  51         147  
  0         0  
  33         61  
  33         89  
  29         61  
  33         74  
  33         83  
  0         0  
  20         96  
  20         48  
  8         31  
  20         54  
  20         84  
  0            
283             }
284             }
285              
286             1;
287              
288             __END__