ruby-ldapserver 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +3 -0
- data/.github/workflows/ci.yml +43 -0
- data/.gitignore +1 -0
- data/{ChangeLog → CHANGELOG.md} +28 -13
- data/README.md +141 -0
- data/examples/rbslapd1.rb +5 -4
- data/examples/rbslapd2.rb +1 -1
- data/examples/rbslapd3.rb +4 -4
- data/examples/rbslapd4.rb +90 -0
- data/examples/rbslapd5.rb +73 -0
- data/examples/rbslapd6.rb +75 -0
- data/lib/ldap/server/connection.rb +16 -10
- data/lib/ldap/server/dn.rb +220 -0
- data/lib/ldap/server/filter.rb +1 -1
- data/lib/ldap/server/operation.rb +47 -9
- data/lib/ldap/server/request.rb +166 -0
- data/lib/ldap/server/result.rb +1 -1
- data/lib/ldap/server/router.rb +220 -0
- data/lib/ldap/server/server.rb +25 -10
- data/lib/ldap/server/syntax.rb +1 -1
- data/lib/ldap/server/tcpserver.rb +16 -3
- data/lib/ldap/server/trie.rb +92 -0
- data/lib/ldap/server/version.rb +1 -1
- data/ruby-ldapserver.gemspec +9 -9
- data/test/dn_test.rb +149 -0
- data/test/encoding_test.rb +142 -179
- data/test/trie_test.rb +60 -0
- data.tar.gz.sig +2 -0
- metadata +84 -22
- metadata.gz.sig +0 -0
- data/README +0 -222
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'ldap/server/dn'
|
2
|
+
require 'ldap/server/util'
|
3
|
+
require 'ldap/server/trie'
|
4
|
+
require 'ldap/server/request'
|
5
|
+
require 'ldap/server/filter'
|
6
|
+
|
7
|
+
module LDAP
|
8
|
+
class Server
|
9
|
+
class Router
|
10
|
+
@logger
|
11
|
+
@routes
|
12
|
+
|
13
|
+
# Scope
|
14
|
+
BaseObject = 0
|
15
|
+
SingleLevel = 1
|
16
|
+
WholeSubtree = 2
|
17
|
+
|
18
|
+
# DerefAliases
|
19
|
+
NeverDerefAliases = 0
|
20
|
+
DerefInSearching = 1
|
21
|
+
DerefFindingBaseObj = 2
|
22
|
+
DerefAlways = 3
|
23
|
+
|
24
|
+
def initialize(logger, &block)
|
25
|
+
@logger = logger
|
26
|
+
|
27
|
+
@routes = Hash.new
|
28
|
+
@routes = Trie.new do |trie|
|
29
|
+
# Add an artificial LDAP component
|
30
|
+
trie << "op=bind"
|
31
|
+
trie << "op=search"
|
32
|
+
end
|
33
|
+
|
34
|
+
self.instance_eval(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def log_exception(e, level = :error)
|
38
|
+
@logger.send level, e.message
|
39
|
+
e.backtrace.each { |line| @logger.send level, "\tfrom#{line}" } if e.backtrace
|
40
|
+
end
|
41
|
+
|
42
|
+
######################
|
43
|
+
### Initialization ###
|
44
|
+
######################
|
45
|
+
|
46
|
+
def route(operation, hash)
|
47
|
+
hash.each do |key, value|
|
48
|
+
if key.nil?
|
49
|
+
@routes.insert "op=#{operation.to_s}", value
|
50
|
+
@logger.debug "map operation #{operation.to_s} all routes to #{value}"
|
51
|
+
else
|
52
|
+
@routes.insert "#{key},op=#{operation.to_s}", value
|
53
|
+
@logger.debug "map #{operation.to_s} #{key} to #{value}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def method_missing(name, *args, &block)
|
59
|
+
if [:bind, :search, :add, :modify, :modifydn, :del, :compare].include? name
|
60
|
+
send :route, name, *args
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
####################################################
|
68
|
+
### Methods to parse and route each request type ###
|
69
|
+
####################################################
|
70
|
+
def parse_route(dn, method)
|
71
|
+
route, action = @routes.match("#{dn},op=#{method.to_s}")
|
72
|
+
if not route or route.empty?
|
73
|
+
@logger.warn "No route defined for \'#{route}\'"
|
74
|
+
raise LDAP::ResultError::UnwillingToPerform
|
75
|
+
end
|
76
|
+
if action.nil?
|
77
|
+
@logger.error "No action defined for route \'#{route}\'"
|
78
|
+
raise LDAP::ResultError::UnwillingToPerform
|
79
|
+
end
|
80
|
+
|
81
|
+
class_name = action.split('#').first
|
82
|
+
method_name = action.split('#').last
|
83
|
+
|
84
|
+
params = LDAP::Server::DN.new("#{dn},op=#{method.to_s}").parse(route)
|
85
|
+
|
86
|
+
return class_name, method_name, params
|
87
|
+
end
|
88
|
+
|
89
|
+
def do_bind(connection, messageId, protocolOp, controls) # :nodoc:
|
90
|
+
request = Request.new(connection, messageId)
|
91
|
+
version = protocolOp.value[0].value
|
92
|
+
dn = protocolOp.value[1].value
|
93
|
+
dn = nil if dn.empty?
|
94
|
+
authentication = protocolOp.value[2]
|
95
|
+
|
96
|
+
@logger.debug "subject:#{connection.binddn} predicate:bind object:#{dn}"
|
97
|
+
|
98
|
+
# Find a route in the routing tree
|
99
|
+
class_name, method_name, params = parse_route(dn, :bind)
|
100
|
+
|
101
|
+
case authentication.tag # tag_class == :CONTEXT_SPECIFIC (check why)
|
102
|
+
when 0
|
103
|
+
Object.const_get(class_name).send method_name, request, version, dn, authentication.value, params
|
104
|
+
when 3
|
105
|
+
mechanism = authentication.value[0].value
|
106
|
+
credentials = authentication.value[1].value
|
107
|
+
# sasl_bind(version, dn, mechanism, credentials)
|
108
|
+
# FIXME: needs to exchange further BindRequests
|
109
|
+
# route_sasl_bind(request, version, dn, mechanism, credentials)
|
110
|
+
raise LDAP::ResultError::AuthMethodNotSupported
|
111
|
+
else
|
112
|
+
raise LDAP::ResultError::ProtocolError, "BindRequest bad AuthenticationChoice"
|
113
|
+
end
|
114
|
+
request.send_BindResponse(0)
|
115
|
+
return dn, version
|
116
|
+
rescue NoMethodError => e
|
117
|
+
log_exception e
|
118
|
+
request.send_BindResponse(LDAP::ResultError::OperationsError.new.to_i, :errorMessage => e.message)
|
119
|
+
return nil, version
|
120
|
+
rescue LDAP::ResultError => e
|
121
|
+
log_exception e
|
122
|
+
request.send_BindResponse(e.to_i, :errorMessage => e.message)
|
123
|
+
return nil, version
|
124
|
+
end
|
125
|
+
|
126
|
+
def do_search(connection, messageId, protocolOp, controls) # :nodoc:
|
127
|
+
request = Request.new(connection, messageId)
|
128
|
+
server = connection.opt[:server]
|
129
|
+
schema = connection.opt[:schema]
|
130
|
+
baseObject = protocolOp.value[0].value
|
131
|
+
scope = protocolOp.value[1].value
|
132
|
+
deref = protocolOp.value[2].value
|
133
|
+
client_sizelimit = protocolOp.value[3].value
|
134
|
+
client_timelimit = protocolOp.value[4].value.to_i
|
135
|
+
request.typesOnly = protocolOp.value[5].value
|
136
|
+
filter = LDAP::Server::Filter::parse(protocolOp.value[6], schema)
|
137
|
+
request.attributes = protocolOp.value[7].value.collect {|x| x.value}
|
138
|
+
|
139
|
+
sizelimit = request.server_sizelimit
|
140
|
+
sizelimit = client_sizelimit if client_sizelimit > 0 and
|
141
|
+
(sizelimit.nil? or client_sizelimit < sizelimit)
|
142
|
+
request.sizelimit = sizelimit
|
143
|
+
|
144
|
+
if baseObject.empty? and scope == BaseObject
|
145
|
+
request.send_SearchResultEntry("", server.root_dse) if
|
146
|
+
server.root_dse and LDAP::Server::Filter.run(filter, server.root_dse)
|
147
|
+
request.send_SearchResultDone(0)
|
148
|
+
return
|
149
|
+
elsif schema and baseObject == schema.subschema_dn
|
150
|
+
request.send_SearchResultEntry(baseObject, schema.subschema_subentry) if
|
151
|
+
schema and schema.subschema_subentry and
|
152
|
+
LDAP::Server::Filter.run(filter, schema.subschema_subentry)
|
153
|
+
request.send_SearchResultDone(0)
|
154
|
+
return
|
155
|
+
end
|
156
|
+
|
157
|
+
t = request.server_timelimit || 10
|
158
|
+
t = client_timelimit if client_timelimit > 0 and client_timelimit < t
|
159
|
+
|
160
|
+
@logger.debug "subject:#{connection.binddn} predicate:search object:#{baseObject}"
|
161
|
+
|
162
|
+
# Find a route in the routing tree
|
163
|
+
class_name, method_name, params = parse_route(baseObject, :search)
|
164
|
+
|
165
|
+
Timeout::timeout(t, LDAP::ResultError::TimeLimitExceeded) do
|
166
|
+
Object.const_get(class_name).send method_name, request, baseObject, scope, deref, filter, params
|
167
|
+
end
|
168
|
+
request.send_SearchResultDone(0)
|
169
|
+
|
170
|
+
# Note that TimeLimitExceeded is a subclass of LDAP::ResultError
|
171
|
+
rescue LDAP::ResultError => e
|
172
|
+
request.send_SearchResultDone(e.to_i, :errorMessage=>e.message)
|
173
|
+
|
174
|
+
rescue Abandon
|
175
|
+
# send no response
|
176
|
+
|
177
|
+
# Since this Operation is running in its own thread, we have to
|
178
|
+
# catch all other exceptions. Otherwise, in the event of a programming
|
179
|
+
# error, this thread will silently terminate and the client will wait
|
180
|
+
# forever for a response.
|
181
|
+
|
182
|
+
rescue Exception => e
|
183
|
+
log_exception e
|
184
|
+
request.send_SearchResultDone(LDAP::ResultError::OperationsError.new.to_i, :errorMessage=>e.message)
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
###########################################################
|
189
|
+
### Methods to actually perform the work requested ###
|
190
|
+
### Use the signatures below to write your own handlers ###
|
191
|
+
###########################################################
|
192
|
+
|
193
|
+
# Handle a simple bind request; raise an exception if the bind is
|
194
|
+
# not acceptable, otherwise just return to accept the bind.
|
195
|
+
#
|
196
|
+
# Write your own class method using this signature
|
197
|
+
|
198
|
+
# def simple_bind(request, version, dn, password, params)
|
199
|
+
# if version != 3
|
200
|
+
# raise LDAP::ResultError::ProtocolError, "version 3 only"
|
201
|
+
# end
|
202
|
+
# if dn
|
203
|
+
# raise LDAP::ResultError::InappropriateAuthentication, "This server only supports anonymous bind"
|
204
|
+
# end
|
205
|
+
# end
|
206
|
+
|
207
|
+
# Handle a search request
|
208
|
+
#
|
209
|
+
# Call request. send_SearchResultEntry for each result found. Raise
|
210
|
+
# an exception if there is a problem. timeLimit, sizeLimit and
|
211
|
+
# typesOnly are taken care of, but you need to perform all
|
212
|
+
# authorisation checks yourself, using @connection.binddn
|
213
|
+
|
214
|
+
# def search(basedn, scope, deref, filter)
|
215
|
+
# debug "search(#{basedn}, #{scope}, #{deref}, #{filter})"
|
216
|
+
# raise LDAP::ResultError::UnwillingToPerform, "search not implemented"
|
217
|
+
# end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
data/lib/ldap/server/server.rb
CHANGED
@@ -15,20 +15,31 @@ class Server
|
|
15
15
|
|
16
16
|
# Create a new server. Options include all those to tcpserver/preforkserver
|
17
17
|
# plus:
|
18
|
-
#
|
19
|
-
# :
|
20
|
-
#
|
21
|
-
# :
|
22
|
-
# :
|
23
|
-
#
|
24
|
-
|
18
|
+
# either
|
19
|
+
# :router=>Router - request router instance
|
20
|
+
# or
|
21
|
+
# :operation_class=>Class - set Operation handler class
|
22
|
+
# :operation_args=>[...] - args to Operation.new
|
23
|
+
#
|
24
|
+
# :ssl_key_file=>pem, :ssl_cert_file=>pem - enable SSL
|
25
|
+
# :ssl_ca_path=>directory - verify peer certificates
|
26
|
+
# :schema=>Schema - Schema object
|
27
|
+
# :namingContexts=>[dn, ...] - base DN(s) we answer
|
28
|
+
#
|
29
|
+
# Specifying a :router always overrides :operation_class
|
30
|
+
|
25
31
|
attr_reader :logger
|
26
32
|
|
27
33
|
def initialize(opt = DEFAULT_OPT)
|
28
34
|
@opt = opt
|
29
35
|
@opt[:server] = self
|
30
|
-
@opt[:
|
31
|
-
|
36
|
+
if @opt[:router]
|
37
|
+
@opt.delete(:operation_class)
|
38
|
+
@opt.delete(:operation_args)
|
39
|
+
else
|
40
|
+
@opt[:operation_class] ||= LDAP::Server::Operation
|
41
|
+
@opt[:operation_args] ||= []
|
42
|
+
end
|
32
43
|
unless @opt[:logger]
|
33
44
|
@opt[:logger] ||= Logger.new($stderr)
|
34
45
|
@opt[:logger].level = Logger::INFO
|
@@ -92,7 +103,11 @@ class Server
|
|
92
103
|
end
|
93
104
|
|
94
105
|
def join
|
95
|
-
|
106
|
+
begin
|
107
|
+
@thread.join
|
108
|
+
rescue Interrupt
|
109
|
+
@logger.info "Exiting..."
|
110
|
+
end
|
96
111
|
end
|
97
112
|
|
98
113
|
def stop
|
data/lib/ldap/server/syntax.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'socket'
|
2
|
+
require 'fileutils'
|
2
3
|
|
3
4
|
module LDAP
|
4
5
|
class Server
|
@@ -22,13 +23,25 @@ class Server
|
|
22
23
|
# :nodelay=>true - set TCP_NODELAY option
|
23
24
|
|
24
25
|
def self.tcpserver(opt, &blk)
|
25
|
-
|
26
|
+
if opt[:socket]
|
27
|
+
server = UNIXServer.new(opt[:socket])
|
28
|
+
FileUtils.chmod(0777, opt[:socket])
|
29
|
+
else
|
30
|
+
server = TCPServer.new(opt[:bindaddr] || "0.0.0.0", opt[:port])
|
31
|
+
end
|
26
32
|
|
27
33
|
# Drop privileges if requested
|
28
34
|
require 'etc' if opt[:group] or opt[:user]
|
29
35
|
Process.gid = Process.egid = Etc.getgrnam(opt[:group]).gid if opt[:group]
|
30
36
|
Process.uid = Process.euid = Etc.getpwnam(opt[:user]).uid if opt[:user]
|
31
|
-
|
37
|
+
|
38
|
+
Process.gid = opt[:gid] if opt[:gid]
|
39
|
+
Process.uid = opt[:uid] if opt[:uid]
|
40
|
+
|
41
|
+
if opt[:socket]
|
42
|
+
FileUtils.chown((opt[:user] || opt[:uid]), (opt[:group] || opt[:gid]), opt[:socket])
|
43
|
+
end
|
44
|
+
|
32
45
|
# Typically the O/S will buffer response data for 100ms before sending.
|
33
46
|
# If the response is sent as a single write() then there's no need for it.
|
34
47
|
if opt[:nodelay]
|
@@ -86,4 +99,4 @@ if __FILE__ == $0
|
|
86
99
|
end
|
87
100
|
#sleep 10; t.raise Interrupt # uncomment to run for fixed time period
|
88
101
|
t.join
|
89
|
-
end
|
102
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'ldap/server/dn'
|
2
|
+
|
3
|
+
module LDAP
|
4
|
+
class Server
|
5
|
+
class Trie
|
6
|
+
|
7
|
+
# Trie or prefix tree suitable for storing LDAP paths
|
8
|
+
# Variables (wildcards) are supported
|
9
|
+
|
10
|
+
class NodeNotFoundError < Error; end
|
11
|
+
|
12
|
+
attr_accessor :parent, :value, :children
|
13
|
+
|
14
|
+
# Create a new Trie. Use with a block
|
15
|
+
def initialize(parent = nil, value = nil)
|
16
|
+
@parent = parent
|
17
|
+
@value = value
|
18
|
+
@children = Hash.new
|
19
|
+
|
20
|
+
yield self if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Insert a path (empty node)
|
24
|
+
def <<(dn)
|
25
|
+
insert(dn)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Insert a node with a value
|
29
|
+
def insert(dn, value = nil)
|
30
|
+
dn = LDAP::Server::DN.new(dn || '') if not dn.is_a? LDAP::Server::DN
|
31
|
+
dn.reverse_each do |component|
|
32
|
+
@children[component] = Trie.new(self) if @children[component].nil?
|
33
|
+
dn.dname.pop
|
34
|
+
if dn.any?
|
35
|
+
@children[component].insert dn, value
|
36
|
+
else
|
37
|
+
@children[component].value = value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Looks up a node and returns its value or raises
|
43
|
+
# LDAP::Server::Trie::NodeNotFoundError if it's not in the tree
|
44
|
+
def lookup(dn)
|
45
|
+
dn = LDAP::Server::DN.new(dn || '') if not dn.is_a? LDAP::Server::DN
|
46
|
+
return @value if dn.dname.empty?
|
47
|
+
component = dn.dname.pop
|
48
|
+
@children.each do |key, value|
|
49
|
+
if key.keys.first == component.keys.first
|
50
|
+
if key.values.first.start_with?(':') or key.values.first == component.values.first
|
51
|
+
return value.lookup dn
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
raise NodeNotFoundError
|
56
|
+
end
|
57
|
+
|
58
|
+
# Looks up a node and returns its value or the (non-nil) value of
|
59
|
+
# the nearest ancestor.
|
60
|
+
def match(dn, path = '')
|
61
|
+
dn = LDAP::Server::DN.new(dn || '') if not dn.is_a? LDAP::Server::DN
|
62
|
+
return path, @value if dn.dname.empty?
|
63
|
+
component = dn.dname.pop
|
64
|
+
@children.each do |key, value|
|
65
|
+
if key.keys.first == component.keys.first
|
66
|
+
if key.values.first.start_with?(':') or key.values.first == component.values.first
|
67
|
+
path.prepend ',' unless path.empty?
|
68
|
+
path.prepend "#{LDAP::Server::DN.join key}"
|
69
|
+
new_path, new_value = value.match dn, path
|
70
|
+
if new_value
|
71
|
+
return new_path, new_value
|
72
|
+
else
|
73
|
+
return (@value ? path : nil), @value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
return path, @value
|
79
|
+
end
|
80
|
+
|
81
|
+
def print_tree(prefix = '')
|
82
|
+
if @value
|
83
|
+
p "#{prefix}{{#{@value}}}"
|
84
|
+
end
|
85
|
+
@children.each do |key, value|
|
86
|
+
p "#{prefix}#{key.keys.first} => #{key.values.first}"
|
87
|
+
@children[key].print_tree("#{prefix} ")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/ldap/server/version.rb
CHANGED
data/ruby-ldapserver.gemspec
CHANGED
@@ -7,21 +7,21 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.name = %q{ruby-ldapserver}
|
8
8
|
s.version = LDAP::Server::VERSION
|
9
9
|
|
10
|
-
s.authors = ["Brian Candler"]
|
10
|
+
s.authors = ["Brian Candler", "Florian Dejonckheere", "Lars Kanis"]
|
11
11
|
s.description = %q{ruby-ldapserver is a lightweight, pure-Ruby skeleton for implementing LDAP server applications.}
|
12
|
-
s.email =
|
12
|
+
s.email = ["B.Candler@pobox.com", "florian@floriandejonckheere.be", "lars@greiz-reinsdorf.de"]
|
13
13
|
s.files = `git ls-files`.split($/)
|
14
|
-
s.homepage = %q{https://github.com/
|
15
|
-
s.rdoc_options = ["--main", "README.
|
14
|
+
s.homepage = %q{https://github.com/larskanis/ruby-ldapserver}
|
15
|
+
s.rdoc_options = ["--main", "README.md"]
|
16
16
|
s.require_paths = ["lib"]
|
17
17
|
s.summary = %q{A pure-Ruby framework for building LDAP servers}
|
18
18
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
19
19
|
|
20
|
-
s.required_ruby_version = '>=
|
20
|
+
s.required_ruby_version = '>= 2.3'
|
21
21
|
|
22
|
-
s.add_development_dependency 'bundler', '
|
23
|
-
s.add_development_dependency 'rake', '~>
|
24
|
-
s.add_development_dependency '
|
25
|
-
s.add_development_dependency 'jruby-ldap', '~> 0.0' if RUBY_PLATFORM == 'java'
|
22
|
+
s.add_development_dependency 'bundler', '>= 1.3', '< 3.0'
|
23
|
+
s.add_development_dependency 'rake', '~> 13.0'
|
24
|
+
s.add_development_dependency 'net-ldap', '~> 0.10'
|
26
25
|
s.add_development_dependency 'rspec', '~> 3.1'
|
26
|
+
s.add_development_dependency 'test-unit', '~> 3.0'
|
27
27
|
end
|
data/test/dn_test.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require 'ldap/server/dn'
|
3
|
+
|
4
|
+
class TestLdapDn < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@dn = LDAP::Server::DN.new("cn=Steve Kille,o=Isode Limited,o=Companies,c=GB")
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_find_first
|
10
|
+
assert_equal(nil, @dn.find_first("ou"))
|
11
|
+
assert_equal("Steve Kille", @dn.find_first("cn"))
|
12
|
+
assert_equal("Isode Limited", @dn.find_first("o"))
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_find_last
|
16
|
+
assert_equal(nil, @dn.find_last("ou"))
|
17
|
+
assert_equal("Steve Kille", @dn.find_last("cn"))
|
18
|
+
assert_equal("Companies", @dn.find_last("o"))
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_find
|
22
|
+
assert_equal([], @dn.find("ou"))
|
23
|
+
assert_equal(["Steve Kille"], @dn.find("cn"))
|
24
|
+
assert_equal(["Isode Limited", "Companies"], @dn.find("o"))
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_find_nth
|
28
|
+
assert_equal(nil, @dn.find_nth("ou", 0))
|
29
|
+
assert_equal("Steve Kille", @dn.find_nth("cn", 0))
|
30
|
+
assert_equal(nil, @dn.find_nth("cn", 1))
|
31
|
+
assert_equal("Isode Limited", @dn.find_nth("o", 0))
|
32
|
+
assert_equal("Companies", @dn.find_nth("o", 1))
|
33
|
+
assert_equal(nil, @dn.find_nth("o", 2))
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_start_with
|
37
|
+
assert @dn.start_with?("cn=Steve Kille")
|
38
|
+
assert @dn.start_with?("cn=Steve Kille, o=Isode Limited")
|
39
|
+
refute @dn.start_with?("cn=John Doe")
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_start_with_format
|
43
|
+
assert @dn.start_with_format?("cn=Steve Kille")
|
44
|
+
assert @dn.start_with_format?("cn=foo, o=bar")
|
45
|
+
refute @dn.start_with_format?("c=GB")
|
46
|
+
refute @dn.start_with_format?("c=BE")
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_end_with
|
50
|
+
assert @dn.end_with?("c=GB")
|
51
|
+
assert @dn.end_with?("o=Companies, c=GB")
|
52
|
+
refute @dn.end_with?("c=BE")
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_end_with_format
|
56
|
+
assert @dn.end_with_format?("c=GB")
|
57
|
+
assert @dn.end_with_format?("o=foo, c=bar")
|
58
|
+
refute @dn.end_with_format?("cn=Steve Kille")
|
59
|
+
refute @dn.end_with_format?("cn=foo")
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_equal
|
63
|
+
assert @dn.equal?("CN=Steve Kille, o=Isode Limited,O=Companies,c=GB")
|
64
|
+
refute @dn.equal?("cn=John Doe,o=Isode Limited,o=Companies,c=GB")
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_equal_format
|
68
|
+
assert @dn.equal_format?("cn=Steve Kille,o=Isode Limited,o=Companies,c=GB")
|
69
|
+
assert @dn.equal_format?("cn=STEVE KILLE,o=ISODE LIMITED,o=COMPANIES,c=GB")
|
70
|
+
assert @dn.equal_format?("cn=foo,o=bar,o=baz,c=bat")
|
71
|
+
assert @dn.equal_format?("CN=foo,O=bar,O=baz,C=bat")
|
72
|
+
refute @dn.equal_format?("cn=foo,o=Isode Limited,c=GB")
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_include
|
76
|
+
assert @dn.include?("cn=Steve Kille,o=Isode Limited,o=Companies,c=GB")
|
77
|
+
assert @dn.include?("cn=Steve Kille")
|
78
|
+
assert @dn.include?("o=Isode Limited")
|
79
|
+
assert @dn.include?("o=Isode Limited,o=Companies")
|
80
|
+
assert @dn.include?("c=GB")
|
81
|
+
|
82
|
+
refute @dn.include?("cn=John Doe,o=Isode Limited,o=Companies,c=GB")
|
83
|
+
refute @dn.include?("cn=Steve Kille,o=Isode Limited,c=GB")
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_include_format
|
87
|
+
assert @dn.include_format?("cn=foo,o=bar,o=baz,c=bat")
|
88
|
+
assert @dn.include_format?("cn=foo")
|
89
|
+
assert @dn.include_format?("o=bar")
|
90
|
+
assert @dn.include_format?("o=bar,o=baz")
|
91
|
+
assert @dn.include_format?("c=bat")
|
92
|
+
|
93
|
+
refute @dn.include_format?("cn=foo,o=bar,c=bat")
|
94
|
+
refute @dn.include_format?("cn=bar,c=bat")
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_parse
|
98
|
+
assert_equal @dn.parse("cn=:cn,o=:company,o=Companies,c=:country"),
|
99
|
+
{
|
100
|
+
:cn => 'Steve Kille',
|
101
|
+
:company => 'Isode Limited',
|
102
|
+
:country => 'GB'
|
103
|
+
}
|
104
|
+
assert_equal @dn.parse("cn=:cn,o=:company,o=:company,c=:country"),
|
105
|
+
{
|
106
|
+
:cn => 'Steve Kille',
|
107
|
+
:company => 'Companies',
|
108
|
+
:country => 'GB'
|
109
|
+
}
|
110
|
+
assert_empty @dn.parse("cn=Steve Kille,o=Isode Limited,o=Companies,c=GB")
|
111
|
+
|
112
|
+
assert_equal @dn.parse("cn=:cn,cn=Steve Kille,o=Isode Limited,o=Companies,c=GB"),
|
113
|
+
{
|
114
|
+
:cn => nil
|
115
|
+
}
|
116
|
+
|
117
|
+
assert_empty @dn.parse("o=Foo,o=Companies,c=GB")
|
118
|
+
assert_empty @dn.parse("cn=:cn,o=Foo,o=Companies,c=GB")
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_each
|
123
|
+
answer = [
|
124
|
+
{ 'cn' => 'Steve Kille' },
|
125
|
+
{ 'o' => 'Isode Limited' },
|
126
|
+
{ 'o' => 'Companies' },
|
127
|
+
{ 'c' => 'GB' }
|
128
|
+
]
|
129
|
+
i = 0
|
130
|
+
@dn.each do |pair|
|
131
|
+
assert_equal pair, answer[i]
|
132
|
+
i += 1
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_reverse_each
|
137
|
+
answer = [
|
138
|
+
{ 'c' => 'GB' },
|
139
|
+
{ 'o' => 'Companies' },
|
140
|
+
{ 'o' => 'Isode Limited' },
|
141
|
+
{ 'cn' => 'Steve Kille' }
|
142
|
+
]
|
143
|
+
i = 0
|
144
|
+
@dn.reverse_each do |pair|
|
145
|
+
assert_equal pair, answer[i]
|
146
|
+
i += 1
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|