[Catalyst-commits] r8570 - in
Catalyst-Authentication-Store-LDAP/trunk: .
lib/Catalyst/Authentication/Store
lib/Catalyst/Authentication/Store/LDAP t t/lib
karpet at dev.catalyst.perl.org
karpet at dev.catalyst.perl.org
Wed Oct 22 02:55:48 BST 2008
Author: karpet
Date: 2008-10-22 02:55:47 +0100 (Wed, 22 Oct 2008)
New Revision: 8570
Added:
Catalyst-Authentication-Store-LDAP/trunk/t/04-user_class.t
Catalyst-Authentication-Store-LDAP/trunk/t/10-roles-mock.t
Catalyst-Authentication-Store-LDAP/trunk/t/lib/UserClass.pm
Modified:
Catalyst-Authentication-Store-LDAP/trunk/Changes
Catalyst-Authentication-Store-LDAP/trunk/MANIFEST
Catalyst-Authentication-Store-LDAP/trunk/Makefile.PL
Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP.pm
Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP/Backend.pm
Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP/User.pm
Log:
release 0.1004
Modified: Catalyst-Authentication-Store-LDAP/trunk/Changes
===================================================================
--- Catalyst-Authentication-Store-LDAP/trunk/Changes 2008-10-22 01:16:52 UTC (rev 8569)
+++ Catalyst-Authentication-Store-LDAP/trunk/Changes 2008-10-22 01:55:47 UTC (rev 8570)
@@ -1,4 +1,11 @@
-0.1003 xxxx
+0.1004 21 Oct 2008
+ - Add the ability to have the user inflated into a custom
+ user class with the user_class option (t0m)
+ - Add the ability for role lookup to be performed within
+ the same (user) bind context that the user's password is
+ checked in (t0m)
+
+0.1003 10 Sept 2008
- get entries in array context rather than scalar context,
allowing for multiple values. patch by scpham.
- lc() to compare Net::LDAP results with supplied $id
Modified: Catalyst-Authentication-Store-LDAP/trunk/MANIFEST
===================================================================
--- Catalyst-Authentication-Store-LDAP/trunk/MANIFEST 2008-10-22 01:16:52 UTC (rev 8569)
+++ Catalyst-Authentication-Store-LDAP/trunk/MANIFEST 2008-10-22 01:55:47 UTC (rev 8570)
@@ -18,8 +18,11 @@
META.yml
t/02-realms_api.t
t/03-entry_class.t
+t/04-user_class.t
+t/10-roles-mock.t
t/50.auth.case.sensitivity.t
t/lib/EntryClass.pm
t/lib/LDAPTest.pm
+t/lib/UserClass.pm
t/pod-coverage.t
t/pod.t
Modified: Catalyst-Authentication-Store-LDAP/trunk/Makefile.PL
===================================================================
--- Catalyst-Authentication-Store-LDAP/trunk/Makefile.PL 2008-10-22 01:16:52 UTC (rev 8569)
+++ Catalyst-Authentication-Store-LDAP/trunk/Makefile.PL 2008-10-22 01:55:47 UTC (rev 8570)
@@ -12,6 +12,7 @@
#requires('Catalyst::Model::LDAP');
build_requires('Net::LDAP::Server::Test' => '0.07');
build_requires('Test::More');
+build_requires('Test::MockObject');
auto_install();
Modified: Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP/Backend.pm
===================================================================
--- Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP/Backend.pm 2008-10-22 01:16:52 UTC (rev 8569)
+++ Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP/Backend.pm 2008-10-22 01:55:47 UTC (rev 8570)
@@ -38,6 +38,7 @@
},
'user_results_filter' => sub { return shift->pop_entry },
'entry_class' => 'MyApp::LDAP::Entry',
+ 'user_class' => 'MyUser',
'use_roles' => 1,
'role_basedn' => 'ou=groups,dc=yourcompany,dc=com',
'role_filter' => '(&(objectClass=posixGroup)(member=%s))',
@@ -47,6 +48,7 @@
'role_search_options' => {
'deref' => 'always',
},
+ 'role_search_as_user' => 0,
);
our $users = Catalyst::Authentication::Store::LDAP::Backend->new(\%config);
@@ -78,10 +80,11 @@
use strict;
use warnings;
-our $VERSION = '0.1003';
+our $VERSION = '0.1004';
use Catalyst::Authentication::Store::LDAP::User;
use Net::LDAP;
+use Catalyst::Utils ();
BEGIN {
__PACKAGE__->mk_accessors(
@@ -91,7 +94,7 @@
user_attrs user_field use_roles role_basedn
role_filter role_scope role_field role_value
role_search_options start_tls start_tls_options
- user_results_filter
+ user_results_filter user_class role_search_as_user
)
);
}
@@ -125,7 +128,10 @@
$config_hash{'use_roles'} ||= '1';
$config_hash{'start_tls'} ||= '0';
$config_hash{'entry_class'} ||= 'Catalyst::Model::LDAP::Entry';
+ $config_hash{'user_class'} ||= 'Catalyst::Authentication::Store::LDAP::User';
+ $config_hash{'role_search_as_user'} ||= 0;
+ Catalyst::Utils::ensure_class_loaded($config_hash{'user_class'});
my $self = \%config_hash;
bless( $self, $class );
return $self;
@@ -157,7 +163,7 @@
sub get_user {
my ( $self, $id ) = @_;
- my $user = Catalyst::Authentication::Store::LDAP::User->new( $self,
+ my $user = $self->user_class->new( $self,
$self->lookup_user($id) );
return $user;
}
@@ -217,10 +223,7 @@
$binddn ||= $self->binddn;
$bindpw ||= $self->bindpw;
if ( $binddn eq "anonymous" ) {
- my $mesg = $ldap->bind;
- if ( $mesg->is_error ) {
- Catalyst::Exception->throw( "Error on Bind: " . $mesg->error );
- }
+ $self->_ldap_bind_anon($ldap);
}
else {
if ($bindpw) {
@@ -239,15 +242,20 @@
}
}
else {
- my $mesg = $ldap->bind($binddn);
- if ( $mesg->is_error ) {
- return undef;
- }
+ $self->_ldap_bind_anon($ldap, $binddn);
}
}
return $ldap;
}
+sub _ldap_bind_anon {
+ my ($self, $ldap, $dn) = @_;
+ my $mesg = $ldap->bind($dn);
+ if ( $mesg->is_error ) {
+ Catalyst::Exception->throw( "Error on Bind: " . $mesg->error );
+ }
+}
+
=head2 lookup_user($id)
Given a User ID, this method will:
@@ -341,10 +349,8 @@
$attrhash->{ lc($attr) } = \@attrvalues;
}
}
- my $load_class = $self->entry_class . ".pm";
- $load_class =~ s|::|/|g;
-
- eval { require $load_class };
+
+ eval { Catalyst::Utils::ensure_class_loaded($self->entry_class) };
if ( !$@ ) {
bless( $userentry, $self->entry_class );
$userentry->{_use_unicode}++;
@@ -356,11 +362,12 @@
return $rv;
}
-=head2 lookup_roles($userobj)
+=head2 lookup_roles($userobj, [$ldap])
This method looks up the roles for a given user. It takes a
L<Catalyst::Authentication::Store::LDAP::User> object
-as it's sole argument.
+as it's first argument, and can optionally take a I<Net::LDAP> object which
+is used rather than the default binding if supplied.
It returns an array containing the role_field attribute from all the
objects that match it's criteria.
@@ -368,11 +375,11 @@
=cut
sub lookup_roles {
- my ( $self, $userobj ) = @_;
+ my ( $self, $userobj, $ldap ) = @_;
if ( $self->use_roles == 0 || $self->use_roles =~ /^false$/i ) {
return undef;
}
- my $ldap = $self->ldap_bind;
+ $ldap ||= $self->ldap_bind;
my @searchopts;
if ( defined( $self->role_basedn ) ) {
push( @searchopts, 'base' => $self->role_basedn );
Modified: Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP/User.pm
===================================================================
--- Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP/User.pm 2008-10-22 01:16:52 UTC (rev 8569)
+++ Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP/User.pm 2008-10-22 01:55:47 UTC (rev 8570)
@@ -46,7 +46,7 @@
use strict;
use warnings;
-our $VERSION = '0.1003';
+our $VERSION = '0.1004';
BEGIN { __PACKAGE__->mk_accessors(qw/user store/) }
@@ -135,6 +135,11 @@
= $self->store->ldap_bind( undef, $self->ldap_entry->dn, $password,
'forauth' );
if ( defined($ldap) ) {
+ if ($self->store->role_search_as_user) {
+ # Have to do the role lookup _now_, as this is the only time
+ # that we have the user's password/ldap bind..
+ $self->roles($ldap);
+ }
return 1;
}
else {
@@ -150,7 +155,9 @@
sub roles {
my $self = shift;
- return $self->store->lookup_roles($self);
+ my $ldap = shift;
+ $self->{_roles} ||= [$self->store->lookup_roles($self, $ldap)];
+ return @{$self->{_roles}};
}
=head2 for_session
Modified: Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP.pm
===================================================================
--- Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP.pm 2008-10-22 01:16:52 UTC (rev 8569)
+++ Catalyst-Authentication-Store-LDAP/trunk/lib/Catalyst/Authentication/Store/LDAP.pm 2008-10-22 01:55:47 UTC (rev 8570)
@@ -3,7 +3,7 @@
use strict;
use warnings;
-our $VERSION = '0.1003';
+our $VERSION = '0.1004';
use Catalyst::Authentication::Store::LDAP::Backend;
@@ -52,6 +52,7 @@
role_scope => "one",
role_search_options => { deref => "always" },
role_value => "dn",
+ role_search_as_user => 0,
start_tls => 1,
start_tls_options => { verify => "none" },
entry_class => "MyApp::LDAP::Entry",
@@ -301,6 +302,23 @@
As they are already taken care of by other configuration options.
+=head2 role_search_as_user
+
+By default this setting is false, and the role search will be performed
+by binding to the directory with the details in the I<binddn> and I<bindpw>
+fields. If this is set to false, then the role search will instead be
+performed when bound as the user you authenticated as.
+
+=head2 entry_class
+
+The name of the class of LDAP entries returned. This class should
+exist and is expected to be a subclass of Net::LDAP::Entry
+
+=head2 user_class
+
+The name of the class of user object returned. By default, this is
+L<Catalyst::Authentication::Store::LDAP::User>.
+
=head1 METHODS
=head2 new
Added: Catalyst-Authentication-Store-LDAP/trunk/t/04-user_class.t
===================================================================
--- Catalyst-Authentication-Store-LDAP/trunk/t/04-user_class.t (rev 0)
+++ Catalyst-Authentication-Store-LDAP/trunk/t/04-user_class.t 2008-10-22 01:55:47 UTC (rev 8570)
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Catalyst::Exception;
+
+use Test::More tests => 5;
+use lib 't/lib';
+use LDAPTest;
+
+SKIP: {
+
+ eval "use Catalyst::Model::LDAP";
+ if ($@) {
+ skip "Catalyst::Model::LDAP not installed", 5;
+ }
+
+ my $server = LDAPTest::spawn_server();
+
+ use_ok("Catalyst::Authentication::Store::LDAP::Backend");
+
+ my $back = Catalyst::Authentication::Store::LDAP::Backend->new(
+ { 'ldap_server' => LDAPTest::server_host(),
+ 'binddn' => 'anonymous',
+ 'bindpw' => 'dontcarehow',
+ 'start_tls' => 0,
+ 'user_basedn' => 'ou=foobar',
+ 'user_filter' => '(&(objectClass=person)(uid=%s))',
+ 'user_scope' => 'one',
+ 'user_field' => 'uid',
+ 'use_roles' => 0,
+ 'user_class' => 'UserClass',
+ }
+ );
+
+ isa_ok( $back, "Catalyst::Authentication::Store::LDAP::Backend" );
+ my $user = $back->find_user( { username => 'somebody' } );
+ isa_ok( $user, "Catalyst::Authentication::Store::LDAP::User" );
+ isa_ok( $user, "UserClass");
+
+ is( $user->my_method, 'frobnitz', "methods on user class work" );
+
+}
Added: Catalyst-Authentication-Store-LDAP/trunk/t/10-roles-mock.t
===================================================================
--- Catalyst-Authentication-Store-LDAP/trunk/t/10-roles-mock.t (rev 0)
+++ Catalyst-Authentication-Store-LDAP/trunk/t/10-roles-mock.t 2008-10-22 01:55:47 UTC (rev 8570)
@@ -0,0 +1,101 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Catalyst::Exception;
+
+use Test::More tests => 7;
+use Test::MockObject::Extends;
+use Net::LDAP::Entry;
+use lib 't/lib';
+
+SKIP: {
+
+ eval "use Catalyst::Model::LDAP";
+ if ($@) {
+ skip "Catalyst::Model::LDAP not installed", 7;
+ }
+
+ use_ok("Catalyst::Authentication::Store::LDAP::Backend");
+
+ my (@searches, @binds);
+ for my $i (0..1) {
+
+ my $back = Catalyst::Authentication::Store::LDAP::Backend->new({
+ 'ldap_server' => 'ldap://127.0.0.1:555',
+ 'binddn' => 'anonymous',
+ 'bindpw' => 'dontcarehow',
+ 'start_tls' => 0,
+ 'user_basedn' => 'ou=foobar',
+ 'user_filter' => '(&(objectClass=inetOrgPerson)(uid=%s))',
+ 'user_scope' => 'one',
+ 'user_field' => 'uid',
+ 'use_roles' => 1,
+ 'role_basedn' => 'ou=roles',
+ 'role_filter' => '(&(objectClass=posixGroup)(memberUid=%s))',
+ 'role_scope' => 'one',
+ 'role_field' => 'userinrole',
+ 'role_value' => 'cn',
+ 'role_search_as_user' => $i,
+ });
+ $back = Test::MockObject::Extends->new($back);
+ my $bind_msg = Test::MockObject->new;
+ $bind_msg->mock(is_error => sub {}); # Cause bind call to always succeed
+ my $ldap = Test::MockObject->new;
+ $ldap->mock('bind', sub { shift; push (@binds, [@_]); return $bind_msg});
+ $ldap->mock('unbind' => sub {});
+ $ldap->mock('disconnect' => sub {});
+ my $search_res = Test::MockObject->new();
+ $search_res->mock(is_error => sub {}); # Never an error
+ $search_res->mock(entries => sub {
+ return map
+ { my $id = $_;
+ Test::MockObject->new->mock(
+ get_value => sub { "quux$id" }
+ )
+ }
+ qw/one two/
+ });
+ my @user_entries;
+ $search_res->mock(pop_entry => sub { return pop @user_entries });
+ $ldap->mock('search', sub { shift; push(@searches, [@_]); return $search_res; });
+ $back->mock('ldap_connect' => sub { $ldap });
+ my $user_entry = Net::LDAP::Entry->new;
+ push(@user_entries, $user_entry);
+ $user_entry->dn('ou=foobar');
+ $user_entry->add(
+ uid => 'somebody',
+ cn => 'test',
+ );
+ my $user = $back->find_user( { username => 'somebody' } );
+ isa_ok( $user, "Catalyst::Authentication::Store::LDAP::User" );
+ $user->check_password('password');
+ is_deeply( [sort $user->roles],
+ [sort qw/quuxone quuxtwo/],
+ "User has the expected set of roles" );
+ }
+ is_deeply(\@searches, [
+ ['base', 'ou=foobar', 'filter', '(&(objectClass=inetOrgPerson)(uid=somebody))', 'scope', 'one'],
+ ['base', 'ou=roles', 'filter', '(&(objectClass=posixGroup)(memberUid=test))', 'scope', 'one', 'attrs', [ 'userinrole' ]],
+ ['base', 'ou=foobar', 'filter', '(&(objectClass=inetOrgPerson)(uid=somebody))', 'scope', 'one'],
+ ['base', 'ou=roles', 'filter', '(&(objectClass=posixGroup)(memberUid=test))', 'scope', 'one', 'attrs', [ 'userinrole' ]],
+ ], 'User searches as expected');
+ is_deeply(\@binds, [
+ [ undef ], # First user search
+ [
+ 'ou=foobar',
+ 'password',
+ 'password'
+ ], # Rebind to confirm user
+ [
+ undef
+ ], # Rebind with initial credentials to find roles
+ # 2nd pass round main loop
+ [ undef ], # First user search
+ [
+ 'ou=foobar',
+ 'password',
+ 'password'
+ ] # Rebind to confirm user _and_ lookup roles;
+ ], 'Binds as expected');
+}
Added: Catalyst-Authentication-Store-LDAP/trunk/t/lib/UserClass.pm
===================================================================
--- Catalyst-Authentication-Store-LDAP/trunk/t/lib/UserClass.pm (rev 0)
+++ Catalyst-Authentication-Store-LDAP/trunk/t/lib/UserClass.pm 2008-10-22 01:55:47 UTC (rev 8570)
@@ -0,0 +1,10 @@
+package UserClass;
+use strict;
+use warnings;
+use base qw( Catalyst::Authentication::Store::LDAP::User );
+
+sub my_method {
+ return 'frobnitz';
+}
+
+1;
More information about the Catalyst-commits
mailing list