Remove META.yml from svn, it's a generated file. Update svn ignore props.
[catagits/Catalyst-Authentication-Store-LDAP.git] / lib / Catalyst / Authentication / Store / LDAP / User.pm
CommitLineData
f66d606b 1
2=pod
3
4=head1 NAME
5
6Catalyst::Authentication::Store::LDAP::User
7 - A User object representing an LDAP object.
8
9=head1 SYNOPSIS
10
afb8e81c 11You should be creating these objects through L<Catalyst::Authentication::Store::LDAP::Backend>'s "get_user" method, or just letting $c->authenticate do
f66d606b 12it for you.
13
14 sub action : Local {
15 my ( $self, $c ) = @_;
afb8e81c 16 $c->authenticate({
17 id => $c->req->param(username),
18 password => $c->req->param(password)
19 );
f66d606b 20 $c->log->debug($c->user->username . "is really neat!");
21 }
22
23If you access just $c->user in a scalar context, it will return the current
24username.
25
26=head1 DESCRIPTION
27
28This wraps up an LDAP object and presents a simplified interface to it's
29contents. It uses some AUTOLOAD magic to pass method calls it doesn't
30understand through as simple read only accessors for the LDAP entries
31various attributes.
32
33It gets grumpy if you ask for an attribute via the AUTOLOAD mechanism
34that it doesn't know about. Avoid that with using "has_attribute",
35discussed in more detail below.
36
37You can skip all that and just go straight to the L<Net::LDAP::Entry>
38object through the "ldap_entry" method:
39
40 my $entry = $c->user->ldap_entry;
41
42It also has support for Roles.
43
44=cut
45
46package Catalyst::Authentication::Store::LDAP::User;
47use base qw( Catalyst::Authentication::User Class::Accessor::Fast );
48
49use strict;
50use warnings;
51
405489b5 52our $VERSION = '0.1004';
f66d606b 53
54BEGIN { __PACKAGE__->mk_accessors(qw/user store/) }
55
56use overload '""' => sub { shift->stringify }, fallback => 1;
57
58=head1 METHODS
59
60=head2 new($store, $user)
61
62Takes a L<Catalyst::Authentication::Store::LDAP::Backend> object
63as $store, and the data structure returned by that class's "get_user"
64method as $user.
65
66Returns a L<Catalyst::Authentication::Store::LDAP::User> object.
67
68=cut
69
70sub new {
71 my ( $class, $store, $user ) = @_;
72
73 return unless $user;
74
75 bless { store => $store, user => $user, }, $class;
76}
77
78=head2 id
79
80Returns the results of the "stringify" method.
81
82=cut
83
84sub id {
85 my $self = shift;
86 return $self->stringify;
87}
88
89=head2 stringify
90
91Uses the "user_field" configuration option to determine what the "username"
92of this object is, and returns it.
93
94If you use the special value "dn" for user_field, it will return the DN
95of the L<Net::LDAP::Entry> object.
96
97=cut
98
99sub stringify {
100 my ($self) = @_;
101 my $userfield = $self->store->user_field;
102 $userfield = $$userfield[0] if ref $userfield eq 'ARRAY';
103 if ( $userfield eq "dn" ) {
104 my ($string) = $self->user->ldap_entry->dn;
105 return $string;
106 }
107 else {
108 my ($string) = $self->$userfield;
109 return $string;
110 }
111}
112
113=head2 supported_features
114
115Returns hashref of features that this Authentication::User subclass supports.
116
117=cut
118
119sub supported_features {
120 return {
121 password => { self_check => 1, },
122 session => 1,
123 roles => { self_check => 0, },
124 };
125}
126
127=head2 check_password($password)
128
129Bind's to the directory as the DN of the internal L<Net::LDAP::Entry> object,
130using the bind password supplied in $password. Returns 1 on a successful
131bind, 0 on failure.
132
133=cut
134
135sub check_password {
136 my ( $self, $password ) = @_;
137 my $ldap
138 = $self->store->ldap_bind( undef, $self->ldap_entry->dn, $password,
139 'forauth' );
140 if ( defined($ldap) ) {
405489b5 141 if ($self->store->role_search_as_user) {
142 # Have to do the role lookup _now_, as this is the only time
143 # that we have the user's password/ldap bind..
144 $self->roles($ldap);
145 }
f66d606b 146 return 1;
147 }
148 else {
149 return 0;
150 }
151}
152
153=head2 roles
154
155Returns the results of L<Catalyst::Authentication::Store::LDAP::Backend>'s "lookup_roles" method, an array of roles that are valid for this user.
156
157=cut
158
159sub roles {
160 my $self = shift;
405489b5 161 my $ldap = shift;
162 $self->{_roles} ||= [$self->store->lookup_roles($self, $ldap)];
163 return @{$self->{_roles}};
f66d606b 164}
165
166=head2 for_session
167
168Returns the User object, stringified.
169
170=cut
171
172sub for_session {
173 my $self = shift;
174 return $self->stringify;
175}
176
177=head2 ldap_entry
178
179Returns the raw ldap_entry.
180
181=cut
182
183sub ldap_entry {
184 my $self = shift;
185 return $self->user->{'ldap_entry'};
186}
187
188=head2 attributes($type)
189
190Returns an array of attributes present for this user. If $type is "ashash",
191it will return a hash with the attribute names as keys. (And the values of
192those attributes as, well, the values of the hash)
193
194=cut
195
196sub attributes {
197 my ( $self, $type ) = @_;
198 if ( $type eq "ashash" ) {
199 return $self->user->{'attributes'};
200 }
201 else {
202 return keys( %{ $self->user->{'attributes'} } );
203 }
204}
205
206=head2 has_attribute
207
208Returns the values for an attribute, or undef if that attribute is not present.
209The safest way to get at an attribute.
210
211=cut
212
213sub has_attribute {
214 my ( $self, $attribute ) = @_;
215 if ( !defined($attribute) ) {
216 Catalyst::Exception->throw(
217 "You must provide an attribute to has_attribute!");
218 }
219 if ( $attribute eq "dn" ) {
220 return $self->ldap_entry->dn;
221 }
222 elsif ( exists( $self->user->{'attributes'}->{$attribute} ) ) {
223 return $self->user->{'attributes'}->{$attribute};
224 }
225 else {
226 return undef;
227 }
228}
229
230=head2 AUTOLOADed methods
231
232We automatically map the attributes of the underlying L<Net::LDAP::Entry>
233object to read-only accessor methods. So, if you have an entry that looks
234like this one:
235
236 dn: cn=adam,ou=users,dc=yourcompany,dc=com
237 cn: adam
238 loginShell: /bin/zsh
239 homeDirectory: /home/adam
240 gecos: Adam Jacob
241 gidNumber: 100
242 uidNumber: 1053
243 mail: adam@yourcompany.com
244 uid: adam
245 givenName: Adam
246 sn: Jacob
247 objectClass: inetOrgPerson
248 objectClass: organizationalPerson
249 objectClass: Person
250 objectClass: Top
251 objectClass: posixAccount
252
253You can call:
254
255 $c->user->homedirectory
256
257And you'll get the value of the "homeDirectory" attribute. Note that
258all the AUTOLOADed methods are automatically lower-cased.
259
260=head2 Special Keywords
261
262The highly useful and common method "username" will map to the configured
263value of user_field (uid by default.)
264
265 $c->user->username == $c->user->uid
266
267=cut
268
269sub AUTOLOAD {
270 my $self = shift;
271
272 ( my $method ) = ( our $AUTOLOAD =~ /([^:]+)$/ );
273
274 if ( $method eq "DESTROY" ) {
275 return;
276 }
277 if ( exists( $self->user->{'attributes'}->{$method} ) ) {
278 return $self->user->{'attributes'}->{$method};
279 }
280 elsif ( $method eq "username" ) {
281 my $userfield = $self->store->user_field;
282 my $username = $self->has_attribute($userfield);
283 if ($username) {
284 return $username;
285 }
286 else {
287 Catalyst::Exception->throw( "User is missing the "
288 . $userfield
289 . " attribute, which should not be possible!" );
290 }
291 }
292 else {
293 Catalyst::Exception->throw(
294 "No attribute $method for User " . $self->stringify );
295 }
296}
297
2981;
299
300__END__
301
302=head1 AUTHORS
303
304Adam Jacob <holoway@cpan.org>
305
306Some parts stolen shamelessly and entirely from
307L<Catalyst::Plugin::Authentication::Store::Htpasswd>.
308
309Currently maintained by Peter Karman <karman@cpan.org>.
310
311=head1 THANKS
312
313To nothingmuch, ghenry, castaway and the rest of #catalyst for the help. :)
314
315=head1 SEE ALSO
316
317L<Catalyst::Authentication::Store::LDAP>, L<Catalyst::Authentication::Store::LDAP::Backend>, L<Catalyst::Plugin::Authentication>, L<Net::LDAP>
318
319=head1 COPYRIGHT & LICENSE
320
321Copyright (c) 2005 the aforementioned authors. All rights
322reserved. This program is free software; you can redistribute
323it and/or modify it under the same terms as Perl itself.
324
325=cut
326