[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