File Coverage

blib/lib/Catalyst/Authentication/Store/Tangram.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Catalyst::Authentication::Store::Tangram;
2 1     1   874 use strict;
  1         2  
  1         32  
3 1     1   5 use warnings;
  1         1  
  1         30  
4 1     1   12 use base qw/Class::Accessor::Fast/;
  1         2  
  1         924  
5 1     1   4407 use Scalar::Util qw/blessed/;
  1         2  
  1         108  
6 1     1   650 use Catalyst::Authentication::Store::Tangram::User;
  0            
  0            
7             use Catalyst::Utils ();
8              
9             our $VERSION = '0.010';
10              
11             __PACKAGE__->mk_accessors(qw/
12             tangram_model
13             tangram_user_class
14             user_class
15             storage_method
16             use_roles
17             role_relation
18             role_name_field
19             user_results_filter
20             /);
21              
22             sub _get_storage {
23             my ($self, $c) = @_;
24             $c->model($self->tangram_model)->${\$self->storage_method}();
25             }
26              
27             sub new {
28             my ($class, $config, $app, $realm) = @_;
29             die("tangram_user_class key must be defined in config")
30             unless $config->{tangram_user_class};
31             $config->{tangram_model} ||= 'Tangram';
32             $config->{storage_method} ||= 'storage';
33             $config->{user_class} ||= __PACKAGE__ . '::User';
34             $config->{use_roles} ||= 0;
35             $config->{use_roles} = 0 if $config->{use_roles} =~ /false/i;
36             die("No role_relation config option set, cannot use roles")
37             if (!length($config->{role_relation}) && $config->{use_roles});
38              
39             Catalyst::Utils::ensure_class_loaded($config->{tangram_user_class});
40             Catalyst::Utils::ensure_class_loaded($config->{user_class});
41              
42             bless { %$config }, $class;
43             }
44              
45             sub find_user {
46             my ($self, $authinfo, $c) = @_;
47             my $tangram_class = $self->tangram_user_class;
48             my $storage = $self->_get_storage($c);
49             my $remote = $storage->remote($tangram_class);
50             my $filter;
51             foreach my $key (keys %$authinfo) {
52             if (defined $filter) {
53             $filter = $filter & $remote->{$key} eq $authinfo->{$key};
54             }
55             else {
56             $filter = $remote->{$key} eq $authinfo->{$key};
57             }
58             }
59             my @result = $storage->select($remote, filter => $filter);
60             if ($self->user_results_filter) {
61             @result = grep { $self->user_results_filter->($_) } @result;
62             }
63             if (@result) {
64             return $self->user_class->new($storage, $result[0], $self);
65             }
66             return;
67             }
68              
69             sub for_session {
70             my ($self, $user) = @_;
71             return $user->id;
72             }
73              
74             sub from_session {
75             my ($self, $id) = @_;
76             my $tangram_class = $self->tangram_user_class;
77             my $tangram_user;
78             eval { $tangram_user = $self->_get_storage->load($id) };
79             return if $@ or !$tangram_user;
80             return $self->user_class->new($self->_get_storage, $tangram_user, $self); # FIXME - $c arg for get_storage.
81             }
82              
83             sub user_supports {
84             my $class = shift;
85              
86             return Catalyst::Authentication::Store::Tangram::User->supports(@_);
87             }
88              
89             sub lookup_roles {
90             my ($self, $user_ob) = @_;
91             return undef unless $self->use_roles;
92              
93             my @roles = $user_ob->${ \$self->role_relation() }();
94             @roles = @{ $roles[0] } # Deal with either a list or listref return
95             if (1 == scalar(@roles) and 'ARRAY' eq ref($roles[0]));
96             if ($self->role_name_field) {
97             return map { $_->${\$self->role_name_field}() } @roles;
98             }
99             else {
100             return @roles;
101             }
102             }
103              
104             1;
105              
106             =head1 NAME
107              
108             Catalyst::Authentication::Store::Tangram - A storage class for Catalyst authentication from a class stored in Tangram
109              
110             =head1 SYNOPSIS
111              
112             use Catalyst qw/
113             Authentication
114             /;
115              
116             __PACKAGE__->config( authentication => {
117             default_realm => 'members',
118             realms => {
119             members => {
120             credential => {
121             class => 'Password',
122             password_field => 'password',
123             password_type => 'clear'
124             },
125             store => {
126             class => 'Tangram',
127             tangram_user_class => 'Users',
128             tangram_model => 'Tangram',
129             storage_method => 'storage', # $c->model('Tangram')->storage use_roles => 1,
130             role_relation -> 'authority',
131             role_name_field => 'name',
132             },
133             },
134             },
135             });
136              
137             # Log a user in:
138             sub login : Global {
139             my ( $self, $c ) = @_;
140              
141             $c->authenticate({
142             email_address => $c->req->param('email_address'),
143             password => $c->req->param('password'),
144             });
145             }
146              
147             =head1 DESCRIPTION
148              
149             The Catalyst::Authentication::Store::Tangram class provides access to
150             authentication information stored in a database via L<Tangram>.
151              
152             =head1 CONFIGURATION
153              
154             The Tangram authentication store is activated by setting the store configuration
155             class element to I<Tangram> as shown above. See the
156             L<Catalyst::Plugin::Authentication> documentation for more details on
157             configuring the store.
158              
159             The Tangram storage module has several configuration options
160              
161             authentication => {
162             default_realm => 'members',
163             realms => {
164             members => {
165             credential => {
166             # ...
167             },
168             store => {
169             class => 'Tangram',
170             user_class => 'Users',
171             tangram_model => 'Tangram',
172             storage_method => 'storage', # $c->model('Tangram')->storage
173             },
174             },
175             },
176             }
177              
178             =over
179              
180             =item class
181              
182             Class is part of the core L<Catalyst::Plugin::Authentication> module, it contains the class name of the store to be used.
183              
184             =item tangram_user_class
185              
186             Contains the class name of the class persisted in your Tangram schema to use as
187             the source for user information.
188             This config item is B<REQUIRED>. This class name is used to get a Tangram remote
189             object when constructing a search for your user when first authenticating, and
190             also this is the class which the ->load method is called on to restore the user
191             from a session.
192              
193             =item tangram_model
194              
195             Contains the class name (as passed to $c->model()) of the Tangram model to use
196             as the source for user information.
197             This config item is REQUIRED. The I<storage_method> method will be invoked on
198             this class to get the L<Tangram::Storage> instance to restore the user from.
199              
200             =item storage_method
201              
202             Contains the method to call on the I<tangram_model> to retrieve the instance of
203             L<Tangram::Storage> which users are looked up from.
204              
205             =item user_class
206              
207             Contains the class which the user object is blessed into. This class is usually
208             L<Catalyst::Authentication::Store::Tangram::User>, but you can sub-class that
209             class and have your subclass used instead by setting this configuration
210             parameter. You will not need to use this setting unless you are doing unusual
211             things with the user class.
212              
213             =item use_roles
214              
215             Activates role support if set to '1'
216              
217             =item role_relation
218              
219             The name of the method to call on your Tangram user object to retrieve an array
220             of roles for this user.
221              
222             This field may be a L<Tangram::Type::Array::FromMany>, or a
223             L<Tangram::Type::Array::FromOne> (in which case you will also need to use
224             I<role_name_field>), or it may be your own function which returns a list of
225             roles..
226              
227             =item role_name_field
228              
229             The name of the field to retrieve the name of the role from on the Tangram
230             class representing roles. Note that if this configuration parameter isn't
231             supplied, then the list returned by the method call to role_relation will be
232             used directly.
233              
234             =back
235              
236             =head1 METHODS
237              
238             =head2 new ( $config, $app, $realm )
239              
240             Simple constructor, returns a blessed reference to the store object instance.
241              
242             =head2 find_user ( $authinfo, $c )
243              
244             I<$auth_info> is expected to be a hash with the keys being field names on your
245             Tangram user object, and the values being what those fields should be matched
246             against. A tangram select will be built from the supplied authentication
247             information, and this select is used to retrieve the user from Tangram.
248              
249             =head2 for_session ( $c, $user )
250              
251             This method returns the Tangram ID for the user, as that is all that is
252             necessary to be persisted in the session to restore the user.
253              
254             =head2 from_session ( $c, $frozenuser )
255              
256             This method is called whenever a user is being restored from the session.
257             $frozenuser contains the Tangram ID of the user to restore.
258              
259             =head2 user_supports
260              
261             Delegates to the L<Catalyst::Authentication::Store::Tangram::User->supports|Catalyst::Authentication::Store::Tangram::User#supports> method.
262              
263             =head2 user_results_filter
264              
265             This is a Perl CODE ref that can be used to filter out multiple results
266             from your Tangram query. In theory, your Tangram query should only return one
267             result and find_user() will throw an exception if it encounters more than one
268             result. However, if you have, for whatever reason, a legitimate reason for
269             returning multiple search results from your Tangram query, use
270             C<user_results_filter> to filter out the Tangram entries you do not want
271             considered. Your CODE ref should expect a single argument, an instance of
272             your Tangram user object, and it should return exactly one value, which is
273             used as a true/false.
274              
275             Example:
276              
277             user_results_filter => sub {
278             my $obj = shift;
279             $obj->permissions =~ /catalystapp/ ? 1 : 0
280             }
281              
282             Note: The above example is B<not> a best practice method for storing roles
283             against a user, you really want a L<Tangram::Type::Array::FromMany>
284              
285             =head2 lookup_roles
286              
287             Returns a list of roles that this user is authorised for.
288              
289             Calls the method specified by the role_relation configuration key, and expects
290             either a list, or a reference to an array of roles to be returned.
291              
292             Note that this method will call the I<role_relation> method on the
293             I<user_class>, not on the I<tangram_user_class> directly. This can therefore be
294             used to add a custom role lookup without changing your underlying model class
295             lookup by sub-classing I<Catalyst::Authentication::Storage::Tangram::User>, and
296             adding the custom lookup there (then setting I<role_relation> and I<user_class>
297             appropriately.
298              
299             =head1 SEE ALSO
300              
301             L<Catalyst::Authentication::Store::Tangram::User>,
302             L<Catalyst::Plugin::Authentication>,
303             L<Catalyst::Authentication::Store>
304              
305             =head1 AUTHOR
306              
307             Tomas Doran, <bobtfish at bobtfish dot net>
308              
309             With thanks to state51, my employer, for giving me the time to work on this.
310              
311             Various ideas stolen from other Catalyst::Authentication modules by other
312             authors.
313              
314             =head1 BUGS
315              
316             All complex software has bugs, and I'm sure that this module is no exception.
317              
318             Please report bugs through the rt.cpan.org bug tracker.
319              
320             =head1 COPYRIGHT
321              
322             Copyright (c) 2008, state51. Some rights reserved.
323              
324             =head1 LICENSE
325              
326             This module is free software; you can use, redistribute, and modify it
327             under the same terms as Perl 5.8.x.
328              
329             =cut
330