ruby-ldapserver 0.5.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|