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