File Coverage

blib/lib/URL/Signature.pm
Criterion Covered Total %
statement 59 62 95.1
branch 8 14 57.1
condition 11 21 52.3
subroutine 13 16 81.2
pod 7 7 100.0
total 98 120 81.6


line stmt bran cond sub pod time code
1             package URL::Signature;
2              
3 3     3   60605 use warnings;
  3         10  
  3         102  
4 3     3   17 use strict;
  3         6  
  3         99  
5              
6 3     3   3315 use URI ();
  3         23316  
  3         67  
7 3     3   2653 use URI::QueryParam ();
  3         2164  
  3         70  
8 3     3   2776 use MIME::Base64 3.11 ();
  3         2501  
  3         69  
9 3     3   2558 use Digest::HMAC ();
  3         1847  
  3         52  
10 3     3   26 use Carp ();
  3         6  
  3         38  
11 3     3   2310 use Class::Load ();
  3         149550  
  3         98  
12 3     3   29 use Params::Util qw( _STRING _POSINT _NONNEGINT _CLASS );
  3         6  
  3         2347  
13              
14             our $VERSION = '0.03';
15              
16              
17             sub new {
18 5     5 1 1943 my ($class, %attrs) = @_;
19 5 100 100     396 Carp::croak('you must specify a secret key!')
20             unless exists $attrs{'key'} and defined _STRING($attrs{'key'});
21              
22             # Digest::SHA defaults to SHA-1
23 3   50     26 $attrs{'digest'} ||= 'Digest::SHA';
24 3 50 33     126 Carp::croak('digest must be a valid Perl class')
25             unless defined _CLASS($attrs{'digest'})
26             and Class::Load::load_class($attrs{'digest'});
27              
28 3   50     12973 $attrs{'length'} ||= 28;
29 3 50       109 Carp::croak('length should be a positive integer')
30             unless defined _POSINT($attrs{'length'});
31              
32 3   100     45 $attrs{'format'} ||= 'path';
33 3         16 my $child_class = 'URL::Signature::' . ucfirst $attrs{'format'};
34              
35 3 50 33     509 Carp::croak(qq[invalid format '$attrs{format}'])
36             unless defined _STRING($attrs{'format'}) and _CLASS($child_class);
37              
38 3         69 $attrs{'hmac'} = Digest::HMAC->new( $attrs{'key'}, $attrs{'digest'} );
39              
40 3         172 my ($loaded, $error) = Class::Load::try_load_class( $child_class );
41 3 50       368 Carp::croak(qq[error trying to load format class '$child_class': $error])
42             unless $loaded;
43              
44 3         11 my $self = bless \%attrs, $child_class;
45 3         40 $self->BUILD;
46 3         20 return $self;
47             }
48              
49              
50             sub code_for_uri {
51 10     10 1 72 my ($self, $uri) = @_;
52              
53             # ensure query parameters are sorted before encoding.
54 10         42 my %params = $uri->query_form;
55 10         727 $uri->query_form( map { $_ => $params{$_} } sort keys %params );
  12         38  
56              
57 10         509 my $code = MIME::Base64::encode_base64url(
58             $self->{hmac}->reset->add( $uri->as_string )->digest
59             );
60              
61             # truncate the digest, if necessary
62 10         708 $code = substr $code, 0, $self->{'length'};
63 10         46 return $code;
64             }
65              
66              
67             sub sign {
68 5     5 1 4194 my ($self, $path) = @_;
69 5         34 my $uri = URI->new( $path );
70 5         12082 my $code = $self->code_for_uri( $uri );
71              
72 5         27 return $self->append( $uri, $code );
73             }
74              
75              
76             sub validate {
77 5     5 1 5442 my ($self, $path, $old_code) = @_;
78 5         21 my $uri = URI->new( $path );
79              
80 5 50       193 return $old_code eq $self->code_for_uri( $uri )
81             if $old_code;
82              
83 5         20 my ($code, $new_uri) = $self->extract( $uri );
84 5 50 33     30 return if not $code
      33        
85             or not $uri
86             or $code ne $self->code_for_uri( $new_uri );
87              
88 5         19 return $new_uri;
89             }
90              
91              
92             # let our subclasses implement those
93 0     0 1   sub BUILD {}
94 0     0 1   sub extract {}
95 0     0 1   sub append {}
96              
97              
98             42;
99             __END__