File Coverage

blib/lib/Authen/Credential.pm
Criterion Covered Total %
statement 78 85 91.7
branch 26 38 68.4
condition 7 15 46.6
subroutine 14 15 93.3
pod 6 6 100.0
total 131 159 82.3


line stmt bran cond sub pod time code
1             #+##############################################################################
2             # #
3             # File: Authen/Credential.pm #
4             # #
5             # Description: abstraction of a credential #
6             # #
7             #-##############################################################################
8              
9             #
10             # module definition
11             #
12              
13             package Authen::Credential;
14 1     1   15015 use strict;
  1         2  
  1         34  
15 1     1   4 use warnings;
  1         1  
  1         60  
16             our $VERSION = "1.1";
17             our $REVISION = sprintf("%d.%02d", q$Revision: 1.12 $ =~ /(\d+)\.(\d+)/);
18              
19             #
20             # used modules
21             #
22              
23 1     1   458 use No::Worries::Die qw(dief);
  1         15018  
  1         9  
24 1     1   166 use Params::Validate qw(validate_with validate_pos :types);
  1         3  
  1         292  
25 1     1   819 use URI::Escape qw(uri_escape uri_unescape);
  1         1691  
  1         1466  
26              
27             #
28             # global variables
29             #
30              
31             our(
32             $_IdRe, # regexp matching an identifier
33             $_ValChars, # set of all allowed value characters
34             $_SepChars, # set of all allowed separator characters
35             %_LoadedModule, # hash of successfully loaded modules
36             %ValidationSpec, # per-scheme Params::Validate specification
37             %Preparator, # per-scheme and target preparator code
38             );
39              
40             $_IdRe = qr{[a-z][a-z0-9]*};
41             $_ValChars = q{a-zA-Z0-9/\-\+\_\~\.\:};
42             $_SepChars = q{\,\ };
43              
44             #+++############################################################################
45             # #
46             # helper functions #
47             # #
48             #---############################################################################
49              
50             #
51             # make sure a module is loaded
52             #
53              
54             sub _require ($) {
55 19     19   21 my($module) = @_;
56              
57 19 100       51 return if $_LoadedModule{$module};
58 4         396 eval("require $module"); ## no critic 'ProhibitStringyEval'
59 4 100       21 if ($@) {
60 1         21 $@ =~ s/\s+at\s.+?\sline\s+\d+\.?$//;
61 1         8 dief("failed to load %s: %s", $module, $@);
62             } else {
63 3         11 $_LoadedModule{$module} = 1;
64             }
65             }
66              
67             #
68             # check that the data matches the per-scheme specification
69             #
70              
71             sub _check ($@) {
72 27     27   29 my($scheme);
73              
74 27         25 $scheme = shift(@_);
75 27 50       62 dief("invalid credential scheme (missing validation spec): %s", $scheme)
76             unless $ValidationSpec{$scheme};
77 27         1025 return(validate_with(
78             params => \@_,
79             spec => {
80 27         38 %{ $ValidationSpec{$scheme} },
81             scheme => {
82             type => SCALAR,
83             regex => qr/^\Q$scheme\E$/,
84             default => $scheme,
85             },
86             },
87             stack_skip => 2,
88             ));
89             }
90              
91             #+++############################################################################
92             # #
93             # object oriented interface #
94             # #
95             #---############################################################################
96              
97             #
98             # constructors
99             #
100              
101             sub new : method {
102 37     37 1 376 my($class, %option, $cc, $scheme);
103              
104 37         36 $class = shift(@_);
105 37 100       72 if ($class eq __PACKAGE__) {
106             # toplevel constructor
107 19         347 %option = validate_with(
108             params => \@_,
109             spec => {
110             scheme => {
111             type => SCALAR,
112             regex => $_IdRe,
113             default => "none",
114             },
115             },
116             allow_extra => 1,
117             );
118 19         341 $cc = $class . "::" . $option{scheme};
119 19         42 _require($cc);
120 18         65 return($cc->new(\%option));
121             }
122             # inherited constructor
123 18         57 $scheme = substr($class, length(__PACKAGE__) + 2);
124 18         27 return(bless({ _check($scheme, @_) }, $class));
125             }
126              
127             sub parse : method {
128 23     23 1 6908 my($class, $string, @list, %option);
129              
130 23         48 $class = shift(@_);
131 23 50 33     169 validate_pos(@_, { type => SCALAR })
      33        
132             unless @_ == 1 and defined($_[0]) and ref($_[0]) eq "";
133 23         29 $string = shift(@_);
134 23 100       43 return($class->new()) if $string eq "";
135 22 100       144 dief("invalid credential string: %s", $string)
136             unless $string =~ /^[${_ValChars}${_SepChars}\%\=]+$/o;
137 21         110 @list = split(/[${_SepChars}]+/o, $string);
138 21 100 100     141 dief("invalid credential string: %s", $string)
139             unless @list and $list[0] =~ /^($_IdRe)$/o;
140 19         48 %option = (scheme => shift(@list));
141 19         34 foreach my $kv (@list) {
142 26 100       209 if ($kv =~ /^($_IdRe)\=([$_ValChars\%]*)$/o) {
143 24 100       62 dief("duplicate credential key: %s", $1)
144             if exists($option{$1});
145 23         54 $option{$1} = uri_unescape($2);
146             } else {
147 2         7 dief("invalid credential key=value: %s", $kv);
148             }
149             }
150 16         121 return($class->new(\%option));
151             }
152              
153             #
154             # transformers
155             #
156              
157             sub hash : method {
158 0     0 1 0 my($self);
159              
160 0         0 $self = shift(@_);
161 0 0       0 validate_pos(@_) if @_;
162 0 0       0 return($self) unless wantarray();
163 0         0 return(%{ $self });
  0         0  
164             }
165              
166             sub string : method {
167 3     3 1 94 my($self, @parts);
168              
169 3         7 $self = shift(@_);
170 3 50       8 validate_pos(@_) if @_;
171 3 50       6 dief("invalid credential: no scheme") unless $self->{scheme};
172 3         7 @parts = ($self->{scheme});
173 3         2 foreach my $key (sort(keys(%{ $self }))) {
  3         13  
174 6 100       252 next if $key eq "scheme";
175 3         12 push(@parts, $key . "=" . uri_escape($self->{$key}, "^$_ValChars"));
176             }
177 3         17 return(join(" ", @parts));
178             }
179              
180             #
181             # accessors
182             #
183              
184             foreach my $name (qw(scheme)) {
185 1     1   15 no strict "refs";
  1         2  
  1         358  
186             *{ $name } = sub {
187 11     11   8 my($self);
188 11         10 $self = shift(@_);
189 11 50       21 validate_pos(@_) if @_;
190 11         28 return($self->{$name});
191             };
192             }
193              
194             #
195             # generic check method using the Params::Validate specification
196             #
197              
198             sub check : method {
199 9     9 1 2576 my($self);
200              
201 9         13 $self = shift(@_);
202 9 50       19 validate_pos(@_) if @_;
203 9         27 return(_check($self->scheme(), $self));
204             }
205              
206             #
207             # generic prepare method using the declared preparators
208             #
209              
210             sub prepare : method {
211 2     2 1 473 my($self, $target, $preparator);
212              
213 2         3 $self = shift(@_);
214 2 50 33     17 validate_pos(@_, { type => SCALAR })
      33        
215             unless @_ == 1 and defined($_[0]) and ref($_[0]) eq "";
216 2         2 $target = shift(@_);
217 2         5 $preparator = $Preparator{$self->scheme()}{$target};
218 2 50       10 return($preparator->($self)) if $preparator;
219 0           dief("invalid %s credential preparation target: %s",
220             $self->scheme(), $target);
221             }
222              
223             1;
224              
225             __DATA__