59dd8f656ced956bddb66a0782160614199f39c8
[catagits/Catalyst-Authentication-Store-LDAP.git] / t / 10-roles-mock.t
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 use Test::More;
7 use Test::MockObject::Extends;
8 use Test::Exception;
9 use Net::LDAP::Entry;
10 use lib 't/lib';
11
12 eval "use Catalyst::Model::LDAP";
13 plan skip_all => "Catalyst::Model::LDAP not installed" if $@;
14
15 use_ok("Catalyst::Authentication::Store::LDAP::Backend");
16
17
18 my $back_without_use_roles = Catalyst::Authentication::Store::LDAP::Backend->new({
19     ldap_server => 'ldap://127.0.0.1:555',
20     binddn      => 'anonymous',
21     bindpw      => 'dontcarehow',
22     user_basedn => 'ou=foobar',
23     user_filter => '(&(objectClass=inetOrgPerson)(uid=%s))',
24     user_scope  => 'one',
25     user_field  => 'uid',
26 });
27 is $back_without_use_roles->use_roles, 1, 'use_roles enabled be default';
28
29 my $back_with_use_roles_disabled = Catalyst::Authentication::Store::LDAP::Backend->new({
30     ldap_server => 'ldap://127.0.0.1:555',
31     binddn      => 'anonymous',
32     bindpw      => 'dontcarehow',
33     user_basedn => 'ou=foobar',
34     user_filter => '(&(objectClass=inetOrgPerson)(uid=%s))',
35     user_scope  => 'one',
36     user_field  => 'uid',
37     use_roles   => 0,
38 });
39 is $back_with_use_roles_disabled->use_roles, 0, 'use_roles disabled when set
40 to 0';
41
42 my $back_with_use_roles_enabled = Catalyst::Authentication::Store::LDAP::Backend->new({
43     ldap_server => 'ldap://127.0.0.1:555',
44     binddn      => 'anonymous',
45     bindpw      => 'dontcarehow',
46     user_basedn => 'ou=foobar',
47     user_filter => '(&(objectClass=inetOrgPerson)(uid=%s))',
48     user_scope  => 'one',
49     user_field  => 'uid',
50     use_roles   => 1,
51 });
52 is $back_with_use_roles_enabled->use_roles, 1, 'use_roles enabled when set to
53 1';
54
55 my (@searches, @binds);
56 for my $i (0..1) {
57
58     my $back = Catalyst::Authentication::Store::LDAP::Backend->new({
59         'ldap_server' => 'ldap://127.0.0.1:555',
60         'binddn'      => 'anonymous',
61         'bindpw'      => 'dontcarehow',
62         'start_tls'   => 0,
63         'user_basedn' => 'ou=foobar',
64         'user_filter' => '(&(objectClass=inetOrgPerson)(uid=%s))',
65         'user_scope'  => 'one',
66         'user_field'  => 'uid',
67         'use_roles'   => 1,
68         'role_basedn' => 'ou=roles',
69         'role_filter' => '(&(objectClass=posixGroup)(memberUid=%s))',
70         'role_scope'  => 'one',
71         'role_field'  => 'userinrole',
72         'role_value'  => 'cn',
73         'role_search_as_user' => $i,
74     });
75     $back = Test::MockObject::Extends->new($back);
76     my $bind_msg = Test::MockObject->new;
77     $bind_msg->mock(is_error => sub {}); # Cause bind call to always succeed
78     my $ldap = Test::MockObject->new;
79     $ldap->mock('bind', sub { shift; push (@binds, [@_]); return $bind_msg});
80     $ldap->mock('unbind' => sub {});
81     $ldap->mock('disconnect' => sub {});
82     my $search_res = Test::MockObject->new();
83     my $search_is_error = 0;
84     $search_res->mock(is_error => sub { $search_is_error });
85     $search_res->mock(entries => sub {
86         return map 
87             {   my $id = $_; 
88                 Test::MockObject->new->mock(
89                     get_value => sub { "quux$id" }
90                 ) 
91             }
92             qw/one two/
93     });
94     my @user_entries;
95     $search_res->mock(pop_entry => sub { return pop @user_entries });
96     $ldap->mock('search', sub { shift; push(@searches, [@_]); return $search_res; });
97     $back->mock('ldap_connect' => sub { $ldap });
98     my $user_entry = Net::LDAP::Entry->new;
99     push(@user_entries, $user_entry);
100     $user_entry->dn('ou=foobar');
101     $user_entry->add(
102         uid => 'somebody',
103         cn => 'test',
104     );
105     my $user = $back->find_user( { username => 'somebody' } );
106     isa_ok( $user, "Catalyst::Authentication::Store::LDAP::User" );
107     $user->check_password('password');
108     is_deeply( [sort $user->roles], 
109                [sort qw/quuxone quuxtwo/], 
110                 "User has the expected set of roles" );
111
112     $search_is_error = 1;
113     lives_ok {
114         ok !$back->find_user( { username => 'doesnotexist' } ),
115             'Nonexistent user returns undef';
116     } 'No exception thrown for nonexistent user';
117
118 }
119 is_deeply(\@searches, [ 
120     ['base', 'ou=foobar', 'filter', '(&(objectClass=inetOrgPerson)(uid=somebody))', 'scope', 'one'],
121     ['base', 'ou=roles', 'filter', '(&(objectClass=posixGroup)(memberUid=test))', 'scope', 'one', 'attrs', [ 'userinrole' ]],
122     ['base', 'ou=foobar', 'filter', '(&(objectClass=inetOrgPerson)(uid=doesnotexist))', 'scope', 'one'],
123     ['base', 'ou=foobar', 'filter', '(&(objectClass=inetOrgPerson)(uid=somebody))', 'scope', 'one'],
124     ['base', 'ou=roles', 'filter', '(&(objectClass=posixGroup)(memberUid=test))', 'scope', 'one', 'attrs', [ 'userinrole' ]],
125     ['base', 'ou=foobar', 'filter', '(&(objectClass=inetOrgPerson)(uid=doesnotexist))', 'scope', 'one'],
126 ], 'User searches as expected');
127 is_deeply(\@binds, [
128     [ undef ], # First user search
129     [
130         'ou=foobar',
131         'password',
132         'password'
133     ], # Rebind to confirm user
134     [
135         undef
136     ], # Rebind with initial credentials to find roles
137     [ undef ], # Second user search
138     # 2nd pass round main loop
139     [  undef ], # First user search
140     [
141         'ou=foobar',
142         'password',
143         'password'
144     ], # Rebind to confirm user
145     [
146         'ou=foobar',
147         'password',
148         'password'
149     ], # Rebind with user credentials to find roles
150     [ undef ], # Second user search
151 ], 'Binds as expected');
152
153 done_testing;