ruby-activeldap 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/activeldap.rb +11 -7
- data/lib/activeldap/base.rb +207 -168
- data/lib/activeldap/configuration.rb +19 -3
- data/lib/activeldap/ldap.rb +26 -0
- metadata +33 -27
data/lib/activeldap.rb
CHANGED
@@ -469,6 +469,7 @@
|
|
469
469
|
# Most of these are obvious, but I'll step through them for completeness:
|
470
470
|
# * :host defines the LDAP server hostname to connect to.
|
471
471
|
# * :port defines the LDAP server port to connect to.
|
472
|
+
# * :method defines the type of connection - :tls, :ssl, :plain
|
472
473
|
# * :base specifies the LDAP search base to use with the prefixes defined in all
|
473
474
|
# subclasses.
|
474
475
|
# * :bind_format specifies what your server expects when attempting to bind with
|
@@ -478,15 +479,17 @@
|
|
478
479
|
# * :user gives the username to substitute into bind_format for binding with
|
479
480
|
# credentials
|
480
481
|
# * :password_block, if defined, give the Proc block for acquiring the password
|
482
|
+
# * :password, if defined, give the user's password as a String
|
483
|
+
# * :store_password indicates whether the password should be stored, or if used
|
484
|
+
# whether the :password_block should be called on each reconnect.
|
481
485
|
# * :allow_anonymous determines whether anonymous binding is allowed if other
|
482
486
|
# bind methods fail
|
487
|
+
# * :try_sasl, when true, tells ActiveLDAP to attempt a SASL-GSSAPI bind
|
488
|
+
# * :sasl_quiet, when true, tells the SASL libraries to not spew messages to STDOUT
|
483
489
|
#
|
484
490
|
# Base.connect both connects and binds in one step. It follows roughly the following approach:
|
485
491
|
#
|
486
|
-
# * Connect to host:port
|
487
|
-
# * Try TLS.
|
488
|
-
# * If that fails try SSL.
|
489
|
-
# * If that fails try no encryption.
|
492
|
+
# * Connect to host:port using :method
|
490
493
|
#
|
491
494
|
# * If user and password_block, attempt to bind with credentials.
|
492
495
|
# * If that fails or no password_block and anonymous allowed, attempt to bind
|
@@ -546,7 +549,7 @@
|
|
546
549
|
# === Others
|
547
550
|
#
|
548
551
|
# Other exceptions may be raised by the Ruby/LDAP module, or by other subsystems.
|
549
|
-
# If you get one of these exceptions and
|
552
|
+
# If you get one of these exceptions and think it should be wrapped, write me an
|
550
553
|
# email and let me know where it is and what you expected. For faster results,
|
551
554
|
# email a patch!
|
552
555
|
#
|
@@ -902,14 +905,15 @@
|
|
902
905
|
# Blanket warning hiding. Remove for debugging
|
903
906
|
$VERBOSE, verbose = false, $VERBOSE
|
904
907
|
|
908
|
+
require 'activeldap/ldap'
|
909
|
+
require 'activeldap/schema2'
|
905
910
|
require 'activeldap/base'
|
906
911
|
require 'activeldap/associations'
|
907
912
|
require 'activeldap/configuration'
|
908
|
-
require 'activeldap/schema2'
|
909
913
|
|
910
914
|
|
911
915
|
module ActiveLDAP
|
912
|
-
VERSION = "0.
|
916
|
+
VERSION = "0.7.0"
|
913
917
|
end
|
914
918
|
|
915
919
|
ActiveLDAP::Base.class_eval do
|
data/lib/activeldap/base.rb
CHANGED
@@ -50,6 +50,12 @@ module ActiveLDAP
|
|
50
50
|
class AttributeEmpty < RuntimeError
|
51
51
|
end
|
52
52
|
|
53
|
+
# ConfigurationError
|
54
|
+
#
|
55
|
+
# An exception raised when there is a problem with Base.connect arguments
|
56
|
+
class ConfigurationError < RuntimeError
|
57
|
+
end
|
58
|
+
|
53
59
|
# DeleteError
|
54
60
|
#
|
55
61
|
# An exception raised when an ActiveLDAP delete action fails
|
@@ -102,6 +108,7 @@ module ActiveLDAP
|
|
102
108
|
@@config = nil # Container for current connection settings
|
103
109
|
@@schema = nil # LDAP server's schema
|
104
110
|
@@conn = nil # LDAP connection
|
111
|
+
@@reconnect_attempts = 0 # Number of reconnects attempted
|
105
112
|
|
106
113
|
# Driver generator
|
107
114
|
#
|
@@ -110,6 +117,7 @@ module ActiveLDAP
|
|
110
117
|
# is really just a proof of concept and has not truly useful purpose.
|
111
118
|
# example: Base.create_object(:class => "user", :dnattr => "uid", :classes => ['top'])
|
112
119
|
#
|
120
|
+
# THIS METHOD IS DANGEROUS. INPUT IS NOT SANITIZED.
|
113
121
|
def Base.create_object(config={})
|
114
122
|
# Just upcase the first letter of the new class name
|
115
123
|
str = config[:class]
|
@@ -160,7 +168,7 @@ module ActiveLDAP
|
|
160
168
|
|
161
169
|
self.class.module_eval <<-"end_eval"
|
162
170
|
class ::#{class_name} < ActiveLDAP::Base
|
163
|
-
ldap_mapping :dnattr => "#{attr}, :prefix => "#{prefix}", :classes =>
|
171
|
+
ldap_mapping :dnattr => "#{attr}", :prefix => "#{prefix}", :classes => #{classes}
|
164
172
|
#{belongs_to.join("\n")}
|
165
173
|
#{has_many.join("\n")}
|
166
174
|
end
|
@@ -177,30 +185,33 @@ module ActiveLDAP
|
|
177
185
|
# :bind_format specifies the string to substitute the username into on bind. e.g. uid=%s,ou=People,dc=dataspill,dc=org. Overrides @@bind_format.
|
178
186
|
# :password_block specifies a Proc object that will yield a String to be used as the password when called.
|
179
187
|
# :logger specifies a preconfigured Log4r::Logger to be used for all logging
|
180
|
-
# :host
|
181
|
-
# :port
|
188
|
+
# :host sets the LDAP server hostname
|
189
|
+
# :port sets the LDAP server port
|
182
190
|
# :base overwrites Base.base - this affects EVERYTHING
|
183
191
|
# :try_sasl indicates that a SASL bind should be attempted when binding to the server (default: false)
|
184
192
|
# :allow_anonymous indicates that a true anonymous bind is allowed when trying to bind to the server (default: true)
|
185
|
-
# :retries - indicates the number of attempts to reconnect that will be undertaken when a stale connection occurs.
|
186
|
-
|
193
|
+
# :retries - indicates the number of attempts to reconnect that will be undertaken when a stale connection occurs. -1 means infinite.
|
194
|
+
# :sasl_quiet - if true, sets @sasl_quiet on the Ruby/LDAP connection
|
195
|
+
# :method - whether to use :ssl, :tls, or :plain (unencrypted)
|
196
|
+
# :retry_wait - seconds to wait before retrying a connection
|
197
|
+
# :ldap_scope - dictates how to find objects. ONELEVEL by default to avoid dn_attr collisions across OUs. Think before changing.
|
198
|
+
# See lib/configuration.rb for defaults for each option
|
199
|
+
def Base.connect(config={})
|
187
200
|
# Process config
|
188
201
|
# Class options
|
189
202
|
## These will be replace by configuration.rb defaults if defined
|
190
|
-
@@config =
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
end_eval
|
203
|
+
@@config = DEFAULT_CONFIG.dup
|
204
|
+
config.keys.each do |key|
|
205
|
+
if key == :base
|
206
|
+
# Scrub before inserting
|
207
|
+
base = config[:base].gsub(/['}{#]/, '')
|
208
|
+
Base.class_eval("def Base.base();'#{base}';end")
|
209
|
+
else
|
210
|
+
@@config[key] = config[key]
|
211
|
+
end
|
200
212
|
end
|
201
|
-
|
202
|
-
|
203
|
-
@@logger = config[:logger] || nil
|
213
|
+
# Assign a easier name for the logger
|
214
|
+
@@logger = @@config[:logger] || nil
|
204
215
|
# Setup default logger to console
|
205
216
|
if @@logger.nil?
|
206
217
|
@@logger = Log4r::Logger.new('activeldap')
|
@@ -209,30 +220,13 @@ module ActiveLDAP
|
|
209
220
|
@@logger.add('console')
|
210
221
|
end
|
211
222
|
|
212
|
-
#
|
213
|
-
|
214
|
-
password_block = nil
|
215
|
-
@@config[:allow_anonymous] = true
|
216
|
-
@@config[:try_sasl] = false
|
217
|
-
|
218
|
-
@@config[:user] = config[:user] || user
|
219
|
-
@@config[:allow_anonymous] = config[:allow_anonymous] if config.has_key? :allow_anonymous
|
220
|
-
@@config[:try_sasl] = config[:try_sasl]
|
221
|
-
@@config[:password_block] = config[:password_block] if config.has_key? :password_block
|
222
|
-
|
223
|
-
# Setup bind credentials
|
224
|
-
@@config[:user] = ENV['USER'] unless @@config[:user]
|
223
|
+
# Reset for the new connection
|
224
|
+
@@reconnect_attempts = 0
|
225
225
|
|
226
|
+
# Make the connection.
|
226
227
|
do_connect()
|
227
|
-
|
228
|
-
# Load Schema (if not straight SSL...)
|
229
|
-
begin
|
230
|
-
@@schema = @@conn.schema() if @@schema.nil?
|
231
|
-
rescue => detail
|
232
|
-
raise ConnectionError, "#{detail.exception} - LDAP connection failure, or server does not support schema queries."
|
233
|
-
end
|
234
228
|
|
235
|
-
#
|
229
|
+
# Make irb users happy with a 'true'
|
236
230
|
return true
|
237
231
|
end # Base.connect
|
238
232
|
|
@@ -260,6 +254,56 @@ module ActiveLDAP
|
|
260
254
|
@@conn = conn
|
261
255
|
end
|
262
256
|
|
257
|
+
# Attempts to reconnect up to the number of times allowed
|
258
|
+
# If forced, try once then fail with ConnectionError if not connected.
|
259
|
+
def Base.reconnect(force=false)
|
260
|
+
not_connected = true
|
261
|
+
while not_connected
|
262
|
+
# Just to be clean, unbind if possible
|
263
|
+
begin
|
264
|
+
@@conn.unbind() if not @@conn.nil? and @@conn.bound?
|
265
|
+
rescue
|
266
|
+
# Ignore complaints.
|
267
|
+
end
|
268
|
+
@@conn = nil
|
269
|
+
|
270
|
+
if @@config[:retries] == -1 or force == true
|
271
|
+
|
272
|
+
|
273
|
+
# Reset the attempts if this was forced.
|
274
|
+
@@reconnect_attempts = 0 if @@reconnect_attempts != 0
|
275
|
+
begin
|
276
|
+
do_connect()
|
277
|
+
not_connected = false
|
278
|
+
rescue => detail
|
279
|
+
@@logger.error("Reconnect to server failed: #{detail.exception}")
|
280
|
+
@@logger.error("Reconnect to server failed backtrace: #{detail.backtrace}")
|
281
|
+
# Do not loop if forced
|
282
|
+
raise ConnectionError, detail.message if force
|
283
|
+
end
|
284
|
+
elsif @@reconnect_attempts < @@config[:retries]
|
285
|
+
|
286
|
+
@@reconnect_attempts += 1
|
287
|
+
begin
|
288
|
+
do_connect()
|
289
|
+
not_connected = false
|
290
|
+
# Reset to 0 if a connection was made.
|
291
|
+
@@reconnect_attempts = 0
|
292
|
+
rescue => detail
|
293
|
+
@@logger.error("Reconnect to server failed: #{detail.exception}")
|
294
|
+
@@logger.error("Reconnect to server failed backtrace: #{detail.backtrace}")
|
295
|
+
end
|
296
|
+
else
|
297
|
+
# Raise a warning
|
298
|
+
raise ConnectionError, 'Giving up trying to reconnect to LDAP server.'
|
299
|
+
end
|
300
|
+
|
301
|
+
# Sleep before looping
|
302
|
+
sleep @@config[:retry_wait]
|
303
|
+
end
|
304
|
+
return true
|
305
|
+
end
|
306
|
+
|
263
307
|
# Return the schema object
|
264
308
|
def Base.schema
|
265
309
|
@@schema
|
@@ -286,7 +330,6 @@ module ActiveLDAP
|
|
286
330
|
values = []
|
287
331
|
config[:attrs] = config[:attrs].to_a # just in case
|
288
332
|
|
289
|
-
tries = 0
|
290
333
|
begin
|
291
334
|
@@conn.search(config[:base], config[:scope], config[:filter], config[:attrs]) do |m|
|
292
335
|
res = {}
|
@@ -301,14 +344,9 @@ module ActiveLDAP
|
|
301
344
|
rescue RuntimeError => detail
|
302
345
|
#TODO# Check for 'No message' when retrying
|
303
346
|
# The connection may have gone stale. Let's reconnect and retry.
|
304
|
-
if
|
305
|
-
|
306
|
-
|
307
|
-
end
|
308
|
-
tries += 1
|
309
|
-
# Reconnect and rebind.
|
310
|
-
do_connect()
|
311
|
-
retry
|
347
|
+
retry if Base.reconnect()
|
348
|
+
# Do nothing on failure
|
349
|
+
|
312
350
|
end
|
313
351
|
return values
|
314
352
|
end
|
@@ -350,10 +388,9 @@ module ActiveLDAP
|
|
350
388
|
|
351
389
|
matches = []
|
352
390
|
|
353
|
-
tries = 0
|
354
391
|
begin
|
355
392
|
# Get some attributes
|
356
|
-
@@conn.search(base(),
|
393
|
+
@@conn.search(base(), @@config[:ldap_scope], "(#{attr}=#{val})") do |m|
|
357
394
|
# Extract the dnattr value
|
358
395
|
dnval = m.dn.split(/,/)[0].split(/=/)[1]
|
359
396
|
|
@@ -364,22 +401,17 @@ module ActiveLDAP
|
|
364
401
|
end
|
365
402
|
end
|
366
403
|
rescue RuntimeError => detail
|
367
|
-
#
|
368
|
-
#
|
369
|
-
if
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
tries += 1
|
374
|
-
# reconnect and rebind.
|
375
|
-
do_connect()
|
376
|
-
retry
|
404
|
+
#TODO# Check for 'No message' when retrying
|
405
|
+
# The connection may have gone stale. Let's reconnect and retry.
|
406
|
+
retry if Base.reconnect()
|
407
|
+
|
408
|
+
# Do nothing on failure
|
409
|
+
|
377
410
|
end
|
378
411
|
return nil
|
379
412
|
end
|
380
413
|
private_class_method :find
|
381
414
|
|
382
|
-
|
383
415
|
# find_all
|
384
416
|
#
|
385
417
|
# Finds all matches for value where |value| is the value of some
|
@@ -412,10 +444,9 @@ module ActiveLDAP
|
|
412
444
|
|
413
445
|
matches = []
|
414
446
|
|
415
|
-
tries = 0
|
416
447
|
begin
|
417
448
|
# Get some attributes
|
418
|
-
@@conn.search(base(),
|
449
|
+
@@conn.search(base(), @@config[:ldap_scope],
|
419
450
|
"(#{attr}=#{val})") do |m|
|
420
451
|
# Extract the dnattr value
|
421
452
|
dnval = m.dn.split(/,/)[0].split(/=/)[1]
|
@@ -427,19 +458,12 @@ module ActiveLDAP
|
|
427
458
|
end
|
428
459
|
end
|
429
460
|
rescue RuntimeError => detail
|
430
|
-
#
|
431
|
-
|
432
|
-
|
461
|
+
#TODO# Check for 'No message' when retrying
|
462
|
+
# The connection may have gone stale. Let's reconnect and retry.
|
463
|
+
retry if Base.reconnect()
|
433
464
|
|
434
|
-
#
|
435
|
-
|
436
|
-
# do nothing on failure
|
437
|
-
|
438
|
-
end
|
439
|
-
tries += 1
|
440
|
-
# reconnect and rebind.
|
441
|
-
do_connect()
|
442
|
-
retry
|
465
|
+
# Do nothing on failure
|
466
|
+
|
443
467
|
end
|
444
468
|
return matches
|
445
469
|
end
|
@@ -499,13 +523,16 @@ module ActiveLDAP
|
|
499
523
|
def initialize(val)
|
500
524
|
@exists = false
|
501
525
|
# Try a default connection if none made explicitly
|
502
|
-
|
526
|
+
if Base.connection.nil? and @@reconnect_attempts < @@config[:retries]
|
503
527
|
# Use @@config if it has been prepopulated and the conn is down.
|
504
528
|
if @@config
|
505
|
-
ActiveLDAP::Base.
|
529
|
+
ActiveLDAP::Base.reconnect
|
506
530
|
else
|
507
531
|
ActiveLDAP::Base.connect
|
508
532
|
end
|
533
|
+
elsif Base.connection.nil?
|
534
|
+
@@logger.error('Attempted to initialize a new object with no connection')
|
535
|
+
raise ConnectionError, 'Number of reconnect attempts exceeded.'
|
509
536
|
end
|
510
537
|
if val.class == LDAP::Entry
|
511
538
|
# Call import, which is basically initialize
|
@@ -543,10 +570,9 @@ module ActiveLDAP
|
|
543
570
|
@dn = "#{dnattr()}=#{val},#{base()}"
|
544
571
|
|
545
572
|
# Search for the existing entry
|
546
|
-
tries = 0
|
547
573
|
begin
|
548
574
|
# Get some attributes
|
549
|
-
Base.connection.search(base(),
|
575
|
+
Base.connection.search(base(), @@config[:ldap_scope], "(#{dnattr()}=#{val})") do |m|
|
550
576
|
@exists = true
|
551
577
|
# Save DN
|
552
578
|
@dn = m.dn
|
@@ -568,17 +594,13 @@ module ActiveLDAP
|
|
568
594
|
end
|
569
595
|
end
|
570
596
|
rescue RuntimeError => detail
|
571
|
-
#
|
572
|
-
#
|
573
|
-
if
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
else
|
579
|
-
@@logger.error('new: unable to search for entry')
|
580
|
-
raise detail
|
581
|
-
end
|
597
|
+
#TODO# Check for 'No message' when retrying
|
598
|
+
# The connection may have gone stale. Let's reconnect and retry.
|
599
|
+
retry if Base.reconnect()
|
600
|
+
|
601
|
+
# Do nothing on failure
|
602
|
+
@@logger.error('new: unable to search for entry')
|
603
|
+
raise detail
|
582
604
|
rescue LDAP::ResultError
|
583
605
|
end
|
584
606
|
end
|
@@ -678,26 +700,19 @@ module ActiveLDAP
|
|
678
700
|
|
679
701
|
end
|
680
702
|
|
681
|
-
|
682
703
|
# delete
|
683
704
|
#
|
684
705
|
# Delete this entry from LDAP
|
685
706
|
def delete
|
686
707
|
|
687
|
-
tries = 0
|
688
708
|
begin
|
689
709
|
@@conn.delete(@dn)
|
690
710
|
@exists = false
|
691
711
|
rescue RuntimeError => detail
|
692
712
|
#todo# check for 'no message' when retrying
|
693
713
|
# the connection may have gone stale. let's reconnect and retry.
|
694
|
-
if
|
695
|
-
|
696
|
-
end
|
697
|
-
tries += 1
|
698
|
-
# reconnect and rebind.
|
699
|
-
do_connect()
|
700
|
-
retry
|
714
|
+
retry if Base.reconnect()
|
715
|
+
raise DeleteError, "Failed to delete LDAP entry: '#{@dn}'"
|
701
716
|
rescue LDAP::ResultError => detail
|
702
717
|
raise DeleteError, "Failed to delete LDAP entry: '#{@dn}'"
|
703
718
|
end
|
@@ -823,7 +838,6 @@ module ActiveLDAP
|
|
823
838
|
end
|
824
839
|
end
|
825
840
|
|
826
|
-
tries = 0
|
827
841
|
begin
|
828
842
|
|
829
843
|
@@conn.modify(@dn, entry)
|
@@ -831,13 +845,8 @@ module ActiveLDAP
|
|
831
845
|
rescue RuntimeError => detail
|
832
846
|
#todo# check for 'no message' when retrying
|
833
847
|
# the connection may have gone stale. let's reconnect and retry.
|
834
|
-
if
|
835
|
-
|
836
|
-
end
|
837
|
-
tries += 1
|
838
|
-
# reconnect and rebind.
|
839
|
-
do_connect()
|
840
|
-
retry
|
848
|
+
retry if Base.reconnect()
|
849
|
+
raise WriteError, "Could not update LDAP entry: #{detail}"
|
841
850
|
rescue => detail
|
842
851
|
raise WriteError, "Could not update LDAP entry: #{detail}"
|
843
852
|
end
|
@@ -861,21 +870,15 @@ module ActiveLDAP
|
|
861
870
|
entry.push(LDAP.mod(LDAP::LDAP_MOD_ADD|binary, pair[0], pair[1]))
|
862
871
|
end
|
863
872
|
end
|
864
|
-
tries = 0
|
865
873
|
begin
|
866
874
|
|
867
875
|
@@conn.add(@dn, entry)
|
868
876
|
|
869
877
|
@exists = true
|
870
|
-
rescue RuntimeError =>
|
878
|
+
rescue RuntimeError => detail
|
871
879
|
# The connection may have gone stale. Let's reconnect and retry.
|
872
|
-
if
|
873
|
-
|
874
|
-
end
|
875
|
-
tries += 1
|
876
|
-
# Reconnect and rebind.
|
877
|
-
do_connect()
|
878
|
-
retry
|
880
|
+
retry if Base.reconnect()
|
881
|
+
raise WriteError, "Could not add LDAP entry[#{Base.connection.err2string(Base.connection.err)}]: #{detail}"
|
879
882
|
rescue LDAP::ResultError => detail
|
880
883
|
raise WriteError, "Could not add LDAP entry[#{Base.connection.err2string(Base.connection.err)}]: #{detail}"
|
881
884
|
end
|
@@ -1138,66 +1141,65 @@ module ActiveLDAP
|
|
1138
1141
|
# Performs the actually connection. This separate so that it may
|
1139
1142
|
# be called to refresh stale connections.
|
1140
1143
|
def Base.do_connect()
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
# SSL using START_TLS
|
1144
|
+
begin
|
1145
|
+
case @@config[:method]
|
1146
|
+
when :ssl
|
1147
|
+
@@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], false)
|
1148
|
+
when :tls
|
1147
1149
|
@@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], true)
|
1148
|
-
|
1149
|
-
@@
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
end
|
1161
|
-
|
1150
|
+
when :plain
|
1151
|
+
@@conn = LDAP::Conn.new(@@config[:host], @@config[:port])
|
1152
|
+
else
|
1153
|
+
raise ConfigurationError,"#{@@config[:method]} is not one of the available connect methods :ssl, :tls, or :plain"
|
1154
|
+
end
|
1155
|
+
rescue ConfigurationError => e
|
1156
|
+
# Pass through
|
1157
|
+
raise e
|
1158
|
+
rescue => e
|
1159
|
+
@@logger.error("Failed to connect using #{@@config[:method]}")
|
1160
|
+
raise e
|
1161
|
+
end
|
1162
1162
|
|
1163
|
-
|
1164
|
-
|
1163
|
+
# Enforce LDAPv3
|
1164
|
+
@@conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
|
1165
1165
|
|
1166
|
-
|
1167
|
-
|
1168
|
-
rescue => e
|
1169
|
-
# Retry
|
1170
|
-
tries += 1
|
1171
|
-
raise e if tries > @@config[:retries]
|
1172
|
-
retry
|
1173
|
-
end
|
1174
|
-
end
|
1166
|
+
# Authenticate
|
1167
|
+
do_bind
|
1175
1168
|
|
1169
|
+
# Retrieve the schema. We need this to automagically determine attributes
|
1170
|
+
begin
|
1171
|
+
@@schema = @@conn.schema() if @@schema.nil?
|
1172
|
+
rescue => e
|
1173
|
+
@@logger.error("Failed to retrieve the schema (#{@@config[:method]})")
|
1174
|
+
@@logger.error("Schema failure exception: #{e.exception}")
|
1175
|
+
@@logger.error("Schema failure backtrace: #{e.backtrace}")
|
1176
|
+
raise ConnectionError, "#{e.exception} - LDAP connection failure, or server does not support schema queries."
|
1177
|
+
end
|
1178
|
+
|
1179
|
+
|
1180
|
+
end
|
1176
1181
|
|
1177
1182
|
# Wrapper all bind activity
|
1178
1183
|
def Base.do_bind()
|
1179
1184
|
bind_dn = @@config[:bind_format] % [@@config[:user]]
|
1180
|
-
if @@config[:password_block]
|
1181
|
-
password = @@config[:password_block].call
|
1182
|
-
@@config[:password_block] = Proc.new { password }
|
1183
|
-
end
|
1184
|
-
|
1185
1185
|
# Rough bind loop:
|
1186
1186
|
# Attempt 1: SASL if available
|
1187
1187
|
# Attempt 2: SIMPLE with credentials if password block
|
1188
1188
|
# Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail (or pwblock returns '')
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1189
|
+
if @@config[:try_sasl] and do_sasl_bind(bind_dn)
|
1190
|
+
@@logger.info('Bound SASL')
|
1191
|
+
elsif do_simple_bind(bind_dn)
|
1192
|
+
@@logger.info('Bound simple')
|
1193
|
+
elsif @@config[:allow_anonymous] and do_anonymous_bind(bind_dn)
|
1194
|
+
@@logger.info('Bound simple')
|
1195
|
+
else
|
1196
|
+
@@logger.error('Failed to bind using any available method')
|
1197
|
+
raise *LDAP::err2exception(@@conn.err) if @@conn.err != 0
|
1196
1198
|
end
|
1197
|
-
|
1199
|
+
|
1200
|
+
return @@conn.bound?
|
1198
1201
|
end
|
1199
1202
|
|
1200
|
-
|
1201
1203
|
# Base.do_anonymous_bind
|
1202
1204
|
#
|
1203
1205
|
# Bind to LDAP with the given DN, but with no password. (anonymous!)
|
@@ -1206,26 +1208,62 @@ module ActiveLDAP
|
|
1206
1208
|
begin
|
1207
1209
|
@@conn.bind()
|
1208
1210
|
return true
|
1209
|
-
rescue
|
1211
|
+
rescue => e
|
1212
|
+
|
1213
|
+
|
1210
1214
|
|
1211
1215
|
@@logger.warn "Warning: Anonymous authentication failed."
|
1216
|
+
@@logger.warn "message: #{e.message}"
|
1212
1217
|
return false
|
1213
1218
|
end
|
1214
1219
|
end
|
1215
1220
|
|
1216
1221
|
# Base.do_simple_bind
|
1217
1222
|
#
|
1218
|
-
# Bind to LDAP with the given DN and
|
1223
|
+
# Bind to LDAP with the given DN and password
|
1219
1224
|
def Base.do_simple_bind(bind_dn)
|
1220
|
-
|
1225
|
+
# Bail if we have no password or password block
|
1226
|
+
if not @@config[:password_block].nil? and not @@config[:password].nil?
|
1227
|
+
return false
|
1228
|
+
end
|
1229
|
+
|
1230
|
+
# TODO: Give a warning to reconnect users with password clearing
|
1231
|
+
# Get the passphrase for the first time, or anew if we aren't storing
|
1232
|
+
password = ''
|
1233
|
+
if not @@config[:password].nil?
|
1234
|
+
password = @@config[:password]
|
1235
|
+
elsif not @@config[:password_block].nil?
|
1236
|
+
unless @@config[:password_block].respond_to?(:call)
|
1237
|
+
@@logger.error('Skipping simple bind: ' +
|
1238
|
+
':password_block not nil or Proc object. Ignoring.')
|
1239
|
+
return false
|
1240
|
+
end
|
1241
|
+
password = @@config[:password_block].call
|
1242
|
+
else
|
1243
|
+
@@logger.error('Skipping simple bind: ' +
|
1244
|
+
':password_block and :password options are empty.')
|
1245
|
+
return false
|
1246
|
+
end
|
1247
|
+
|
1221
1248
|
begin
|
1222
|
-
@@conn.bind(bind_dn,
|
1223
|
-
|
1224
|
-
rescue
|
1249
|
+
@@conn.bind(bind_dn, password)
|
1250
|
+
rescue => e
|
1225
1251
|
|
1226
|
-
|
1252
|
+
# TODO: replace this with LDAP::err2exception()
|
1253
|
+
if @@conn.err == LDAP::LDAP_SERVER_DOWN
|
1254
|
+
@@logger.error "Warning: " + e.message
|
1255
|
+
else
|
1256
|
+
@@logger.warn "Warning: SIMPLE authentication failed."
|
1257
|
+
end
|
1227
1258
|
return false
|
1228
1259
|
end
|
1260
|
+
# Store the password for quick reference later
|
1261
|
+
if @@config[:store_password]
|
1262
|
+
@@config[:password] = password
|
1263
|
+
elsif @@config[:store_password] == false
|
1264
|
+
@@config[:password] = nil
|
1265
|
+
end
|
1266
|
+
return true
|
1229
1267
|
end
|
1230
1268
|
|
1231
1269
|
# Base.do_sasl_bind
|
@@ -1240,6 +1278,7 @@ module ActiveLDAP
|
|
1240
1278
|
# TODO: Investigate further SASL support
|
1241
1279
|
if mechanisms.respond_to? :member? and mechanisms.member? 'GSSAPI'
|
1242
1280
|
begin
|
1281
|
+
@@conn.sasl_quiet = @@config[:sasl_quiet] if @@config.has_key?(:sasl_quiet)
|
1243
1282
|
@@conn.sasl_bind(bind_dn, 'GSSAPI')
|
1244
1283
|
return true
|
1245
1284
|
rescue
|
@@ -6,9 +6,25 @@ module ActiveLDAP
|
|
6
6
|
# ActiveLDAP to work with your LDAP server. All of these
|
7
7
|
# settings can be passed in at initialization time.
|
8
8
|
module Configuration
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
DEFAULT_CONFIG = {}
|
10
|
+
DEFAULT_CONFIG[:host] = '127.0.0.1'
|
11
|
+
DEFAULT_CONFIG[:port] = 389
|
12
|
+
DEFAULT_CONFIG[:method] = :plain # :ssl, :tls, :plain allowed
|
13
|
+
|
14
|
+
DEFAULT_CONFIG[:bind_format] = "cn=%s,dc=localdomain"
|
15
|
+
DEFAULT_CONFIG[:user] = ENV['USER']
|
16
|
+
DEFAULT_CONFIG[:password_block] = nil
|
17
|
+
DEFAULT_CONFIG[:password] = nil
|
18
|
+
DEFAULT_CONFIG[:store_password] = true
|
19
|
+
DEFAULT_CONFIG[:allow_anonymous] = true
|
20
|
+
DEFAULT_CONFIG[:sasl_quiet] = false
|
21
|
+
DEFAULT_CONFIG[:try_sasl] = false
|
22
|
+
|
23
|
+
DEFAULT_CONFIG[:retries] = 3
|
24
|
+
DEFAULT_CONFIG[:retry_wait] = 3
|
25
|
+
|
26
|
+
DEFAULT_CONFIG[:logger] = nil
|
27
|
+
DEFAULT_CONFIG[:ldap_scope] = LDAP::LDAP_SCOPE_ONELEVEL
|
12
28
|
|
13
29
|
# Make the return value the string that is your LDAP base
|
14
30
|
def Base.base
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Extensions to Rubu/LDAP to make ActiveLDAP behave better
|
2
|
+
#
|
3
|
+
|
4
|
+
|
5
|
+
|
6
|
+
module LDAP
|
7
|
+
# Creates useful exceptions from err2string output
|
8
|
+
# Returns [exception, message] based on err2string
|
9
|
+
def LDAP.err2exception(errno=0)
|
10
|
+
err = LDAP::err2string(errno)
|
11
|
+
err = err.split(' ').collect {|w| w.capitalize }.join('')
|
12
|
+
err.gsub!(/[^A-Za-z]/, '')
|
13
|
+
# If the exception exists - raise it!
|
14
|
+
begin
|
15
|
+
exc = LDAP.const_get(err)
|
16
|
+
rescue NameError
|
17
|
+
# Doesn't exist :-)
|
18
|
+
LDAP.module_eval(<<-end_module_eval)
|
19
|
+
class #{err} < LDAP::ResultError
|
20
|
+
end
|
21
|
+
end_module_eval
|
22
|
+
exc = LDAP.const_get(err)
|
23
|
+
end
|
24
|
+
return [exc, err2string(errno)]
|
25
|
+
end
|
26
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.8.
|
2
|
+
rubygems_version: 0.8.11
|
3
3
|
specification_version: 1
|
4
4
|
name: ruby-activeldap
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2006-01
|
6
|
+
version: 0.7.0
|
7
|
+
date: 2006-05-03 00:00:00 +01:00
|
8
8
|
summary: Ruby/ActiveLDAP is a object-oriented API to LDAP
|
9
9
|
require_paths:
|
10
|
-
|
10
|
+
- lib
|
11
11
|
email: will@alum.bu.edu
|
12
12
|
homepage: http://projects.dataspill.org/libraries/ruby/activeldap/index.html
|
13
13
|
rubyforge_project: ruby-activeldap
|
@@ -18,37 +18,43 @@ bindir: bin
|
|
18
18
|
has_rdoc: false
|
19
19
|
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
20
|
requirements:
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
version: 0.0.0
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
25
24
|
version:
|
26
25
|
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
27
28
|
authors:
|
28
|
-
|
29
|
+
- Will Drewry
|
29
30
|
files:
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
- lib/activeldap
|
32
|
+
- lib/activeldap.rb
|
33
|
+
- lib/activeldap/associations.rb
|
34
|
+
- lib/activeldap/base.rb
|
35
|
+
- lib/activeldap/configuration.rb
|
36
|
+
- lib/activeldap/ldap.rb
|
37
|
+
- lib/activeldap/schema2.rb
|
36
38
|
test_files: []
|
39
|
+
|
37
40
|
rdoc_options: []
|
41
|
+
|
38
42
|
extra_rdoc_files: []
|
43
|
+
|
39
44
|
executables: []
|
45
|
+
|
40
46
|
extensions: []
|
47
|
+
|
41
48
|
requirements:
|
42
|
-
|
43
|
-
|
49
|
+
- (Open)LDAP server
|
50
|
+
- ruby-ldap = 0.8.2
|
44
51
|
dependencies:
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
version:
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: log4r
|
54
|
+
version_requirement:
|
55
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 1.0.4
|
60
|
+
version:
|