powerhome-activeldap 3.2.3

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.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +6 -0
  3. data/COPYING +340 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE +59 -0
  6. data/README.textile +140 -0
  7. data/TODO +32 -0
  8. data/benchmark/README.md +64 -0
  9. data/benchmark/bench-backend.rb +247 -0
  10. data/benchmark/bench-instantiate.rb +98 -0
  11. data/benchmark/config.yaml.sample +5 -0
  12. data/doc/text/development.textile +54 -0
  13. data/doc/text/news.textile +811 -0
  14. data/doc/text/rails.textile +144 -0
  15. data/doc/text/tutorial.textile +1010 -0
  16. data/examples/config.yaml.example +5 -0
  17. data/examples/example.der +0 -0
  18. data/examples/example.jpg +0 -0
  19. data/examples/groupadd +41 -0
  20. data/examples/groupdel +35 -0
  21. data/examples/groupls +49 -0
  22. data/examples/groupmod +42 -0
  23. data/examples/lpasswd +55 -0
  24. data/examples/objects/group.rb +13 -0
  25. data/examples/objects/ou.rb +4 -0
  26. data/examples/objects/user.rb +20 -0
  27. data/examples/ouadd +38 -0
  28. data/examples/useradd +45 -0
  29. data/examples/useradd-binary +53 -0
  30. data/examples/userdel +34 -0
  31. data/examples/userls +50 -0
  32. data/examples/usermod +42 -0
  33. data/examples/usermod-binary-add +50 -0
  34. data/examples/usermod-binary-add-time +54 -0
  35. data/examples/usermod-binary-del +48 -0
  36. data/examples/usermod-lang-add +43 -0
  37. data/lib/active_ldap.rb +85 -0
  38. data/lib/active_ldap/action_controller/ldap_benchmarking.rb +55 -0
  39. data/lib/active_ldap/acts/tree.rb +78 -0
  40. data/lib/active_ldap/adapter/base.rb +707 -0
  41. data/lib/active_ldap/adapter/jndi.rb +184 -0
  42. data/lib/active_ldap/adapter/jndi_connection.rb +185 -0
  43. data/lib/active_ldap/adapter/ldap.rb +290 -0
  44. data/lib/active_ldap/adapter/ldap_ext.rb +105 -0
  45. data/lib/active_ldap/adapter/net_ldap.rb +309 -0
  46. data/lib/active_ldap/adapter/net_ldap_ext.rb +23 -0
  47. data/lib/active_ldap/association/belongs_to.rb +47 -0
  48. data/lib/active_ldap/association/belongs_to_many.rb +58 -0
  49. data/lib/active_ldap/association/children.rb +21 -0
  50. data/lib/active_ldap/association/collection.rb +105 -0
  51. data/lib/active_ldap/association/has_many.rb +31 -0
  52. data/lib/active_ldap/association/has_many_utils.rb +44 -0
  53. data/lib/active_ldap/association/has_many_wrap.rb +75 -0
  54. data/lib/active_ldap/association/proxy.rb +107 -0
  55. data/lib/active_ldap/associations.rb +205 -0
  56. data/lib/active_ldap/attribute_methods.rb +23 -0
  57. data/lib/active_ldap/attribute_methods/before_type_cast.rb +24 -0
  58. data/lib/active_ldap/attribute_methods/dirty.rb +43 -0
  59. data/lib/active_ldap/attribute_methods/query.rb +31 -0
  60. data/lib/active_ldap/attribute_methods/read.rb +44 -0
  61. data/lib/active_ldap/attribute_methods/write.rb +38 -0
  62. data/lib/active_ldap/attributes.rb +176 -0
  63. data/lib/active_ldap/base.rb +1410 -0
  64. data/lib/active_ldap/callbacks.rb +71 -0
  65. data/lib/active_ldap/command.rb +49 -0
  66. data/lib/active_ldap/compatible.rb +44 -0
  67. data/lib/active_ldap/configuration.rb +147 -0
  68. data/lib/active_ldap/connection.rb +299 -0
  69. data/lib/active_ldap/distinguished_name.rb +291 -0
  70. data/lib/active_ldap/entry_attribute.rb +78 -0
  71. data/lib/active_ldap/escape.rb +12 -0
  72. data/lib/active_ldap/get_text.rb +20 -0
  73. data/lib/active_ldap/get_text/parser.rb +161 -0
  74. data/lib/active_ldap/helper.rb +92 -0
  75. data/lib/active_ldap/human_readable.rb +133 -0
  76. data/lib/active_ldap/ldap_error.rb +74 -0
  77. data/lib/active_ldap/ldif.rb +930 -0
  78. data/lib/active_ldap/log_subscriber.rb +50 -0
  79. data/lib/active_ldap/object_class.rb +95 -0
  80. data/lib/active_ldap/operations.rb +624 -0
  81. data/lib/active_ldap/persistence.rb +100 -0
  82. data/lib/active_ldap/populate.rb +53 -0
  83. data/lib/active_ldap/railtie.rb +43 -0
  84. data/lib/active_ldap/railties/controller_runtime.rb +48 -0
  85. data/lib/active_ldap/schema.rb +701 -0
  86. data/lib/active_ldap/schema/syntaxes.rb +422 -0
  87. data/lib/active_ldap/timeout.rb +75 -0
  88. data/lib/active_ldap/timeout_stub.rb +17 -0
  89. data/lib/active_ldap/user_password.rb +99 -0
  90. data/lib/active_ldap/validations.rb +200 -0
  91. data/lib/active_ldap/version.rb +3 -0
  92. data/lib/active_ldap/xml.rb +139 -0
  93. data/lib/rails/generators/active_ldap/model/USAGE +18 -0
  94. data/lib/rails/generators/active_ldap/model/model_generator.rb +47 -0
  95. data/lib/rails/generators/active_ldap/model/templates/model_active_ldap.rb +3 -0
  96. data/lib/rails/generators/active_ldap/scaffold/scaffold_generator.rb +14 -0
  97. data/lib/rails/generators/active_ldap/scaffold/templates/ldap.yml +19 -0
  98. data/po/en/active-ldap.po +4029 -0
  99. data/po/ja/active-ldap.po +4060 -0
  100. data/test/add-phonetic-attribute-options-to-slapd.ldif +10 -0
  101. data/test/al-test-utils.rb +428 -0
  102. data/test/command.rb +111 -0
  103. data/test/config.yaml.sample +6 -0
  104. data/test/fixtures/lower_case_object_class_schema.rb +802 -0
  105. data/test/run-test.rb +34 -0
  106. data/test/test_acts_as_tree.rb +60 -0
  107. data/test/test_adapter.rb +121 -0
  108. data/test/test_associations.rb +701 -0
  109. data/test/test_attributes.rb +117 -0
  110. data/test/test_base.rb +1214 -0
  111. data/test/test_base_per_instance.rb +61 -0
  112. data/test/test_bind.rb +62 -0
  113. data/test/test_callback.rb +31 -0
  114. data/test/test_configuration.rb +40 -0
  115. data/test/test_connection.rb +82 -0
  116. data/test/test_connection_per_class.rb +112 -0
  117. data/test/test_connection_per_dn.rb +112 -0
  118. data/test/test_dirty.rb +98 -0
  119. data/test/test_dn.rb +172 -0
  120. data/test/test_find.rb +176 -0
  121. data/test/test_groupadd.rb +50 -0
  122. data/test/test_groupdel.rb +46 -0
  123. data/test/test_groupls.rb +107 -0
  124. data/test/test_groupmod.rb +51 -0
  125. data/test/test_ldif.rb +1890 -0
  126. data/test/test_load.rb +133 -0
  127. data/test/test_lpasswd.rb +75 -0
  128. data/test/test_object_class.rb +74 -0
  129. data/test/test_persistence.rb +131 -0
  130. data/test/test_reflection.rb +175 -0
  131. data/test/test_schema.rb +559 -0
  132. data/test/test_syntax.rb +444 -0
  133. data/test/test_user.rb +217 -0
  134. data/test/test_user_password.rb +108 -0
  135. data/test/test_useradd-binary.rb +62 -0
  136. data/test/test_useradd.rb +57 -0
  137. data/test/test_userdel.rb +48 -0
  138. data/test/test_userls.rb +91 -0
  139. data/test/test_usermod-binary-add-time.rb +65 -0
  140. data/test/test_usermod-binary-add.rb +64 -0
  141. data/test/test_usermod-binary-del.rb +66 -0
  142. data/test/test_usermod-lang-add.rb +59 -0
  143. data/test/test_usermod.rb +58 -0
  144. data/test/test_validation.rb +274 -0
  145. metadata +379 -0
@@ -0,0 +1,105 @@
1
+ require 'ldap'
2
+ require 'ldap/ldif'
3
+ require 'ldap/schema'
4
+
5
+ module LDAP
6
+ unless const_defined?(:LDAP_OPT_ERROR_NUMBER)
7
+ LDAP_OPT_ERROR_NUMBER = 0x0031
8
+ end
9
+
10
+ class Mod
11
+ unless instance_method(:to_s).arity.zero?
12
+ alias_method :original_to_s, :to_s
13
+ def to_s
14
+ inspect
15
+ end
16
+ end
17
+
18
+ alias_method :_initialize, :initialize
19
+ def initialize(op, type, vals)
20
+ if (VERSION.split(/\./).collect {|x| x.to_i} <=> [0, 9, 7]) <= 0
21
+ @op, @type, @vals = op, type, vals # to protect from GC
22
+ end
23
+ _initialize(op, type, vals)
24
+ end
25
+ end
26
+
27
+ IMPLEMENT_SPECIFIC_ERRORS = {}
28
+ {
29
+ 0x51 => "SERVER_DOWN",
30
+ 0x52 => "LOCAL_ERROR",
31
+ 0x53 => "ENCODING_ERROR",
32
+ 0x54 => "DECODING_ERROR",
33
+ 0x55 => "TIMEOUT",
34
+ 0x56 => "AUTH_UNKNOWN",
35
+ 0x57 => "FILTER_ERROR",
36
+ 0x58 => "USER_CANCELLED",
37
+ 0x59 => "PARAM_ERROR",
38
+ 0x5a => "NO_MEMORY",
39
+
40
+ 0x5b => "CONNECT_ERROR",
41
+ 0x5c => "NOT_SUPPORTED",
42
+ 0x5d => "CONTROL_NOT_FOUND",
43
+ 0x5e => "NO_RESULTS_RETURNED",
44
+ 0x5f => "MORE_RESULTS_TO_RETURN",
45
+ 0x60 => "CLIENT_LOOP",
46
+ 0x61 => "REFERRAL_LIMIT_EXCEEDED",
47
+ }.each do |code, name|
48
+ IMPLEMENT_SPECIFIC_ERRORS[code] =
49
+ ActiveLdap::LdapError.define(code, name, self)
50
+ end
51
+
52
+ class Conn
53
+ begin
54
+ instance_method(:search_ext)
55
+ @@have_search_ext = true
56
+ rescue NameError
57
+ @@have_search_ext = false
58
+ end
59
+
60
+ def search_with_limit(base, scope, filter, attributes, limit, &block)
61
+ if @@have_search_ext
62
+ search_ext(base, scope, filter, attributes,
63
+ false, nil, nil, 0, 0, limit || 0, &block)
64
+ else
65
+ i = 0
66
+ search(base, scope, filter, attributes) do |entry|
67
+ i += 1
68
+ block.call(entry)
69
+ break if limit and limit <= i
70
+ end
71
+ end
72
+ end
73
+
74
+ def failed?
75
+ not error_code.zero?
76
+ end
77
+
78
+ def error_code
79
+ code = err
80
+ code = get_option(LDAP_OPT_ERROR_NUMBER) if code.zero?
81
+ code
82
+ end
83
+
84
+ def error_message
85
+ if failed?
86
+ LDAP.err2string(error_code)
87
+ else
88
+ nil
89
+ end
90
+ end
91
+
92
+ def assert_error_code
93
+ return unless failed?
94
+ code = error_code
95
+ message = error_message
96
+ klass = ActiveLdap::LdapError::ERRORS[code]
97
+ klass ||= IMPLEMENT_SPECIFIC_ERRORS[code]
98
+ if klass.nil? and message == "Can't contact LDAP server"
99
+ klass = ActiveLdap::ConnectionError
100
+ end
101
+ klass ||= ActiveLdap::LdapError
102
+ raise klass, message
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,309 @@
1
+ require 'digest/md5'
2
+
3
+ require 'active_ldap/adapter/base'
4
+
5
+ module ActiveLdap
6
+ module Adapter
7
+ class Base
8
+ class << self
9
+ def net_ldap_connection(options)
10
+ require 'active_ldap/adapter/net_ldap_ext'
11
+ NetLdap.new(options)
12
+ end
13
+ end
14
+ end
15
+
16
+ class NetLdap < Base
17
+ METHOD = {
18
+ :ssl => :simple_tls,
19
+ :tls => :start_tls,
20
+ :plain => nil,
21
+ }
22
+
23
+ def connect(options={})
24
+ super do |host, port, method|
25
+ config = {
26
+ :host => host,
27
+ :port => port,
28
+ }
29
+ config[:encryption] = {:method => method} if method
30
+ begin
31
+ uri = construct_uri(host, port, method == :simple_tls)
32
+ with_start_tls = method == :start_tls
33
+ info = {:uri => uri, :with_start_tls => with_start_tls}
34
+ [log("connect", info) {Net::LDAP::Connection.new(config)},
35
+ uri, with_start_tls]
36
+ rescue Net::LDAP::LdapError
37
+ raise ConnectionError, $!.message
38
+ end
39
+ end
40
+ end
41
+
42
+ def unbind(options={})
43
+ super do
44
+ log("unbind") do
45
+ @connection.close # Net::LDAP doesn't implement unbind.
46
+ end
47
+ end
48
+ end
49
+
50
+ def bind(options={})
51
+ begin
52
+ super
53
+ rescue Net::LDAP::LdapError
54
+ raise AuthenticationError, $!.message
55
+ end
56
+ end
57
+
58
+ def bind_as_anonymous(options={})
59
+ super do
60
+ execute(:bind, {:name => "bind: anonymous"}, {:method => :anonymous})
61
+ true
62
+ end
63
+ end
64
+
65
+ def search(options={})
66
+ super(options) do |base, scope, filter, attrs, limit|
67
+ args = {
68
+ :base => base,
69
+ :scope => scope,
70
+ :filter => filter,
71
+ :attributes => attrs,
72
+ :size => limit,
73
+ }
74
+ info = {
75
+ :base => base, :scope => scope_name(scope),
76
+ :filter => filter, :attributes => attrs, :limit => limit
77
+ }
78
+ execute(:search, info, args) do |entry|
79
+ attributes = {}
80
+ entry.original_attribute_names.each do |name|
81
+ value = entry[name]
82
+ attributes[name] = value if value
83
+ end
84
+ yield([entry.dn, attributes])
85
+ end
86
+ end
87
+ end
88
+
89
+ def delete(targets, options={})
90
+ super do |target|
91
+ args = {:dn => target}
92
+ info = args.dup
93
+ execute(:delete, info, args)
94
+ end
95
+ end
96
+
97
+ def add(dn, entries, options={})
98
+ super do |_dn, _entries|
99
+ attributes = {}
100
+ _entries.each do |type, key, attrs|
101
+ attrs.each do |name, values|
102
+ attributes[name] = values
103
+ end
104
+ end
105
+ args = {:dn => _dn, :attributes => attributes}
106
+ info = args.dup
107
+ execute(:add, info, args)
108
+ end
109
+ end
110
+
111
+ def modify(dn, entries, options={})
112
+ super do |_dn, _entries|
113
+ info = {:dn => _dn, :attributes => _entries}
114
+ execute(:modify, info,
115
+ :dn => _dn,
116
+ :operations => parse_entries(_entries))
117
+ end
118
+ end
119
+
120
+ def modify_rdn(dn, new_rdn, delete_old_rdn, new_superior, options={})
121
+ super do |_dn, _new_rdn, _delete_old_rdn, _new_superior|
122
+ if _new_superior
123
+ raise NotImplemented.new(_("modify RDN with new superior"))
124
+ end
125
+ info = {
126
+ :name => "modify: RDN",
127
+ :dn => _dn,
128
+ :new_rdn => _new_rdn,
129
+ :new_superior => _new_superior,
130
+ :delete_old_rdn => _delete_old_rdn
131
+ }
132
+ execute(:rename, info,
133
+ :olddn => _dn,
134
+ :newrdn => _new_rdn,
135
+ :delete_attributes => _delete_old_rdn)
136
+ end
137
+ end
138
+
139
+ private
140
+ def execute(method, info=nil, *args, &block)
141
+ name = (info || {}).delete(:name) || method
142
+ result = log(name, info) do
143
+ begin
144
+ @connection.send(method, *args, &block)
145
+ rescue Errno::EPIPE
146
+ raise ConnectionError, "#{$!.class}: #{$!.message}"
147
+ end
148
+ end
149
+ message = nil
150
+ if result.is_a?(Hash)
151
+ message = result[:errorMessage]
152
+ result = result[:resultCode]
153
+ end
154
+ unless result.zero?
155
+ klass = LdapError::ERRORS[result]
156
+ klass ||= LdapError
157
+ return if klass == LdapError::SizeLimitExceeded
158
+ message = [Net::LDAP.result2string(result), message].compact.join(": ")
159
+ raise klass, message
160
+ end
161
+ end
162
+
163
+ def ensure_method(method)
164
+ method ||= "plain"
165
+ normalized_method = method.to_s.downcase.to_sym
166
+ return METHOD[normalized_method] if METHOD.has_key?(normalized_method)
167
+
168
+ available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ")
169
+ format = _("%s is not one of the available connect methods: %s")
170
+ raise ConfigurationError, format % [method.inspect, available_methods]
171
+ end
172
+
173
+ def ensure_scope(scope)
174
+ scope_map = {
175
+ :base => Net::LDAP::SearchScope_BaseObject,
176
+ :sub => Net::LDAP::SearchScope_WholeSubtree,
177
+ :one => Net::LDAP::SearchScope_SingleLevel,
178
+ }
179
+ value = scope_map[scope || :sub]
180
+ if value.nil?
181
+ available_scopes = scope_map.keys.inspect
182
+ format = _("%s is not one of the available LDAP scope: %s")
183
+ raise ArgumentError, format % [scope.inspect, available_scopes]
184
+ end
185
+ value
186
+ end
187
+
188
+ def scope_name(scope)
189
+ {
190
+ Net::LDAP::SearchScope_BaseObject => :base,
191
+ Net::LDAP::SearchScope_WholeSubtree => :sub,
192
+ Net::LDAP::SearchScope_SingleLevel => :one,
193
+ }[scope]
194
+ end
195
+
196
+ def sasl_bind(bind_dn, options={})
197
+ super do |_bind_dn, mechanism, quiet|
198
+ normalized_mechanism = mechanism.downcase.gsub(/-/, '_')
199
+ sasl_bind_setup = "sasl_bind_setup_#{normalized_mechanism}"
200
+ next unless respond_to?(sasl_bind_setup, true)
201
+ initial_credential, challenge_response =
202
+ send(sasl_bind_setup, _bind_dn, options)
203
+ args = {
204
+ :method => :sasl,
205
+ :initial_credential => initial_credential,
206
+ :mechanism => mechanism,
207
+ :challenge_response => challenge_response,
208
+ }
209
+ info = {
210
+ :name => "bind: SASL", :dn => _bind_dn, :mechanism => mechanism,
211
+ }
212
+ execute(:bind, info, args)
213
+ true
214
+ end
215
+ end
216
+
217
+ def sasl_bind_setup_digest_md5(bind_dn, options)
218
+ initial_credential = ""
219
+ nonce_count = 1
220
+ challenge_response = Proc.new do |cred|
221
+ params = parse_sasl_digest_md5_credential(cred)
222
+ qops = params["qop"].split(/,/)
223
+ unless qops.include?("auth")
224
+ raise ActiveLdap::AuthenticationError,
225
+ _("unsupported qops: %s") % qops.inspect
226
+ end
227
+ qop = "auth"
228
+ server = @connection.instance_variable_get("@conn").addr[2]
229
+ realm = params['realm']
230
+ uri = "ldap/#{server}"
231
+ nc = "%08x" % nonce_count
232
+ nonce = params["nonce"]
233
+ cnonce = generate_client_nonce
234
+ requests = {
235
+ :username => bind_dn.inspect,
236
+ :realm => realm.inspect,
237
+ :nonce => nonce.inspect,
238
+ :cnonce => cnonce.inspect,
239
+ :nc => nc,
240
+ :qop => qop,
241
+ :maxbuf => "65536",
242
+ "digest-uri" => uri.inspect,
243
+ }
244
+ a1 = "#{bind_dn}:#{realm}:#{password(cred, options)}"
245
+ a1 = "#{Digest::MD5.digest(a1)}:#{nonce}:#{cnonce}"
246
+ ha1 = Digest::MD5.hexdigest(a1)
247
+ a2 = "AUTHENTICATE:#{uri}"
248
+ ha2 = Digest::MD5.hexdigest(a2)
249
+ response = "#{ha1}:#{nonce}:#{nc}:#{cnonce}:#{qop}:#{ha2}"
250
+ requests["response"] = Digest::MD5.hexdigest(response)
251
+ nonce_count += 1
252
+ requests.collect do |key, value|
253
+ "#{key}=#{value}"
254
+ end.join(",")
255
+ end
256
+ [initial_credential, challenge_response]
257
+ end
258
+
259
+ def parse_sasl_digest_md5_credential(cred)
260
+ params = {}
261
+ cred.scan(/(\w+)=(\"?)(.+?)\2(?:,|$)/) do |name, sep, value|
262
+ params[name] = value
263
+ end
264
+ params
265
+ end
266
+
267
+ CHARS = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
268
+ def generate_client_nonce(size=32)
269
+ nonce = ""
270
+ size.times do |i|
271
+ nonce << CHARS[rand(CHARS.size)]
272
+ end
273
+ nonce
274
+ end
275
+
276
+ def simple_bind(bind_dn, options={})
277
+ super do |_bind_dn, password|
278
+ args = {
279
+ :method => :simple,
280
+ :username => _bind_dn,
281
+ :password => password,
282
+ }
283
+ execute(:bind, {:dn => _bind_dn}, args)
284
+ true
285
+ end
286
+ end
287
+
288
+ def parse_entries(entries)
289
+ result = []
290
+ entries.each do |type, key, attributes|
291
+ mod_type = ensure_mod_type(type)
292
+ attributes.each do |name, values|
293
+ result << [mod_type, name, values]
294
+ end
295
+ end
296
+ result
297
+ end
298
+
299
+ def ensure_mod_type(type)
300
+ case type
301
+ when :replace, :add, :delete
302
+ type
303
+ else
304
+ raise ArgumentError, _("unknown type: %s") % type
305
+ end
306
+ end
307
+ end
308
+ end
309
+ end
@@ -0,0 +1,23 @@
1
+ require 'net/ldap'
2
+
3
+ module Net
4
+ class LDAP
5
+ class Entry
6
+ alias initialize_without_original_attribute_names initialize
7
+ def initialize(*args)
8
+ @original_attribute_names = []
9
+ initialize_without_original_attribute_names(*args)
10
+ end
11
+
12
+ alias aset_without_original_attribute_names []=
13
+ def []=(name, value)
14
+ @original_attribute_names << name
15
+ aset_without_original_attribute_names(name, value)
16
+ end
17
+
18
+ def original_attribute_names
19
+ @original_attribute_names.compact.uniq
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ require 'active_ldap/association/proxy'
2
+
3
+ module ActiveLdap
4
+ module Association
5
+ class BelongsTo < Proxy
6
+ def replace(entry)
7
+ if entry.nil?
8
+ @target = @owner[@options[:foreign_key_name]] = nil
9
+ else
10
+ @target = (Proxy === entry ? entry.target : entry)
11
+ infect_connection(@target)
12
+ unless entry.new_entry?
13
+ @owner[@options[:foreign_key_name]] = entry[primary_key]
14
+ end
15
+ @updated = true
16
+ end
17
+
18
+ loaded
19
+ entry
20
+ end
21
+
22
+ def updated?
23
+ @updated
24
+ end
25
+
26
+ private
27
+ def have_foreign_key?
28
+ not @owner[@options[:foreign_key_name]].nil?
29
+ end
30
+
31
+ def find_target
32
+ value = @owner[@options[:foreign_key_name]]
33
+ raise EntryNotFound if value.nil?
34
+ key = primary_key
35
+ if key == "dn"
36
+ result = foreign_class.find(value, find_options)
37
+ else
38
+ filter = {key => value}
39
+ options = find_options(:filter => filter, :limit => 1)
40
+ result = foreign_class.find(:all, options).first
41
+ end
42
+ raise EntryNotFound if result.nil?
43
+ result
44
+ end
45
+ end
46
+ end
47
+ end