Skip to content

Commit 091e453

Browse files
committed
Create RT::Test::LDAP for common LDAP test logic
In the interests of not repeating code, let's centralise it.
1 parent 33ca7cd commit 091e453

File tree

13 files changed

+358
-506
lines changed

13 files changed

+358
-506
lines changed

lib/RT/Test/LDAP.pm

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# BEGIN BPS TAGGED BLOCK {{{
2+
#
3+
# COPYRIGHT:
4+
#
5+
# This software is Copyright (c) 1996-2023 Best Practical Solutions, LLC
6+
# <sales@bestpractical.com>
7+
#
8+
# (Except where explicitly superseded by other copyright notices)
9+
#
10+
#
11+
# LICENSE:
12+
#
13+
# This work is made available to you under the terms of Version 2 of
14+
# the GNU General Public License. A copy of that license should have
15+
# been provided with this software, but in any event can be snarfed
16+
# from www.gnu.org.
17+
#
18+
# This work is distributed in the hope that it will be useful, but
19+
# WITHOUT ANY WARRANTY; without even the implied warranty of
20+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21+
# General Public License for more details.
22+
#
23+
# You should have received a copy of the GNU General Public License
24+
# along with this program; if not, write to the Free Software
25+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26+
# 02110-1301 or visit their web page on the internet at
27+
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
28+
#
29+
#
30+
# CONTRIBUTION SUBMISSION POLICY:
31+
#
32+
# (The following paragraph is not intended to limit the rights granted
33+
# to you to modify and distribute this software under the terms of
34+
# the GNU General Public License and is only of importance to you if
35+
# you choose to contribute your changes and enhancements to the
36+
# community by submitting them to Best Practical Solutions, LLC.)
37+
#
38+
# By intentionally submitting any modifications, corrections or
39+
# derivatives to this work, or any other work intended for use with
40+
# Request Tracker, to Best Practical Solutions, LLC, you confirm that
41+
# you are the copyright holder for those contributions and you grant
42+
# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43+
# royalty-free, perpetual, license to use, copy, create derivative
44+
# works based on those contributions, and sublicense and distribute
45+
# those contributions and any derivatives thereof.
46+
#
47+
# END BPS TAGGED BLOCK }}}
48+
49+
# Portions Copyright 2023 Andrew Ruthven <andrew@etc.gen.nz>
50+
51+
package RT::Test::LDAP;
52+
53+
use strict;
54+
use warnings;
55+
use IO::Socket::INET;
56+
57+
use base 'RT::Test';
58+
59+
sub new {
60+
my $proto = shift;
61+
my %options = @_;
62+
my $class = ref($proto) ? ref($proto) : $proto;
63+
my $self = bless {
64+
ldap_ip => '127.0.0.1',
65+
base_dn => $options{base_dn} || 'dc=bestpractical,dc=com',
66+
}, $class;
67+
68+
# Set a base default. Some tests will add or override portions of this
69+
# hash.
70+
$self->{'externalauth'} = {
71+
'My_LDAP' => {
72+
'type' => 'ldap',
73+
'base' => $self->{'base_dn'},
74+
'filter' => '(objectClass=*)',
75+
'd_filter' => '()',
76+
'tls' => 0,
77+
'net_ldap_args' => [ version => 3 ],
78+
'attr_match_list' => [ 'Name', 'EmailAddress' ],
79+
'attr_map' => {
80+
'Name' => 'uid',
81+
'EmailAddress' => 'mail',
82+
'RealName' => 'cn',
83+
'Gecos' => 'uid',
84+
'NickName' => 'nick',
85+
},
86+
},
87+
};
88+
89+
return $self;
90+
}
91+
92+
sub import {
93+
my $class = shift;
94+
95+
$class->SUPER::import(@_, tests => undef);
96+
97+
eval {
98+
require RT::LDAPImport;
99+
require RT::Authen::ExternalAuth;
100+
require Net::LDAP::Server::Test;
101+
1;
102+
} or do {
103+
RT::Test::plan(
104+
skip_all => 'Unable to test without Net::LDAP and Net::LDAP::Server::Test'
105+
);
106+
};
107+
108+
my %args = @_;
109+
RT::Test::plan( tests => $args{'tests'} ) if $args{tests};
110+
111+
$class->export_to_level(1);
112+
}
113+
114+
sub new_server {
115+
my $self = shift;
116+
117+
$self->{'ldap_port'} = RT::Test->find_idle_port;
118+
my $ldap_socket = IO::Socket::INET->new(
119+
Listen => 5,
120+
Proto => 'tcp',
121+
Reuse => 1,
122+
LocalAddr => $self->{'ldap_ip'},
123+
LocalPort => $self->{'ldap_port'},
124+
)
125+
|| die "Failed to create socket: $IO::Socket::errstr";
126+
127+
$self->{'ldap_server'}
128+
= Net::LDAP::Server::Test->new( $ldap_socket, auto_schema => 1 )
129+
|| die "Failed to spawn test LDAP server on port " . $self->{'ldap_port'};
130+
131+
my $ldap_client
132+
= Net::LDAP->new(join(':', $self->{'ldap_ip'}, $self->{'ldap_port'}))
133+
|| die "Failed to connect to LDAP server: $@";
134+
135+
$ldap_client->bind();
136+
$ldap_client->add($self->{'base_dn'});
137+
138+
return $ldap_client;
139+
}
140+
141+
sub config_set_externalauth {
142+
my $self = shift;
143+
my $settings = shift;
144+
145+
$settings->{'ExternalAuthPriority'} //= ['My_LDAP'];
146+
$settings->{'ExternalInfoPriority'} //= ['My_LDAP'];
147+
$settings->{'AutoCreateNonExternalUsers'} //= 0;
148+
$settings->{'AutoCreate'} //= undef;
149+
150+
while (my ($key, $val) = each %{$settings}) {
151+
RT->Config->Set($key, $val);
152+
}
153+
154+
$self->{'externalauth'}{'My_LDAP'}{'server'} //=
155+
join(':', $self->{'ldap_ip'}, $self->{'ldap_port'});
156+
157+
RT->Config->Set(ExternalSettings => $self->{'externalauth'});
158+
RT->Config->PostLoadCheck;
159+
}
160+
161+
sub config_set_ldapimport {
162+
my $self = shift;
163+
my $settings = shift;
164+
165+
$settings->{'LDAPHost'}
166+
//= 'ldap://' . $self->{'ldap_ip'} . ':' . $self->{'ldap_port'};
167+
$settings->{'LDAPMapping'} //= {
168+
Name => 'uid',
169+
EmailAddress => 'mail',
170+
RealName => 'cn',
171+
};
172+
$settings->{'LDAPBase'} //= $self->{'base_dn'};
173+
$settings->{'LDAPFilter'} //= '(objectClass=User)';
174+
$settings->{'LDAPSkipAutogeneratedGroup'} //= 1;
175+
176+
while (my ($key, $val) = each %{$settings}) {
177+
RT->Config->Set($key, $val);
178+
}
179+
}
180+
181+
sub config_set_ldapimport_group {
182+
my $self = shift;
183+
my $settings = shift;
184+
185+
$settings->{'LDAPGroupBase'} //= $self->{'base_dn'};
186+
$settings->{'LDAPGroupFilter'} //= '(objectClass=Group)';
187+
188+
while (my ($key, $val) = each %{$settings}) {
189+
RT->Config->Set($key, $val);
190+
}
191+
}
192+
193+
1;

t/externalauth/ldap.t

Lines changed: 36 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
11
use strict;
22
use warnings;
3-
use IO::Socket::INET;
43

5-
use RT::Test tests => undef;
4+
use RT::Test::LDAP tests => undef;
65

7-
eval { require RT::Authen::ExternalAuth; require Net::LDAP::Server::Test; 1; } or do {
8-
plan skip_all => 'Unable to test without Net::LDAP and Net::LDAP::Server::Test';
9-
};
10-
11-
my $ldap_port = RT::Test->find_idle_port;
12-
my $ldap_socket = IO::Socket::INET->new(
13-
Listen => 5,
14-
Proto => 'tcp',
15-
Reuse => 1,
16-
LocalPort => $ldap_port,
17-
);
18-
ok( my $server = Net::LDAP::Server::Test->new( $ldap_socket, auto_schema => 1 ),
19-
"spawned test LDAP server on port $ldap_port" );
6+
my $test = RT::Test::LDAP->new();
7+
my $base = $test->{'base_dn'};
8+
my $ldap = $test->new_server();
209

21-
my $ldap = Net::LDAP->new("localhost:$ldap_port") || die "Failed to connect to LDAP server: $@";
22-
$ldap->bind();
2310
my $username = "testuser";
24-
my $base = "dc=bestpractical,dc=com";
2511
my $dn = "uid=$username,$base";
2612
my $entry = {
2713
cn => $username,
@@ -68,40 +54,24 @@ ok( $delegate_cf->Create(
6854
);
6955
ok( $delegate_cf->AddToObject( RT::User->new( RT->SystemUser ) ), 'applied Delegate globally' );
7056

71-
RT->Config->Set( ExternalAuthPriority => ['My_LDAP'] );
72-
RT->Config->Set( ExternalInfoPriority => ['My_LDAP'] );
73-
RT->Config->Set( AutoCreateNonExternalUsers => 0 );
74-
RT->Config->Set( AutoCreate => undef );
75-
RT->Config->Set(
76-
ExternalSettings => { # AN EXAMPLE DB SERVICE
77-
'My_LDAP' => {
78-
'type' => 'ldap',
79-
'server' => "127.0.0.1:$ldap_port",
80-
'base' => $base,
81-
'filter' => '(objectClass=*)',
82-
'd_filter' => '()',
83-
'tls' => 0,
84-
'net_ldap_args' => [ version => 3 ],
85-
'attr_match_list' => [ 'Name', 'EmailAddress' ],
86-
'attr_map' => {
87-
'Name' => 'uid',
88-
'EmailAddress' => 'mail',
89-
'FreeformContactInfo' => [ 'uid', 'mail' ],
90-
'CF.Employee Type' => 'employeeType',
91-
'UserCF.Employee Type' => 'employeeType',
92-
'UserCF.Employee ID' => sub {
93-
my %args = @_;
94-
return ( 'employeeType', 'employeeID' ) unless $args{external_entry};
95-
return (
96-
$args{external_entry}->get_value('employeeType') // '',
97-
$args{external_entry}->get_value('employeeID') // '',
98-
);
99-
},
100-
}
101-
},
102-
}
103-
);
104-
RT->Config->PostLoadCheck;
57+
# Just wholesale replace the default attr_map.
58+
$test->{'externalauth'}{'My_LDAP'}{'attr_map'} = {
59+
'Name' => 'uid',
60+
'EmailAddress' => 'mail',
61+
'FreeformContactInfo' => [ 'uid', 'mail' ],
62+
'CF.Employee Type' => 'employeeType',
63+
'UserCF.Employee Type' => 'employeeType',
64+
'UserCF.Employee ID' => sub {
65+
my %args = @_;
66+
return ( 'employeeType', 'employeeID' ) unless $args{external_entry};
67+
return (
68+
$args{external_entry}->get_value('employeeType') // '',
69+
$args{external_entry}->get_value('employeeID') // '',
70+
);
71+
},
72+
};
73+
74+
$test->config_set_externalauth();
10575

10676
my ( $baseurl, $m ) = RT::Test->started_ok();
10777

@@ -222,31 +192,20 @@ diag "test user update via login";
222192

223193
diag 'Login with UserCF as username';
224194

225-
RT::Test->stop_server();
226-
227-
RT->Config->Set(
228-
ExternalSettings => { # AN EXAMPLE DB SERVICE
229-
'My_LDAP' => {
230-
'type' => 'ldap',
231-
'server' => "127.0.0.1:$ldap_port",
232-
'base' => $base,
233-
'filter' => '(objectClass=*)',
234-
'd_filter' => '()',
235-
'tls' => 0,
236-
'net_ldap_args' => [ version => 3 ],
237-
'attr_match_list' => [ 'UserCF.Employee ID', 'EmailAddress' ],
238-
'attr_map' => {
239-
'Name' => 'uid',
240-
'EmailAddress' => 'mail',
241-
'FreeformContactInfo' => [ 'uid', 'mail' ],
242-
'CF.Employee Type' => 'employeeType',
243-
'UserCF.Employee Type' => 'employeeType',
244-
'UserCF.Employee ID' => 'employeeID',
245-
}
246-
},
247-
}
248-
);
249-
RT->Config->PostLoadCheck;
195+
$test->stop_server();
196+
197+
$test->{'externalauth'}{'My_LDAP'}{'attr_match_list'}
198+
= [ 'UserCF.Employee ID', 'EmailAddress' ];
199+
$test->{'externalauth'}{'My_LDAP'}{'attr_map'} = {
200+
'Name' => 'uid',
201+
'EmailAddress' => 'mail',
202+
'FreeformContactInfo' => [ 'uid', 'mail' ],
203+
'CF.Employee Type' => 'employeeType',
204+
'UserCF.Employee Type' => 'employeeType',
205+
'UserCF.Employee ID' => 'employeeID',
206+
};
207+
208+
$test->config_set_externalauth();
250209

251210
( $baseurl, $m ) = RT::Test->started_ok();
252211

t/externalauth/ldap_email_login.t

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,13 @@
11
use strict;
22
use warnings;
3-
use IO::Socket::INET;
43

5-
use RT::Test tests => undef;
4+
use RT::Test::LDAP tests => undef;
65

7-
eval { require RT::Authen::ExternalAuth; require Net::LDAP::Server::Test; 1; } or do {
8-
plan skip_all => 'Unable to test without Net::LDAP and Net::LDAP::Server::Test';
9-
};
6+
my $test = RT::Test::LDAP->new();
7+
my $base = $test->{'base_dn'};
8+
my $ldap = $test->new_server();
109

11-
my $ldap_port = RT::Test->find_idle_port;
12-
my $ldap_socket = IO::Socket::INET->new(
13-
Listen => 5,
14-
Proto => 'tcp',
15-
Reuse => 1,
16-
LocalPort => $ldap_port,
17-
);
18-
ok( my $server = Net::LDAP::Server::Test->new( $ldap_socket, auto_schema => 1 ),
19-
"spawned test LDAP server on port $ldap_port" );
20-
21-
my $ldap = Net::LDAP->new("localhost:$ldap_port") || die "Failed to connect to LDAP server: $@";
22-
$ldap->bind();
23-
24-
my $base = 'dc=bestpractical,dc=com';
25-
26-
RT->Config->Set( ExternalAuthPriority => ['My_LDAP'] );
27-
RT->Config->Set( ExternalInfoPriority => ['My_LDAP'] );
28-
RT->Config->Set( AutoCreate => undef );
29-
RT->Config->Set(
30-
ExternalSettings => {
31-
'My_LDAP' => {
32-
'type' => 'ldap',
33-
'server' => "127.0.0.1:$ldap_port",
34-
'base' => $base,
35-
'filter' => '(objectClass=*)',
36-
'd_filter' => '()',
37-
'tls' => 0,
38-
'net_ldap_args' => [ version => 3 ],
39-
'attr_match_list' => [ 'Name', 'EmailAddress' ],
40-
'attr_map' => {
41-
'Name' => 'uid',
42-
'EmailAddress' => 'mail',
43-
'RealName' => 'cn',
44-
'Gecos' => 'uid',
45-
'NickName' => 'nick',
46-
}
47-
},
48-
}
49-
);
50-
RT->Config->PostLoadCheck;
10+
$test->config_set_externalauth();
5111

5212
my ( $baseurl, $m ) = RT::Test->started_ok();
5313

0 commit comments

Comments
 (0)