File Coverage

blib/lib/Net/OpenID/IndirectMessage.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1              
2             package Net::OpenID::IndirectMessage;
3             $Net::OpenID::IndirectMessage::VERSION = '1.20';
4 2     2   25143 use strict;
  2         3  
  2         42  
5 2     2   6 use Carp;
  2         1  
  2         104  
6 2     2   1042 use Net::OpenID::Common;
  0            
  0            
7              
8             sub new {
9             my $class = shift;
10             my $what = shift;
11             my %opts = @_;
12              
13             my $self = bless {}, $class;
14              
15             $self->{minimum_version} = delete $opts{minimum_version};
16              
17             Carp::croak("Unknown options: " . join(", ", keys %opts)) if %opts;
18              
19             my $getter;
20             my $enumer;
21             if (ref $what eq "HASH") {
22             # In this case it's the caller's responsibility to determine
23             # whether the method is GET or POST.
24             $getter = sub { $what->{$_[0]}; };
25             $enumer = sub { keys(%$what); };
26             }
27             elsif (ref $what eq "Apache") {
28             my %get;
29             if ($what->method eq 'POST') {
30             %get = $what->content;
31             }
32             else {
33             %get = $what->args;
34             }
35             $getter = sub { $get{$_[0]}; };
36             $enumer = sub { keys(%get); };
37             }
38             elsif (ref $what eq "Plack::Request") {
39             my $p = $what->method eq 'POST' ? $what->body_parameters : $what->query_parameters;
40             $getter = sub { $p->get($_[0]); };
41             $enumer = sub { keys %{$p}; };
42             }
43             elsif (ref $what ne "CODE") {
44             # assume an object that follows the CGI interface and has a param() method
45             # CGI does the right thing and omits query parameters if this is a POST
46             # others (Apache::Request, Apache2::Request) mix query and body params.
47             $getter = sub { scalar $what->param($_[0]); };
48             $enumer = sub { $what->param; };
49             }
50              
51             else {
52             # CODE reference
53             my @keys = ();
54             my $enumerated;
55             $getter = $what;
56             $enumer = sub {
57             unless ($enumerated) {
58             $enumerated = 1;
59             # In Consumer/Common 1.03 and predecessors, coderefs
60             # did not have to be able to enumerate all keys.
61             # Therefore, we must cope with legacy coderefs being
62             # passed in which don't expect to be called with no
63             # arguments, and then, most likely, fail in one of
64             # three ways:
65             # (1) return empty list
66             # (2) retrieve undef/'' value for undef/'' key.
67             # (3) raise an error
68             # We normalize these all to empty list, which our
69             # caller can then recognize as obviously wrong
70             # and do something about it.
71             eval { @keys = $what->() };
72             @keys = ()
73             if (@keys == 1 &&
74             !(defined($keys[0]) && length($keys[0])));
75             }
76             return @keys;
77             }
78             }
79             $self->{getter} = $getter;
80             $self->{enumer} = $enumer;
81              
82             # Now some quick pre-configuration of a few bits
83              
84             # Is this an OpenID message at all?
85             # All OpenID messages have an openid.mode value...
86             return undef unless $self->get('mode');
87              
88             # Is this an OpenID 2.0 message?
89             my $ns = $self->get('ns');
90              
91              
92             # The 2.0 spec section 4.1.2 requires that we support these namespace values
93             # but act like it's a normal 1.1 request.
94             # We do this by just pretending that ns wasn't set at all.
95             if ($ns && ($ns eq 'http://openid.net/signon/1.1' || $ns eq 'http://openid.net/signon/1.0')) {
96             $ns = undef;
97             }
98              
99             if (defined($ns) && $ns eq OpenID::util::version_2_namespace()) {
100             $self->{protocol_version} = 2;
101             }
102             elsif (! defined($ns)) {
103             # No namespace at all means a 1.1 message
104             if (($self->{minimum_version}||0) <= 1) {
105             $self->{protocol_version} = 1;
106             }
107             else {
108             # Pretend we don't understand the message.
109             return undef;
110             }
111             }
112             else {
113             # Unknown version is the same as not being an OpenID message at all
114             return undef;
115             }
116              
117             # This will be populated in on demand
118             $self->{extension_prefixes} = undef;
119              
120             return $self;
121             }
122              
123             sub protocol_version {
124             return $_[0]->{protocol_version};
125             }
126              
127             sub mode {
128             my $self = shift;
129             return $self->get('mode');
130             }
131              
132             sub get {
133             my $self = shift;
134             my $key = shift or Carp::croak("No argument name supplied to get method");
135              
136             # Arguments can only contain letters, numbers, underscores and dashes
137             Carp::croak("Invalid argument key $key") unless $key =~ /^[\w\-]+$/;
138             Carp::croak("Too many arguments") if scalar(@_);
139              
140             return $self->{getter}->("openid.$key");
141             }
142              
143             sub raw_get {
144             my $self = shift;
145             my $key = shift or Carp::croak("No argument name supplied to raw_get method");
146              
147             return $self->{getter}->($key);
148             }
149              
150             sub getter {
151             my $self = shift;
152              
153             return $self->{getter};
154             }
155              
156             # NOTE RE all_parameters():
157             #
158             # It was originally thought that enumeration of URL parameters was
159             # unnecessary except to support extensions, i.e., that support of the
160             # core protocol did not need it. While this is true in OpenID 1.1, it
161             # is not the case in OpenID 2.0 where check_authentication requires
162             # sending back a complete copy of the positive assertion message
163             # that was received indirectly.
164             #
165             # In cases where legacy client code is not supplying a real enumerator,
166             # this routine will return an empty list and callers will need to
167             # check for this. Recall that actual messages in all versions of the
168             # Openid protocol (thus far) are guaranteed to have at least an
169             # 'openid.mode' parameter.
170              
171             sub all_parameters {
172             my $self = shift;
173              
174             return $self->{enumer}->();
175             }
176              
177             sub get_ext {
178             my $self = shift;
179             my $namespace = shift or Carp::croak("No namespace URI supplied to get_ext method");
180             my $key = shift;
181              
182             Carp::croak("Too many arguments") if scalar(@_);
183              
184             $self->_compute_extension_prefixes() unless defined($self->{extension_prefixes});
185              
186             my $alias = $self->{extension_prefixes}{$namespace};
187             return $key ? undef : {} unless $alias;
188              
189             if ($key) {
190             return $self->{getter}->("openid.$alias.$key");
191             }
192             else {
193             my $prefix = "openid.$alias.";
194             my $prefixlen = length($prefix);
195             my $ret = {};
196             foreach my $key ($self->all_parameters) {
197             next unless substr($key, 0, $prefixlen) eq $prefix;
198             $ret->{substr($key, $prefixlen)} = $self->{getter}->($key);
199             }
200             return $ret;
201             }
202             }
203              
204             sub has_ext {
205             my $self = shift;
206             my $namespace = shift or Carp::croak("No namespace URI supplied to get_ext method");
207              
208             Carp::croak("Too many arguments") if scalar(@_);
209              
210             $self->_compute_extension_prefixes() unless defined($self->{extension_prefixes});
211              
212             return defined($self->{extension_prefixes}{$namespace}) ? 1 : 0;
213             }
214              
215             sub _compute_extension_prefixes {
216             my ($self) = @_;
217              
218             # return unless $self->{enumer};
219              
220             $self->{extension_prefixes} = {};
221             if ($self->protocol_version != 1) {
222             foreach my $key ($self->all_parameters) {
223             next unless $key =~ /^openid\.ns\.(\w+)$/;
224             my $alias = $1;
225             my $uri = $self->{getter}->($key);
226             $self->{extension_prefixes}{$uri} = $alias;
227             }
228             }
229             else {
230             # Synthesize the SREG namespace as it was used in OpenID 1.1
231             $self->{extension_prefixes}{"http://openid.net/extensions/sreg/1.1"} = "sreg";
232             }
233             }
234              
235             1;
236              
237             =head1 NAME
238              
239             Net::OpenID::IndirectMessage - Class representing a collection of namespaced arguments
240              
241             =head1 VERSION
242              
243             version 1.20
244              
245             =head1 DESCRIPTION
246              
247             This class acts as an abstraction layer over a collection of flat URL arguments
248             which supports namespaces as defined by the OpenID Auth 2.0 specification.
249              
250             It also recognises when it is given OpenID 1.1 non-namespaced arguments and
251             acts as if the relevant namespaces were present. In this case, it only
252             supports the basic OpenID 1.1 arguments and the extension arguments
253             for Simple Registration.
254              
255             This class can operate on
256             a normal hashref,
257             a L object or any object with a C method that behaves similarly
258             (L, L, L,...),
259             an L object,
260             a L object, or
261             an arbitrary C ref that when given a key name as its first parameter
262             and returns a value and if given no arguments returns a list of all keys present.
263              
264             If you pass in a hashref or a coderef it is your responsibility as the caller
265             to check the HTTP request method and pass in the correct set of arguments.
266             For the other kinds of objects, this module will do the right thing automatically.
267              
268             =head1 SYNOPSIS
269              
270             use Net::OpenID::IndirectMessage;
271              
272             # Pass in something suitable for the underlying flat dictionary.
273             # Will return an instance if the request arguments can be understood
274             # as a supported OpenID Message format.
275             # Will return undef if this doesn't seem to be an OpenID Auth message.
276             # Will croak if the $argumenty_thing is not of a suitable type.
277             my $args = Net::OpenID::IndirectMessage->new($argumenty_thing);
278              
279             # Determine which protocol version the message is using.
280             # Currently this can be either 1 for 1.1 or 2 for 2.0.
281             # Expect larger numbers for other versions in future.
282             # Most callers don't really need to care about this.
283             my $version = $args->protocol_version();
284              
285             # Get a core argument value ("openid.mode")
286             my $mode = $args->get("mode");
287              
288             # Get an extension argument value
289             my $nickname = $args->get_ext("http://openid.net/extensions/sreg/1.1", "nickname");
290              
291             # Get hashref of all arguments in a given namespace
292             my $sreg = $args->get_ext("http://openid.net/extensions/sreg/1.1");
293              
294             Most of the time callers won't need to use this class directly, but will instead
295             access it through a L instance.
296              
297             =head1 METHODS
298              
299             =over 4
300              
301             =item B
302              
303             Currently returns 1 or 2, according as this is an OpenID 1.0/1.1 or an OpenID 2.0 message.
304              
305             =item B
306              
307             Takes an extension namespace and returns true if the named extension is used in this message.
308              
309             =item B
310              
311             Takes an extension namespace and an optional parameter name, returns the parameter value,
312             or if no parameter given, the parameter value.
313              
314             =back