don't rely on loading of Net::LDAP::Entry by Net::LDAP
[catagits/Catalyst-Authentication-Store-LDAP.git] / lib / Catalyst / Authentication / Store / LDAP.pm
CommitLineData
f66d606b 1package Catalyst::Authentication::Store::LDAP;
2
3use strict;
4use warnings;
5
dfead577 6our $VERSION = '1.015';
f66d606b 7
8use Catalyst::Authentication::Store::LDAP::Backend;
9
10sub new {
11 my ( $class, $config, $app ) = @_;
12 return Catalyst::Authentication::Store::LDAP::Backend->new(
13 $config);
14}
15
161;
17
18__END__
19
20=pod
21
22=head1 NAME
23
9638f14b 24Catalyst::Authentication::Store::LDAP
f66d606b 25 - Authentication from an LDAP Directory.
26
27=head1 SYNOPSIS
28
24ff036b 29 use Catalyst qw(
f66d606b 30 Authentication
24ff036b 31 );
f66d606b 32
33 __PACKAGE__->config(
34 'authentication' => {
35 default_realm => "ldap",
36 realms => {
37 ldap => {
38 credential => {
39 class => "Password",
40 password_field => "password",
41 password_type => "self_check",
42 },
43 store => {
44 binddn => "anonymous",
45 bindpw => "dontcarehow",
46 class => "LDAP",
47 ldap_server => "ldap.yourcompany.com",
48 ldap_server_options => { timeout => 30 },
49 role_basedn => "ou=groups,ou=OxObjects,dc=yourcompany,dc=com",
50 role_field => "uid",
51 role_filter => "(&(objectClass=posixGroup)(memberUid=%s))",
52 role_scope => "one",
53 role_search_options => { deref => "always" },
54 role_value => "dn",
405489b5 55 role_search_as_user => 0,
f66d606b 56 start_tls => 1,
57 start_tls_options => { verify => "none" },
58 entry_class => "MyApp::LDAP::Entry",
59 use_roles => 1,
60 user_basedn => "ou=people,dc=yourcompany,dc=com",
61 user_field => "uid",
62 user_filter => "(&(objectClass=posixAccount)(uid=%s))",
57d476f1 63 user_scope => "one", # or "sub" for Active Directory
a03db022 64 user_search_options => {
65 deref => 'always',
66 attrs => [qw( distinguishedname name mail )],
67 },
1647b33a 68 user_results_filter => sub { return shift->pop_entry },
439924cb 69 persist_in_session => 'all',
f66d606b 70 },
71 },
72 },
73 },
74 );
75
76 sub login : Global {
77 my ( $self, $c ) = @_;
78
79 $c->authenticate({
9638f14b 80 id => $c->req->param("login"),
81 password => $c->req->param("password")
f66d606b 82 });
83 $c->res->body("Welcome " . $c->user->username . "!");
84 }
85
86=head1 DESCRIPTION
87
88This plugin implements the L<Catalyst::Authentication> v.10 API. Read that documentation first if
89you are upgrading from a previous version of this plugin.
90
91This plugin uses C<Net::LDAP> to let your application authenticate against
9638f14b 92an LDAP directory. It has a pretty high degree of flexibility, given the
93wide variation of LDAP directories and schemas from one system to another.
f66d606b 94
95It authenticates users in two steps:
96
971) A search of the directory is performed, looking for a user object that
9638f14b 98 matches the username you pass. This is done with the bind credentials
f66d606b 99 supplied in the "binddn" and "bindpw" configuration options.
100
1012) If that object is found, we then re-bind to the directory as that object.
9638f14b 102 Assuming this is successful, the user is Authenticated.
f66d606b 103
104=head1 CONFIGURATION OPTIONS
105
106=head2 Configuring with YAML
107
108Set Configuration to be loaded via Config.yml in YourApp.pm
109
110 use YAML qw(LoadFile);
111 use Path::Class 'file';
112
113 __PACKAGE__->config(
114 LoadFile(
115 file(__PACKAGE__->config->{home}, 'Config.yml')
116 )
117 );
118
119Settings in Config.yml (adapt these to whatever configuration format you use):
120
121 # Config for Store::LDAP
122 authentication:
123 default_realm: ldap
124 realms:
125 ldap:
126 credential:
127 class: Password
128 password_field: password
129 password_type: self_check
130 store:
131 class: LDAP
132 ldap_server: ldap.yourcompany.com
133 ldap_server_options:
134 timeout: 30
135 binddn: anonymous
136 bindpw: dontcarehow
137 start_tls: 1
138 start_tls_options:
139 verify: none
140 user_basedn: ou=people,dc=yourcompany,dc=com
141 user_filter: (&(objectClass=posixAccount)(uid=%s))
142 user_scope: one
143 user_field: uid
144 user_search_options:
145 deref: always
146 use_roles: 1
147 role_basedn: ou=groups,ou=OxObjects,dc=yourcompany,dc=com
148 role_filter: (&(objectClass=posixGroup)(memberUid=%s))
149 role_scope: one
150 role_field: uid
151 role_value: dn
152 role_search_options:
153 deref: always
154
155
156B<NOTE:> The settings above reflect the default values for OpenLDAP. If you
157are using Active Directory instead, Matija Grabnar suggests that the following
158tweeks to the example configuration will work:
159
160 user_basedn: ou=Domain Users,ou=Accounts,dc=mycompany,dc=com
161 user_field: samaccountname
9638f14b 162 user_filter: (sAMAccountName=%s)
57d476f1 163 user_scope: sub
f66d606b 164
9638f14b 165He also notes: "I found the case in the value of user_field to be significant:
f66d606b 166it didn't seem to work when I had the mixed case value there."
167
168=head2 ldap_server
169
170This should be the hostname of your LDAP server.
171
172=head2 ldap_server_options
173
9638f14b 174This should be a hashref containing options to pass to L<Net::LDAP>->new().
f66d606b 175See L<Net::LDAP> for the full list.
176
177=head2 binddn
178
179This should be the DN of the object you wish to bind to the directory as
180during the first phase of authentication. (The user lookup phase)
181
182If you supply the value "anonymous" to this option, we will bind anonymously
183to the directory. This is the default.
184
185=head2 bindpw
186
187This is the password for the initial bind.
188
189=head2 start_tls
190
191If this is set to 1, we will convert the LDAP connection to use SSL.
192
193=head2 start_tls_options
194
195This is a hashref, which contains the arguments to the L<Net::LDAP> start_tls
196method. See L<Net::LDAP> for the complete list of options.
197
198=head2 user_basedn
199
200This is the basedn for the initial user lookup. Usually points to the
201top of your "users" branch; ie "ou=people,dc=yourcompany,dc=com".
202
203=head2 user_filter
204
9638f14b 205This is the LDAP Search filter used during user lookup. The special string
f66d606b 206'%s' will be replaced with the username you pass to $c->login. By default
207it is set to '(uid=%s)'. Other possibly useful filters:
208
209 (&(objectClass=posixAccount)(uid=%s))
210 (&(objectClass=User)(cn=%s))
211
212=head2 user_scope
213
214This specifies the scope of the search for the initial user lookup. Valid
215values are "base", "one", and "sub". Defaults to "sub".
216
217=head2 user_field
218
219This is the attribute of the returned LDAP object we will use for their
220"username". This defaults to "uid". If you had user_filter set to:
221
222 (&(objectClass=User)(cn=%s))
223
224You would probably set this to "cn". You can also set it to an array,
225to allow more than one login field. The first field will be returned
226as identifier for the user.
227
228=head2 user_search_options
229
230This takes a hashref. It will append it's values to the call to
231L<Net::LDAP>'s "search" method during the initial user lookup. See
232L<Net::LDAP> for valid options.
233
234Be careful not to specify:
235
236 filter
237 scope
238 base
239
240As they are already taken care of by other configuration options.
241
1647b33a 242=head2 user_results_filter
243
244This is a Perl CODE ref that can be used to filter out multiple results
245from your LDAP query. In theory, your LDAP query should only return one result
246and find_user() will throw an exception if it encounters more than one result.
247However, if you have, for whatever reason, a legitimate reason for returning
248multiple search results from your LDAP query, use C<user_results_filter> to filter
249out the LDAP entries you do not want considered. Your CODE ref should expect
250a single argument, a Net::LDAP::Search object, and it should return exactly one
251value, a Net::LDAP::Entry object.
252
253Example:
254
255 user_results_filter => sub {
256 my $search_obj = shift;
257 foreach my $entry ($search_obj->entries) {
258 return $entry if my_match_logic( $entry );
259 }
260 return undef; # i.e., no match
261 }
9638f14b 262
f66d606b 263=head2 use_roles
264
9638f14b 265Whether or not to enable role lookups. It defaults to true; set it to 0 if
f66d606b 266you want to always avoid role lookups.
267
268=head2 role_basedn
269
270This should be the basedn where the LDAP Objects representing your roles are.
271
272=head2 role_filter
273
274This should be the LDAP Search filter to use during the role lookup. It
275defaults to '(memberUid=%s)'. The %s in this filter is replaced with the value
276of the "role_value" configuration option.
277
278So, if you had a role_value of "cn", then this would be populated with the cn
279of the User's LDAP object. The special case is a role_value of "dn", which
280will be replaced with the User's DN.
281
282=head2 role_scope
283
284This specifies the scope of the search for the user's role lookup. Valid
285values are "base", "one", and "sub". Defaults to "sub".
286
287=head2 role_field
288
289Should be set to the Attribute of the Role Object's returned during Role lookup you want to use as the "name" of the role. Defaults to "CN".
290
291=head2 role_value
292
9638f14b 293This is the attribute of the User object we want to use in our role_filter.
f66d606b 294If this is set to "dn", we will use the User Objects DN.
295
296=head2 role_search_options
297
298This takes a hashref. It will append it's values to the call to
299L<Net::LDAP>'s "search" method during the user's role lookup. See
300L<Net::LDAP> for valid options.
301
302Be careful not to specify:
303
304 filter
305 scope
306 base
307
308As they are already taken care of by other configuration options.
309
405489b5 310=head2 role_search_as_user
311
312By default this setting is false, and the role search will be performed
313by binding to the directory with the details in the I<binddn> and I<bindpw>
314fields. If this is set to false, then the role search will instead be
315performed when bound as the user you authenticated as.
316
439924cb 317=head2 persist_in_session
318
319Can take one of the following values, defaults to undefined:
320
321=head3 undefined
322
323Only store the username in the session and lookup the user and its roles
324on every request. That was how the module worked until version 1.015 and is
325also the default for backwards compatibility.
326
327=head3 all
328
329Store the user object and its roles in the session and never look it up in
330the store after login.
331
332B<NOTE:> It's recommended to limit the user attributes fetched from LDAP
333using L<user_search_options> / attrs to not exhaust the session store..
334
405489b5 335=head2 entry_class
336
337The name of the class of LDAP entries returned. This class should
338exist and is expected to be a subclass of Net::LDAP::Entry
339
340=head2 user_class
341
342The name of the class of user object returned. By default, this is
343L<Catalyst::Authentication::Store::LDAP::User>.
344
f66d606b 345=head1 METHODS
346
347=head2 new
348
349This method will populate
9638f14b 350L<Catalyst::Plugin::Authentication/default_auth_store> with this object.
f66d606b 351
352=head1 AUTHORS
353
354Adam Jacob <holoway@cpan.org>
355
356Some parts stolen shamelessly and entirely from
357L<Catalyst::Plugin::Authentication::Store::Htpasswd>.
358
359Currently maintained by Peter Karman <karman@cpan.org>.
360
361=head1 THANKS
362
363To nothingmuch, ghenry, castaway and the rest of #catalyst for the help. :)
364
365=head1 SEE ALSO
366
367L<Catalyst::Authentication::Store::LDAP>,
368L<Catalyst::Authentication::Store::LDAP::User>,
369L<Catalyst::Authentication::Store::LDAP::Backend>,
9638f14b 370L<Catalyst::Plugin::Authentication>,
f66d606b 371L<Net::LDAP>
372
373=head1 COPYRIGHT & LICENSE
374
375Copyright (c) 2005 the aforementioned authors. All rights
376reserved. This program is free software; you can redistribute
377it and/or modify it under the same terms as Perl itself.
378
379=cut
380
381