Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cf65eb4

Browse files
committedJul 7, 2022
Add test suite with 100% line/branch coverage
1 parent c053bb9 commit cf65eb4

File tree

6 files changed

+459
-14
lines changed

6 files changed

+459
-14
lines changed
 

‎.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/rdoc
22
/simple_ldap_authenticator-*.gem
3+
/coverage

‎LICENSE

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
Copyright (c) 2004-2007 Jeremy Evans
1+
Copyright (c) 2006-2022 Jeremy Evans
2+
3+
test/ldapserver.rb Copyright (c) 2006-2011 by Francis Cianfrocca and other contributors.
24

35
Permission is hereby granted, free of charge, to any person obtaining a copy
46
of this software and associated documentation files (the "Software"), to deal

‎Rakefile

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
1-
require 'rake'
21
require 'rake/clean'
3-
begin
4-
require 'hanna/rdoctask'
5-
rescue LoadError
6-
require 'rake/rdoctask'
7-
end
82

9-
Rake::RDocTask.new do |rdoc|
10-
rdoc.rdoc_dir = "rdoc"
11-
rdoc.options += ["--quiet", "--line-numbers", "--inline-source"]
12-
rdoc.main = "README"
13-
rdoc.title = "simple_ldap_authenticator: Easy authentication to an LDAP server(s)"
14-
rdoc.rdoc_files.add ["README", "LICENSE", "lib/simple_ldap_authenticator.rb"]
15-
end
3+
CLEAN.include %w'*.gem coverage rdoc'
164

175
desc "Package simple_ldap_authenticator"
186
task :package do
197
sh %{gem build simple_ldap_authenticator.gemspec}
208
end
9+
10+
### Specs
11+
12+
desc "Run tests"
13+
task :test do
14+
ruby = ENV['RUBY'] ||= FileUtils::RUBY
15+
sh "#{ruby} #{"-w" if RUBY_VERSION >= '3'} test/simple_ldap_authenticator_test.rb"
16+
end
17+
18+
task :default => :test
19+
20+
desc "Run tests with coverage"
21+
task :test_cov do
22+
ruby = ENV['RUBY'] ||= FileUtils::RUBY
23+
ENV['COVERAGE'] = '1'
24+
sh "#{ruby} test/simple_ldap_authenticator_test.rb"
25+
end
26+
27+
### RDoc
28+
29+
require "rdoc/task"
30+
31+
RDoc::Task.new do |rdoc|
32+
rdoc.rdoc_dir = "rdoc"
33+
rdoc.options += ['--inline-source', '--line-numbers', '--title', 'simple_ldap_authenticator: Easy authentication to an LDAP server(s)', '--main', 'README', '-f', 'hanna']
34+
rdoc.rdoc_files.add %w"README LICENSE lib/simple_ldap_authenticator.rb"
35+
end

‎simple_ldap_authenticator.gemspec

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,9 @@ spec = Gem::Specification.new do |s|
99
s.extra_rdoc_files = ["LICENSE"]
1010
s.require_paths = ["lib"]
1111
s.rdoc_options = %w'--inline-source --line-numbers README lib'
12+
13+
s.add_development_dependency "minitest-global_expectations"
14+
s.add_development_dependency "eventmachine"
15+
s.add_development_dependency "net-ldap"
16+
s.add_development_dependency "ldap"
1217
end

‎test/ldapserver.rb

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
# $Id$
2+
#
3+
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
4+
# Gmail account: garbagecat10.
5+
#
6+
# This is an LDAP server intended for unit testing of Net::LDAP.
7+
# It implements as much of the protocol as we have the stomach
8+
# to implement but serves static data. Use ldapsearch to test
9+
# this server!
10+
#
11+
# To make this easier to write, we use the Ruby/EventMachine
12+
# reactor library.
13+
#
14+
15+
#------------------------------------------------
16+
17+
module LdapServer
18+
LdapServerAsnSyntaxTemplate = {
19+
:application => {
20+
:constructed => {
21+
0 => :array, # LDAP BindRequest
22+
3 => :array # LDAP SearchRequest
23+
},
24+
:primitive => {
25+
2 => :string, # ldapsearch sends this to unbind
26+
},
27+
},
28+
:context_specific => {
29+
:primitive => {
30+
0 => :string, # simple auth (password)
31+
7 => :string # present filter
32+
},
33+
:constructed => {
34+
3 => :array # equality filter
35+
},
36+
},
37+
}
38+
39+
def post_init
40+
#$logger.info "Accepted LDAP connection"
41+
@authenticated = false
42+
end
43+
44+
def receive_data data
45+
@data ||= ""; @data << data
46+
while pdu = @data.read_ber!(LdapServerAsnSyntax)
47+
begin
48+
handle_ldap_pdu pdu
49+
rescue
50+
#$logger.error "closing connection due to error #{$!}"
51+
close_connection
52+
end
53+
end
54+
end
55+
56+
def handle_ldap_pdu pdu
57+
tag_id = pdu[1].ber_identifier
58+
case tag_id
59+
when 0x60
60+
handle_bind_request pdu
61+
when 0x63
62+
handle_search_request pdu
63+
when 0x42
64+
# bizarre thing, it's a null object (primitive application-2)
65+
# sent by ldapsearch to request an unbind (or a kiss-off, not sure which)
66+
close_connection_after_writing
67+
else
68+
#$logger.error "received unknown packet-type #{tag_id}"
69+
close_connection_after_writing
70+
end
71+
end
72+
73+
def handle_bind_request pdu
74+
# TODO, return a proper LDAP error instead of blowing up on version error
75+
if pdu[1][0] != 3 || pdu[1][1] == "bad_version"
76+
send_ldap_response 1, pdu[0].to_i, 2, "", "We only support version 3"
77+
elsif pdu[1][1] != "user"
78+
send_ldap_response 1, pdu[0].to_i, 48, "", "Who are you?"
79+
elsif pdu[1][2].ber_identifier != 0x80
80+
send_ldap_response 1, pdu[0].to_i, 7, "", "Keep it simple, man"
81+
elsif pdu[1][2] != "password"
82+
send_ldap_response 1, pdu[0].to_i, 49, "", "Make my day"
83+
else
84+
@authenticated = true
85+
send_ldap_response 1, pdu[0].to_i, 0, pdu[1][1], "I'll take it"
86+
end
87+
end
88+
89+
# --
90+
# Search Response ::=
91+
# CHOICE {
92+
# entry [APPLICATION 4] SEQUENCE {
93+
# objectName LDAPDN,
94+
# attributes SEQUENCE OF SEQUENCE {
95+
# AttributeType,
96+
# SET OF AttributeValue
97+
# }
98+
# },
99+
# resultCode [APPLICATION 5] LDAPResult
100+
# }
101+
def handle_search_request pdu
102+
unless @authenticated
103+
# NOTE, early exit.
104+
send_ldap_response 5, pdu[0].to_i, 50, "", "Who did you say you were?"
105+
return
106+
end
107+
108+
treebase = pdu[1][0]
109+
if treebase != "dc=bayshorenetworks,dc=com"
110+
send_ldap_response 5, pdu[0].to_i, 32, "", "unknown treebase"
111+
return
112+
end
113+
114+
msgid = pdu[0].to_i.to_ber
115+
116+
# pdu[1][7] is the list of requested attributes.
117+
# If it's an empty array, that means that *all* attributes were requested.
118+
requested_attrs = if pdu[1][7].length > 0
119+
pdu[1][7].map(&:downcase)
120+
else
121+
:all
122+
end
123+
124+
filters = pdu[1][6]
125+
if filters.length == 0
126+
# NOTE, early exit.
127+
send_ldap_response 5, pdu[0].to_i, 53, "", "No filter specified"
128+
end
129+
130+
# TODO, what if this returns nil?
131+
filter = Net::LDAP::Filter.parse_ldap_filter(filters)
132+
133+
$ldif.each do |dn, entry|
134+
if filter.match(entry)
135+
attrs = []
136+
entry.each do |k, v|
137+
if requested_attrs == :all || requested_attrs.include?(k.downcase)
138+
attrvals = v.map(&:to_ber).to_ber_set
139+
attrs << [k.to_ber, attrvals].to_ber_sequence
140+
end
141+
end
142+
143+
appseq = [dn.to_ber, attrs.to_ber_sequence].to_ber_appsequence(4)
144+
pkt = [msgid.to_ber, appseq].to_ber_sequence
145+
send_data pkt
146+
end
147+
end
148+
149+
send_ldap_response 5, pdu[0].to_i, 0, "", "Was that what you wanted?"
150+
end
151+
152+
def send_ldap_response pkt_tag, msgid, code, dn, text
153+
send_data([msgid.to_ber, [code.to_ber, dn.to_ber, text.to_ber].to_ber_appsequence(pkt_tag)].to_ber)
154+
end
155+
end
156+
157+
#------------------------------------------------
158+
159+
# Rather bogus, a global method, which reads a HARDCODED filename
160+
# parses out LDIF data. It will be used to serve LDAP queries out of this server.
161+
#
162+
def load_test_data
163+
ary = (<<END).split("\n")
164+
dn: dc=bayshorenetworks,dc=com
165+
objectClass: dcObject
166+
objectClass: organization
167+
o: Bayshore Networks LLC
168+
dc: bayshorenetworks
169+
170+
dn: cn=Manager,dc=bayshorenetworks,dc=com
171+
objectClass: organizationalrole
172+
cn: Manager
173+
174+
dn: ou=people,dc=bayshorenetworks,dc=com
175+
objectClass: organizationalunit
176+
ou: people
177+
178+
dn: ou=privileges,dc=bayshorenetworks,dc=com
179+
objectClass: organizationalunit
180+
ou: privileges
181+
182+
dn: ou=roles,dc=bayshorenetworks,dc=com
183+
objectClass: organizationalunit
184+
ou: roles
185+
186+
dn: ou=office,dc=bayshorenetworks,dc=com
187+
objectClass: organizationalunit
188+
ou: office
189+
190+
dn: mail=nogoodnik@steamheat.net,ou=people,dc=bayshorenetworks,dc=com
191+
cn: Bob Fosse
192+
mail: nogoodnik@steamheat.net
193+
sn: Fosse
194+
ou: people
195+
objectClass: top
196+
objectClass: inetorgperson
197+
objectClass: authorizedperson
198+
hasAccessRole: uniqueIdentifier=engineer,ou=roles
199+
hasAccessRole: uniqueIdentifier=ldapadmin,ou=roles
200+
hasAccessRole: uniqueIdentifier=ldapsuperadmin,ou=roles
201+
hasAccessRole: uniqueIdentifier=ogilvy_elephant_user,ou=roles
202+
hasAccessRole: uniqueIdentifier=ogilvy_eagle_user,ou=roles
203+
hasAccessRole: uniqueIdentifier=greenplug_user,ou=roles
204+
hasAccessRole: uniqueIdentifier=brandplace_logging_user,ou=roles
205+
hasAccessRole: uniqueIdentifier=brandplace_report_user,ou=roles
206+
hasAccessRole: uniqueIdentifier=workorder_user,ou=roles
207+
hasAccessRole: uniqueIdentifier=bayshore_eagle_user,ou=roles
208+
hasAccessRole: uniqueIdentifier=bayshore_eagle_superuser,ou=roles
209+
hasAccessRole: uniqueIdentifier=kledaras_user,ou=roles
210+
211+
dn: mail=elephant@steamheat.net,ou=people,dc=bayshorenetworks,dc=com
212+
cn: Gwen Verdon
213+
mail: elephant@steamheat.net
214+
sn: Verdon
215+
ou: people
216+
objectClass: top
217+
objectClass: inetorgperson
218+
objectClass: authorizedperson
219+
hasAccessRole: uniqueIdentifier=brandplace_report_user,ou=roles
220+
hasAccessRole: uniqueIdentifier=engineer,ou=roles
221+
hasAccessRole: uniqueIdentifier=ogilvy_elephant_user,ou=roles
222+
hasAccessRole: uniqueIdentifier=ldapsuperadmin,ou=roles
223+
hasAccessRole: uniqueIdentifier=ldapadmin,ou=roles
224+
225+
dn: uniqueIdentifier=engineering,ou=privileges,dc=bayshorenetworks,dc=com
226+
uniqueIdentifier: engineering
227+
ou: privileges
228+
objectClass: accessPrivilege
229+
230+
dn: uniqueIdentifier=engineer,ou=roles,dc=bayshorenetworks,dc=com
231+
uniqueIdentifier: engineer
232+
ou: roles
233+
objectClass: accessRole
234+
hasAccessPrivilege: uniqueIdentifier=engineering,ou=privileges
235+
236+
dn: uniqueIdentifier=ldapadmin,ou=roles,dc=bayshorenetworks,dc=com
237+
uniqueIdentifier: ldapadmin
238+
ou: roles
239+
objectClass: accessRole
240+
241+
dn: uniqueIdentifier=ldapsuperadmin,ou=roles,dc=bayshorenetworks,dc=com
242+
uniqueIdentifier: ldapsuperadmin
243+
ou: roles
244+
objectClass: accessRole
245+
246+
dn: mail=catperson@steamheat.net,ou=people,dc=bayshorenetworks,dc=com
247+
cn: Sid Sorokin
248+
mail: catperson@steamheat.net
249+
sn: Sorokin
250+
ou: people
251+
objectClass: top
252+
objectClass: inetorgperson
253+
objectClass: authorizedperson
254+
hasAccessRole: uniqueIdentifier=engineer,ou=roles
255+
hasAccessRole: uniqueIdentifier=ogilvy_elephant_user,ou=roles
256+
hasAccessRole: uniqueIdentifier=ldapsuperadmin,ou=roles
257+
hasAccessRole: uniqueIdentifier=ogilvy_eagle_user,ou=roles
258+
hasAccessRole: uniqueIdentifier=greenplug_user,ou=roles
259+
hasAccessRole: uniqueIdentifier=workorder_user,ou=roles
260+
END
261+
hash = {}
262+
while (line = ary.shift) && line.chomp!
263+
if line =~ /^dn:[\s]*/i
264+
dn = $'
265+
hash[dn] = {}
266+
while (attr = ary.shift) && attr.chomp! && attr =~ /^([\w]+)[\s]*:[\s]*/
267+
hash[dn][$1.downcase] ||= []
268+
hash[dn][$1.downcase] << $'
269+
end
270+
end
271+
end
272+
hash
273+
end
274+
275+
#------------------------------------------------
276+
277+
if __FILE__ == $0
278+
require 'eventmachine'
279+
require 'logger'
280+
#$logger = Logger.new $stderr
281+
$ldif = load_test_data
282+
283+
require 'net/ldap'
284+
LdapServerAsnSyntax = Net::BER.compile_syntax(LdapServer::LdapServerAsnSyntaxTemplate)
285+
EventMachine.run do
286+
port = (ENV['PORT'] || 3890).to_i
287+
EventMachine.start_server "127.0.0.1", port, LdapServer
288+
#EventMachine.add_periodic_timer 60, proc { $logger.info "heartbeat" }
289+
#$logger.info "started LDAP server on 127.0.0.1 port #{port}"
290+
end
291+
end
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.