ruby-activeldap 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/test/test_adapter.rb +17 -0
  2. data/test/test_associations.rb +19 -0
  3. data/test/test_attributes.rb +2 -1
  4. data/test/test_base.rb +28 -1
  5. data/test/test_base_per_instance.rb +2 -1
  6. data/test/test_callback.rb +2 -2
  7. data/test/test_connection.rb +2 -1
  8. data/test/test_connection_per_dn.rb +81 -0
  9. data/test/test_dn.rb +3 -2
  10. data/test/test_find.rb +35 -1
  11. data/test/test_object_class.rb +12 -1
  12. data/test/test_reflection.rb +16 -10
  13. data/test/test_schema.rb +141 -2
  14. data/test/test_user.rb +14 -4
  15. metadata +7 -104
  16. data/CHANGES +0 -397
  17. data/COPYING +0 -340
  18. data/LICENSE +0 -58
  19. data/Manifest.txt +0 -99
  20. data/README +0 -85
  21. data/Rakefile +0 -70
  22. data/TODO +0 -23
  23. data/benchmark/bench-al.rb +0 -152
  24. data/examples/config.yaml.example +0 -5
  25. data/examples/example.der +0 -0
  26. data/examples/example.jpg +0 -0
  27. data/examples/groupadd +0 -41
  28. data/examples/groupdel +0 -35
  29. data/examples/groupls +0 -49
  30. data/examples/groupmod +0 -42
  31. data/examples/lpasswd +0 -55
  32. data/examples/objects/group.rb +0 -13
  33. data/examples/objects/ou.rb +0 -4
  34. data/examples/objects/user.rb +0 -20
  35. data/examples/ouadd +0 -38
  36. data/examples/useradd +0 -45
  37. data/examples/useradd-binary +0 -50
  38. data/examples/userdel +0 -34
  39. data/examples/userls +0 -50
  40. data/examples/usermod +0 -42
  41. data/examples/usermod-binary-add +0 -47
  42. data/examples/usermod-binary-add-time +0 -51
  43. data/examples/usermod-binary-del +0 -48
  44. data/examples/usermod-lang-add +0 -43
  45. data/lib/active_ldap.rb +0 -964
  46. data/lib/active_ldap/adapter/base.rb +0 -461
  47. data/lib/active_ldap/adapter/ldap.rb +0 -232
  48. data/lib/active_ldap/adapter/ldap_ext.rb +0 -69
  49. data/lib/active_ldap/adapter/net_ldap.rb +0 -288
  50. data/lib/active_ldap/adapter/net_ldap_ext.rb +0 -29
  51. data/lib/active_ldap/association/belongs_to.rb +0 -40
  52. data/lib/active_ldap/association/belongs_to_many.rb +0 -39
  53. data/lib/active_ldap/association/collection.rb +0 -80
  54. data/lib/active_ldap/association/has_many.rb +0 -40
  55. data/lib/active_ldap/association/has_many_wrap.rb +0 -55
  56. data/lib/active_ldap/association/proxy.rb +0 -89
  57. data/lib/active_ldap/associations.rb +0 -162
  58. data/lib/active_ldap/attributes.rb +0 -203
  59. data/lib/active_ldap/base.rb +0 -1510
  60. data/lib/active_ldap/callbacks.rb +0 -19
  61. data/lib/active_ldap/command.rb +0 -46
  62. data/lib/active_ldap/configuration.rb +0 -106
  63. data/lib/active_ldap/connection.rb +0 -142
  64. data/lib/active_ldap/distinguished_name.rb +0 -246
  65. data/lib/active_ldap/ldap_error.rb +0 -74
  66. data/lib/active_ldap/object_class.rb +0 -74
  67. data/lib/active_ldap/schema.rb +0 -299
  68. data/lib/active_ldap/timeout.rb +0 -75
  69. data/lib/active_ldap/timeout_stub.rb +0 -17
  70. data/lib/active_ldap/user_password.rb +0 -92
  71. data/lib/active_ldap/validations.rb +0 -76
  72. data/rails/plugin/active_ldap/README +0 -54
  73. data/rails/plugin/active_ldap/generators/scaffold_al/scaffold_al_generator.rb +0 -7
  74. data/rails/plugin/active_ldap/generators/scaffold_al/templates/ldap.yml +0 -21
  75. data/rails/plugin/active_ldap/init.rb +0 -12
  76. data/test/TODO +0 -2
  77. data/test/al-test-utils.rb +0 -381
  78. data/test/command.rb +0 -62
  79. data/test/config.yaml.sample +0 -6
  80. data/test/run-test.rb +0 -29
  81. data/test/test-unit-ext.rb +0 -2
  82. data/test/test-unit-ext/always-show-result.rb +0 -28
  83. data/test/test-unit-ext/priority.rb +0 -163
@@ -1,461 +0,0 @@
1
- require 'active_ldap/schema'
2
- require 'active_ldap/ldap_error'
3
-
4
- module ActiveLdap
5
- module Adapter
6
- class Base
7
- VALID_ADAPTER_CONFIGURATION_KEYS = [:host, :port, :method, :timeout,
8
- :retry_on_timeout, :retry_limit,
9
- :retry_wait, :bind_dn, :password,
10
- :password_block, :try_sasl,
11
- :sasl_mechanisms, :sasl_quiet,
12
- :allow_anonymous, :store_password]
13
- def initialize(configuration={})
14
- @connection = nil
15
- @configuration = configuration.dup
16
- @logger = @configuration.delete(:logger)
17
- @configuration.assert_valid_keys(VALID_ADAPTER_CONFIGURATION_KEYS)
18
- VALID_ADAPTER_CONFIGURATION_KEYS.each do |name|
19
- instance_variable_set("@#{name}", configuration[name])
20
- end
21
- end
22
-
23
- def connect(options={})
24
- host = options[:host] || @host
25
- port = options[:port] || @port
26
- method = ensure_method(options[:method] || @method)
27
- @connection = yield(host, port, method)
28
- prepare_connection(options)
29
- bind(options)
30
- end
31
-
32
- def disconnect!(options={})
33
- return if @connection.nil?
34
- unbind(options)
35
- @connection = nil
36
- end
37
-
38
- def rebind(options={})
39
- unbind(options) if bound?
40
- connect(options)
41
- end
42
-
43
- def bind(options={})
44
- bind_dn = options[:bind_dn] || @bind_dn
45
- try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl
46
- if options.has_key?(:allow_anonymous)
47
- allow_anonymous = options[:allow_anonymous]
48
- else
49
- allow_anonymous = @allow_anonymous
50
- end
51
-
52
- # Rough bind loop:
53
- # Attempt 1: SASL if available
54
- # Attempt 2: SIMPLE with credentials if password block
55
- # Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail (or pwblock returns '')
56
- if try_sasl and sasl_bind(bind_dn, options)
57
- @logger.info {'Bound SASL'}
58
- elsif simple_bind(bind_dn, options)
59
- @logger.info {'Bound simple'}
60
- elsif allow_anonymous and bind_as_anonymous(options)
61
- @logger.info {'Bound anonymous'}
62
- else
63
- message = yield if block_given?
64
- message ||= 'All authentication methods exhausted.'
65
- raise AuthenticationError, message
66
- end
67
-
68
- bound?
69
- end
70
-
71
- def bind_as_anonymous(options={})
72
- @logger.info {"Attempting anonymous authentication"}
73
- operation(options) do
74
- yield
75
- end
76
- end
77
-
78
- def connecting?
79
- not @connection.nil?
80
- end
81
-
82
- def schema(options={})
83
- @schema ||= operation(options) do
84
- base = options[:base]
85
- attrs = options[:attributes]
86
-
87
- attrs ||= [
88
- 'objectClasses',
89
- 'attributeTypes',
90
- 'matchingRules',
91
- 'matchingRuleUse',
92
- 'dITStructureRules',
93
- 'dITContentRules',
94
- 'nameForms',
95
- 'ldapSyntaxes',
96
- #'extendedAttributeInfo', # if we need RANGE-LOWER/UPPER.
97
- ]
98
- key = 'subschemaSubentry'
99
- base ||= root_dse([key], options)[0][key][0]
100
- base ||= 'cn=schema'
101
- dn, attributes = search(:base => base,
102
- :scope => :base,
103
- :filter => '(objectClass=subschema)',
104
- :attributes => attrs).first
105
- Schema.new(attributes)
106
- end
107
- end
108
-
109
- def load(ldifs, options={})
110
- operation(options) do
111
- ldifs.split(/(?:\r?\n){2,}/).each do |ldif|
112
- yield(ldif)
113
- end
114
- end
115
- end
116
-
117
- def search(options={})
118
- filter = parse_filter(options[:filter] || 'objectClass=*')
119
- attrs = options[:attributes] || []
120
- scope = ensure_scope(options[:scope])
121
- base = options[:base]
122
- limit = options[:limit] || 0
123
- limit = nil if limit <= 0
124
-
125
- attrs = attrs.to_a # just in case
126
-
127
- values = []
128
- callback = Proc.new do |value, block|
129
- value = block.call(value) if block
130
- values << value
131
- end
132
-
133
- begin
134
- operation(options) do
135
- yield(base, scope, filter, attrs, limit, callback)
136
- end
137
- rescue LdapError
138
- # Do nothing on failure
139
- @logger.debug {"Ignore error #{$!.class}(#{$!.message}) " +
140
- "for #{filter} and attrs #{attrs.inspect}"}
141
- end
142
-
143
- values
144
- end
145
-
146
- def delete(targets, options={})
147
- targets = [targets] unless targets.is_a?(Array)
148
- return if targets.empty?
149
- target = nil
150
- begin
151
- operation(options) do
152
- targets.each do |target|
153
- yield(target)
154
- end
155
- end
156
- rescue LdapError::NoSuchObject
157
- raise EntryNotFound, "No such entry: #{target}"
158
- end
159
- end
160
-
161
- def add(dn, entries, options={})
162
- begin
163
- operation(options) do
164
- yield(dn, entries)
165
- end
166
- rescue LdapError::NoSuchObject
167
- raise EntryNotFound, "No such entry: #{dn}"
168
- rescue LdapError::InvalidDnSyntax
169
- raise DistinguishedNameInvalid.new(dn)
170
- rescue LdapError::AlreadyExists
171
- raise EntryAlreadyExist, "#{$!.message}: #{dn}"
172
- rescue LdapError::StrongAuthRequired
173
- raise StrongAuthenticationRequired, "#{$!.message}: #{dn}"
174
- rescue LdapError::ObjectClassViolation
175
- raise RequiredAttributeMissed, "#{$!.message}: #{dn}"
176
- rescue LdapError::UnwillingToPerform
177
- raise OperationNotPermitted, "#{$!.message}: #{dn}"
178
- end
179
- end
180
-
181
- def modify(dn, entries, options={})
182
- begin
183
- operation(options) do
184
- yield(dn, entries)
185
- end
186
- rescue LdapError::UndefinedType
187
- raise
188
- rescue LdapError::ObjectClassViolation
189
- raise RequiredAttributeMissed, "#{$!.message}: #{dn}"
190
- end
191
- end
192
-
193
- private
194
- def prepare_connection(options)
195
- end
196
-
197
- def operation(options)
198
- reconnect_if_need
199
- try_reconnect = !options.has_key?(:try_reconnect) ||
200
- options[:try_reconnect]
201
- with_timeout(try_reconnect, options) do
202
- yield
203
- end
204
- end
205
-
206
- def need_credential_sasl_mechanism?(mechanism)
207
- not %(GSSAPI EXTERNAL ANONYMOUS).include?(mechanism)
208
- end
209
-
210
- def password(bind_dn, options={})
211
- passwd = options[:password] || @password
212
- return passwd if passwd
213
-
214
- password_block = options[:password_block] || @password_block
215
- # TODO: Give a warning to reconnect users with password clearing
216
- # Get the passphrase for the first time, or anew if we aren't storing
217
- if password_block.respond_to?(:call)
218
- passwd = password_block.call(bind_dn)
219
- else
220
- @logger.error {'password_block not nil or Proc object. Ignoring.'}
221
- return nil
222
- end
223
-
224
- # Store the password for quick reference later
225
- if options.has_key?(:store_password)
226
- store_password = options[:store_password]
227
- else
228
- store_password = @store_password
229
- end
230
- @password = store_password ? passwd : nil
231
-
232
- passwd
233
- end
234
-
235
- def with_timeout(try_reconnect=true, options={}, &block)
236
- begin
237
- Timeout.alarm(@timeout, &block)
238
- rescue Timeout::Error => e
239
- @logger.error {'Requested action timed out.'}
240
- retry if try_reconnect and @retry_on_timeout and reconnect(options)
241
- @logger.error {e.message}
242
- raise TimeoutError, e.message
243
- end
244
- end
245
-
246
- def sasl_bind(bind_dn, options={})
247
- return false unless bind_dn
248
-
249
- # Get all SASL mechanisms
250
- mechanisms = operation(options) do
251
- key = "supportedSASLMechanisms"
252
- root_dse([key])[0][key]
253
- end
254
- mechanisms ||= []
255
-
256
- if options.has_key?(:sasl_quiet)
257
- sasl_quiet = options[:sasl_quiet]
258
- else
259
- sasl_quiet = @sasl_quiet
260
- end
261
-
262
- sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms
263
- sasl_mechanisms.each do |mechanism|
264
- next unless mechanisms.include?(mechanism)
265
- operation(options) do
266
- yield(bind_dn, mechanism, sasl_quiet)
267
- return true if bound?
268
- end
269
- end
270
- false
271
- end
272
-
273
- def simple_bind(bind_dn, options={})
274
- return false unless bind_dn
275
-
276
- passwd = password(bind_dn, options)
277
- return false unless passwd
278
-
279
- begin
280
- operation(options) do
281
- yield(bind_dn, passwd)
282
- bound?
283
- end
284
- rescue LdapError::InvalidDnSyntax
285
- @logger.debug {"DN is invalid: #{bind_dn}"}
286
- raise DistinguishedNameInvalid.new(bind_dn)
287
- rescue LdapError::InvalidCredentials
288
- false
289
- end
290
- end
291
-
292
- def parse_filter(filter, operator=nil)
293
- return nil if filter.nil?
294
- if !filter.is_a?(String) and !filter.respond_to?(:collect)
295
- filter = filter.to_s
296
- end
297
-
298
- case filter
299
- when String
300
- parse_filter_string(filter)
301
- when Hash
302
- components = filter.sort_by {|k, v| k.to_s}.collect do |key, value|
303
- construct_component(key, value, operator)
304
- end
305
- construct_filter(components, operator)
306
- else
307
- operator, components = normalize_array_filter(filter, operator)
308
-
309
- components = components.collect do |component|
310
- if component.is_a?(Array) and component.size == 2
311
- key, value = component
312
- if filter_logical_operator?(key) or value.is_a?(Hash)
313
- parse_filter(value, key)
314
- else
315
- construct_component(key, value, operator)
316
- end
317
- elsif component.is_a?(Symbol)
318
- assert_filter_logical_operator(component)
319
- nil
320
- else
321
- parse_filter(component, operator)
322
- end
323
- end
324
- construct_filter(components, operator)
325
- end
326
- end
327
-
328
- def parse_filter_string(filter)
329
- if /\A\s*\z/.match(filter)
330
- nil
331
- else
332
- if filter[0, 1] == "("
333
- filter
334
- else
335
- "(#{filter})"
336
- end
337
- end
338
- end
339
-
340
- def normalize_array_filter(filter, operator=nil)
341
- filter_operator, *components = filter
342
- if filter_logical_operator?(filter_operator)
343
- operator = filter_operator
344
- else
345
- components.unshift(filter_operator)
346
- end
347
- [operator, components]
348
- end
349
-
350
- def construct_component(key, value, operator=nil)
351
- if collection?(value)
352
- values = []
353
- value.each do |val|
354
- if collection?(val)
355
- values.concat(val.collect {|v| [key, v]})
356
- else
357
- values << [key, val]
358
- end
359
- end
360
- values[0] = values[0][1] if filter_logical_operator?(values[0][1])
361
- parse_filter(values, operator)
362
- else
363
- "(#{key}=#{value})"
364
- end
365
- end
366
-
367
- def construct_filter(components, operator=nil)
368
- operator = normalize_filter_logical_operator(operator)
369
- components = components.compact
370
- case components.size
371
- when 0
372
- nil
373
- when 1
374
- components.join
375
- else
376
- "(#{operator == :and ? '&' : '|'}#{components.join})"
377
- end
378
- end
379
-
380
- def collection?(object)
381
- !object.is_a?(String) and object.respond_to?(:each)
382
- end
383
-
384
- LOGICAL_OPERATORS = [:and, :or, :&, :|]
385
- def filter_logical_operator?(operator)
386
- LOGICAL_OPERATORS.include?(operator)
387
- end
388
-
389
- def normalize_filter_logical_operator(operator)
390
- assert_filter_logical_operator(operator)
391
- case (operator || :and)
392
- when :and, :&
393
- :and
394
- else
395
- :or
396
- end
397
- end
398
-
399
- def assert_filter_logical_operator(operator)
400
- return if operator.nil?
401
- unless filter_logical_operator?(operator)
402
- raise ArgumentError,
403
- "invalid logical operator: #{operator.inspect}: " +
404
- "available operators: #{LOGICAL_OPERATORS.inspect}"
405
- end
406
- end
407
-
408
- # Attempts to reconnect up to the number of times allowed
409
- # If forced, try once then fail with ConnectionError if not connected.
410
- def reconnect(options={})
411
- options = options.dup
412
- force = options[:force]
413
- retry_limit = options[:retry_limit] || @retry_limit
414
- retry_wait = options[:retry_wait] || @retry_wait
415
- options[:reconnect_attempts] ||= 0
416
-
417
- loop do
418
- unless can_reconnect?(options)
419
- raise ConnectionError,
420
- 'Giving up trying to reconnect to LDAP server.'
421
- end
422
-
423
- @logger.debug {'Attempting to reconnect'}
424
- disconnect!
425
-
426
- # Reset the attempts if this was forced.
427
- options[:reconnect_attempts] = 0 if force
428
- options[:reconnect_attempts] += 1 if retry_limit >= 0
429
- begin
430
- connect(options)
431
- break
432
- rescue => detail
433
- @logger.error {"Reconnect to server failed: #{detail.exception}"}
434
- @logger.error {"Reconnect to server failed backtrace:\n" +
435
- detail.backtrace.join("\n")}
436
- # Do not loop if forced
437
- raise ConnectionError, detail.message if force
438
- end
439
-
440
- # Sleep before looping
441
- sleep retry_wait
442
- end
443
-
444
- true
445
- end
446
-
447
- def reconnect_if_need(options={})
448
- reconnect(options) if !connecting? and can_reconnect?(options)
449
- end
450
-
451
- # Determine if we have exceed the retry limit or not.
452
- # True is reconnecting is allowed - False if not.
453
- def can_reconnect?(options={})
454
- retry_limit = options[:retry_limit] || @retry_limit
455
- reconnect_attempts = options[:reconnect_attempts] || 0
456
-
457
- retry_limit < 0 or reconnect_attempts < (retry_limit - 1)
458
- end
459
- end
460
- end
461
- end