ruby-activeldap 0.5.8 → 0.5.9

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.
data/lib/activeldap.rb CHANGED
@@ -8,7 +8,7 @@
8
8
  # Ruby/ActiveLDAP is a novel way of interacting with LDAP. Most interaction with
9
9
  # LDAP is done using clunky LDIFs, web interfaces, or with painful APIs that
10
10
  # required a thick reference manual nearby. Ruby/ActiveLDAP aims to fix that.
11
- # Inspired by ActiveRecord[http://activerecord.rubyonrails.com], Ruby/ActiveLDAP provides an
11
+ # Inspired by ActiveRecord[http://activerecord.rubyonrails.org], Ruby/ActiveLDAP provides an
12
12
  # object oriented interface to LDAP entries.
13
13
  #
14
14
  # The target audience is system administrators and LDAP users everywhere that
@@ -26,8 +26,8 @@
26
26
  # against Microsoft's ActiveDirectory, despite what the name implies.)
27
27
  #
28
28
  # Further reading:
29
- # * RFC1777[http://www.faqs.com/rfcs/rfc1777.html] - Lightweight Directory Access Protocol
30
- # * OpenLDAP[http://www.openldap.com]
29
+ # * RFC1777[http://www.faqs.org/rfcs/rfc1777.html] - Lightweight Directory Access Protocol
30
+ # * OpenLDAP[http://www.openldap.org]
31
31
  #
32
32
  # === So why use Ruby/ActiveLDAP?
33
33
  #
@@ -50,18 +50,18 @@
50
50
  #
51
51
  # === Requirements
52
52
  #
53
- # * Ruby[http://www.ruby-lang.com] 1.8.x
54
- # * Ruby/LDAP[http://ruby-ldap.sourcefcome.net]
55
- # * Log4r[http://log4r.sourcefcome.net]
56
- # * (Optional) Ruby/LDAP+GSSAPI[http://caliban.com/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm]
57
- # * An LDAP server compatible with Ruby/LDAP: OpenLDAP[http://www.openldap.com], etc
53
+ # * Ruby[http://www.ruby-lang.org] 1.8.x
54
+ # * Ruby/LDAP[http://ruby-ldap.sourceforge.net]
55
+ # * Log4r[http://log4r.sourceforge.net]
56
+ # * (Optional) Ruby/LDAP+GSSAPI[http://caliban.org/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm]
57
+ # * An LDAP server compatible with Ruby/LDAP: OpenLDAP[http://www.openldap.org], etc
58
58
  # - Your LDAP server must allow root_dse queries to allow for schema queries
59
- # * Examples also require: Ruby/Password[http://raa.ruby-lang.com/project/ruby-password/]
59
+ # * Examples also require: Ruby/Password[http://raa.ruby-lang.org/project/ruby-password/]
60
60
  #
61
61
  # === Installation
62
62
  #
63
63
  # Assuming all the requirements are installed, you can install by grabbing the latest tgz file from
64
- # the download site[http://projects.example.com/libraries/ruby/activeldap/download.html].
64
+ # the download site[http://projects.dataspill.org/libraries/ruby/activeldap/download.html].
65
65
  #
66
66
  # The following steps will get the Ruby/ActiveLDAP installed in no time!
67
67
  #
@@ -183,23 +183,23 @@
183
183
  # As you can see, this method is used for defining how this class maps in to LDAP. Let's say that
184
184
  # my LDAP tree looks something like this:
185
185
  #
186
- # * dc=example,dc=com
187
- # |- ou=People,dc=example,dc=com
188
- # |+ ou=Groups,dc=example,dc=com
186
+ # * dc=dataspill,dc=org
187
+ # |- ou=People,dc=dataspill,dc=org
188
+ # |+ ou=Groups,dc=dataspill,dc=org
189
189
  # \
190
- # |- cn=develop,ou=Groups,dc=example,dc=com
191
- # |- cn=root,ou=Groups,dc=example,dc=com
190
+ # |- cn=develop,ou=Groups,dc=dataspill,dc=org
191
+ # |- cn=root,ou=Groups,dc=dataspill,dc=org
192
192
  # |- ...
193
193
  #
194
194
  # Under ou=People I store user objects, and under ou=Groups, I store group
195
195
  # objects. What |ldap_mapping| has done is mapped the class in to the LDAP tree
196
196
  # abstractly. With the given :dnattr and :prefix, it will only work for entries
197
- # under ou=Groups,dc=example,dc=com using the primary attribute 'cn' as the
197
+ # under ou=Groups,dc=dataspill,dc=org using the primary attribute 'cn' as the
198
198
  # beginning of the distinguished name.
199
199
  #
200
200
  # Just for clarity, here's how the arguments map out:
201
201
  #
202
- # cn=develop,ou=Groups,dc=example,dc=com
202
+ # cn=develop,ou=Groups,dc=dataspill,dc=org
203
203
  # ^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
204
204
  # :dnattr | |
205
205
  # :prefix |
@@ -233,11 +233,11 @@
233
233
  # tying objects together across the LDAP tree. Often, user objects will be
234
234
  # members of, or belong_to, Group objects.
235
235
  #
236
- # * dc=example,dc=com
237
- # |+ ou=People,dc=example,dc=com
236
+ # * dc=dataspill,dc=org
237
+ # |+ ou=People,dc=dataspill,dc=org
238
238
  # \
239
- # |- uid=drewry,ou=People,dc=example,dc=com
240
- # |- ou=Groups,dc=example,dc=com
239
+ # |- uid=drewry,ou=People,dc=dataspill,dc=org
240
+ # |- ou=Groups,dc=dataspill,dc=org
241
241
  #
242
242
  #
243
243
  # In the above tree, one such example would be user 'drewry' who is a part of the
@@ -385,7 +385,7 @@
385
385
  #
386
386
  # irb> Base.search(:base => 'dc=example,dc=com', :filter => '(uid=roo*)',
387
387
  # :scope => LDAP::LDAP_SCOPE_SUBTREE, :attrs => ['uid', 'cn'])
388
- # => [{"dn"=>"uid=root,ou=People,dc=example,dc=com","cn"=>["root"], "uidNumber"=>["0"]}]
388
+ # => [{"dn"=>"uid=root,ou=People,dc=dataspill,dc=org","cn"=>["root"], "uidNumber"=>["0"]}]
389
389
  # You can specify the :filter, :base, :scope, and :attrs, but they all have defaults --
390
390
  # * :filter defaults to objectClass=* - usually this isn't what you want
391
391
  # * :base defaults to the base of the class this is executed from (as set in ldap_mapping)
@@ -443,10 +443,10 @@
443
443
  # won't need to call Base.connect. Here is a fully parameterized call:
444
444
  #
445
445
  # Base.connect(
446
- # :host => 'ldap.example.com',
446
+ # :host => 'ldap.dataspill.org',
447
447
  # :port => 389,
448
- # :base => 'dc=example,dc=com',
449
- # :bind_format => "uid=%s,ou=People,dc=example,dc=com",
448
+ # :base => 'dc=dataspill,dc=org',
449
+ # :bind_format => "uid=%s,ou=People,dc=dataspill,dc=org",
450
450
  # :logger => log4r_obj,
451
451
  # :user => 'drewry',
452
452
  # :password_block => Proc.new { 'password12345' },
@@ -909,7 +909,7 @@ require 'activeldap/schema2'
909
909
 
910
910
 
911
911
  module ActiveLDAP
912
- VERSION = "0.5.8"
912
+ VERSION = "0.5.9"
913
913
  end
914
914
 
915
915
  ActiveLDAP::Base.class_eval do
@@ -33,7 +33,7 @@ module ActiveLDAP
33
33
  # class which can then be inherited, etc
34
34
  # which describe the mapping to LDAP.
35
35
  klass.class_eval <<-"end_eval"
36
- class << #{klass}
36
+ class << self
37
37
  # Return the list of required object classes
38
38
  def required_classes
39
39
  #{classes}
@@ -34,7 +34,7 @@ require 'ldap/schema'
34
34
  require 'log4r'
35
35
 
36
36
  module ActiveLDAP
37
- # OO-interface to LDAP assuming pam/nss_ldap-style comanization with Active specifics
37
+ # OO-interface to LDAP assuming pam/nss_ldap-style organization with Active specifics
38
38
  # Each subclass does a ldapsearch for the matching entry.
39
39
  # If no exact match, raise an error.
40
40
  # If match, change all LDAP attributes in accessor attributes on the object.
@@ -167,7 +167,7 @@ module ActiveLDAP
167
167
  # +config+ must be a hash that may contain any of the following fields:
168
168
  # :user, :password_block, :logger, :host, :port, :base, :bind_format, :try_sasl, :allow_anonymous
169
169
  # :user specifies the username to bind with.
170
- # :bind_format specifies the string to substitute the username into on bind. e.g. uid=%s,ou=People,dc=example,dc=com. Overrides @@bind_format.
170
+ # :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.
171
171
  # :password_block specifies a Proc object that will yield a String to be used as the password when called.
172
172
  # :logger specifies a preconfigured Log4r::Logger to be used for all logging
173
173
  # :host overrides the configuration.rb @@host setting with the LDAP server hostname
@@ -294,7 +294,7 @@ module ActiveLDAP
294
294
  rescue RuntimeError => detail
295
295
  #TODO# Check for 'No message' when retrying
296
296
  # The connection may have gone stale. Let's reconnect and retry.
297
- if tries > @max_retries
297
+ if tries > @@config[:retries]
298
298
  # Do nothing on failure
299
299
 
300
300
  end
@@ -346,12 +346,12 @@ module ActiveLDAP
346
346
  tries = 0
347
347
  begin
348
348
  # Get some attributes
349
- @@conn.search(base(), LDAP::LDAP_SCOPE_SUBTREE, "(#{attr}=#{val})") do |m|
349
+ @@conn.search(base(), LDAP::LDAP_SCOPE_ONELEVEL, "(#{attr}=#{val})") do |m|
350
350
  # Extract the dnattr value
351
351
  dnval = m.dn.split(/,/)[0].split(/=/)[1]
352
352
 
353
353
  if objects
354
- return eval("#{real_klass}.new(m)")
354
+ return real_klass.new(m)
355
355
  else
356
356
  return dnval
357
357
  end
@@ -359,7 +359,7 @@ module ActiveLDAP
359
359
  rescue RuntimeError => detail
360
360
  #todo# check for 'no message' when retrying
361
361
  # the connection may have gone stale. let's reconnect and retry.
362
- if tries > @max_retries
362
+ if tries > @@config[:retries]
363
363
  # do nothing on failure
364
364
 
365
365
  end
@@ -394,11 +394,12 @@ module ActiveLDAP
394
394
 
395
395
  # Allow a single string argument
396
396
  val = config
397
+ attr = dnattr()
397
398
  objects = false
398
399
  # Or a hash
399
400
  if config.respond_to?"has_key?"
400
- attr = config[:attribute] || dnattr()
401
401
  val = config[:value] || '*'
402
+ attr = config[:attribute] || dnattr()
402
403
  objects = config[:objects]
403
404
  end
404
405
 
@@ -407,20 +408,23 @@ module ActiveLDAP
407
408
  tries = 0
408
409
  begin
409
410
  # Get some attributes
410
- @@conn.search(base(), LDAP::LDAP_SCOPE_SUBTREE, "(#{attr}=#{val})") do |m|
411
+ @@conn.search(base(), LDAP::LDAP_SCOPE_ONELEVEL, "(#{attr}=#{val})") do |m|
411
412
  # Extract the dnattr value
412
413
  dnval = m.dn.split(/,/)[0].split(/=/)[1]
413
414
 
414
415
  if objects
415
- matches.push(eval("#{real_klass}.new(m)"))
416
+ matches.push(real_klass.new(m))
416
417
  else
417
418
  matches.push(dnval)
418
419
  end
419
420
  end
420
421
  rescue RuntimeError => detail
421
422
  #todo# check for 'no message' when retrying
423
+
424
+ #TODO# This is broken because search gives bad messages.
425
+
422
426
  # the connection may have gone stale. let's reconnect and retry.
423
- if tries > @max_retries
427
+ if tries > @@config[:retries]
424
428
  # do nothing on failure
425
429
 
426
430
  end
@@ -508,7 +512,10 @@ module ActiveLDAP
508
512
  @data = {} # where the r/w entry data is stored
509
513
  @ldap_data = {} # original ldap entry data
510
514
  @attr_methods = {} # list of valid method calls for attributes used for dereferencing
511
- @last_oc = nil # for use in other methods for "caching"
515
+ @last_oc = false # for use in other methods for "caching"
516
+ if dnattr().empty?
517
+ raise RuntimeError, "dnattr() not set for this class."
518
+ end
512
519
 
513
520
  # Break val apart if it is a dn
514
521
  if val.match(/^#{dnattr()}=([^,=]+),#{base()}$/i)
@@ -529,7 +536,7 @@ module ActiveLDAP
529
536
  tries = 0
530
537
  begin
531
538
  # Get some attributes
532
- Base.connection.search("#{dnattr()}=#{val},#{base()}", LDAP::LDAP_SCOPE_SUBTREE, "objectClass=*") do |m|
539
+ Base.connection.search(base(), LDAP::LDAP_SCOPE_ONELEVEL, "(#{dnattr()}=#{val})") do |m|
533
540
  # Save DN
534
541
  @dn = m.dn
535
542
  # Load up data into tmp
@@ -557,24 +564,24 @@ module ActiveLDAP
557
564
  @ldap_data.each do |pair|
558
565
  send(:attribute_method=, pair[0], pair[1].dup)
559
566
  end
560
- rescue RuntimeError => detail
561
- #todo# check for 'no message' when retrying
562
- # the connection may have gone stale. let's reconnect and retry.
563
- if tries > @max_retries
564
- @exists = false
565
- # Create what should be the authoritative DN
566
- @dn = "#{dnattr()}=#{val},#{base()}"
567
- send(:apply_objectclass, required_classes())
568
-
569
- # Setup dn attribute (later rdn too!)
570
- attr_sym = "#{dnattr()}=".to_sym
571
-
572
- send(attr_sym, val)
573
- end
574
- tries += 1
575
- # reconnect and rebind.
576
- do_connect()
577
- retry
567
+ rescue RuntimeError => detail
568
+ #todo# check for 'no message' when retrying
569
+ # the connection may have gone stale. let's reconnect and retry.
570
+ if tries > @@config[:retries]
571
+ @exists = false
572
+ # Create what should be the authoritative DN
573
+ @dn = "#{dnattr()}=#{val},#{base()}"
574
+ send(:apply_objectclass, required_classes())
575
+
576
+ # Setup dn attribute (later rdn too!)
577
+ attr_sym = "#{dnattr()}=".to_sym
578
+
579
+ send(attr_sym, val)
580
+ end
581
+ tries += 1
582
+ # reconnect and rebind.
583
+ do_connect()
584
+ retry
578
585
  rescue LDAP::ResultError
579
586
  @exists = false
580
587
  # Create what should be the authoritative DN
@@ -672,16 +679,16 @@ module ActiveLDAP
672
679
  begin
673
680
  @@conn.delete(@dn)
674
681
  @exists = false
675
- rescue RuntimeError => detail
676
- #todo# check for 'no message' when retrying
677
- # the connection may have gone stale. let's reconnect and retry.
678
- if tries > @max_retries
682
+ rescue RuntimeError => detail
683
+ #todo# check for 'no message' when retrying
684
+ # the connection may have gone stale. let's reconnect and retry.
685
+ if tries > @@config[:retries]
679
686
  raise DeleteError, "Failed to delete LDAP entry: '#{@dn}'"
680
- end
681
- tries += 1
682
- # reconnect and rebind.
683
- do_connect()
684
- retry
687
+ end
688
+ tries += 1
689
+ # reconnect and rebind.
690
+ do_connect()
691
+ retry
685
692
  rescue LDAP::ResultError => detail
686
693
  raise DeleteError, "Failed to delete LDAP entry: '#{@dn}'"
687
694
  end
@@ -812,16 +819,16 @@ module ActiveLDAP
812
819
 
813
820
  @@conn.modify(@dn, entry)
814
821
 
815
- rescue RuntimeError => detail
816
- #todo# check for 'no message' when retrying
817
- # the connection may have gone stale. let's reconnect and retry.
818
- if tries > @max_retries
819
- raise WriteError, "Could not update LDAP entry: #{detail}"
820
- end
821
- tries += 1
822
- # reconnect and rebind.
823
- do_connect()
824
- retry
822
+ rescue RuntimeError => detail
823
+ #todo# check for 'no message' when retrying
824
+ # the connection may have gone stale. let's reconnect and retry.
825
+ if tries > @@config[:retries]
826
+ raise WriteError, "Could not update LDAP entry: #{detail}"
827
+ end
828
+ tries += 1
829
+ # reconnect and rebind.
830
+ do_connect()
831
+ retry
825
832
  rescue => detail
826
833
  raise WriteError, "Could not update LDAP entry: #{detail}"
827
834
  end
@@ -853,7 +860,7 @@ module ActiveLDAP
853
860
  @exists = true
854
861
  rescue RuntimeError => e
855
862
  # The connection may have gone stale. Let's reconnect and retry.
856
- if tries > @max_retries
863
+ if tries > @@config[:retries]
857
864
  raise WriteError, "Could not add LDAP entry[#{Base.connection.err2string(Base.connection.err)}]: #{detail}"
858
865
  end
859
866
  tries += 1
@@ -984,6 +991,9 @@ module ActiveLDAP
984
991
 
985
992
  new_oc = val
986
993
  new_oc = [val] if new_oc.class != Array
994
+ if defined?(@last_oc).nil?
995
+ @last_oc = false
996
+ end
987
997
  return new_oc if @last_oc == new_oc
988
998
 
989
999
  # Store for caching purposes
@@ -996,16 +1006,16 @@ module ActiveLDAP
996
1006
  # Build |data| from schema
997
1007
  # clear attr_method mapping first
998
1008
  @attr_methods = {}
999
- @must = []
1000
- @may = []
1009
+ @must = []
1010
+ @may = []
1001
1011
  new_oc.each do |objc|
1002
1012
  # get all attributes for the class
1003
1013
  attributes = Base.schema.class_attributes(objc.to_s)
1004
- @must += attributes[:must]
1005
- @may += attributes[:may]
1014
+ @must += attributes[:must]
1015
+ @may += attributes[:may]
1006
1016
  end
1007
- @must.uniq!
1008
- @may.uniq!
1017
+ @must.uniq!
1018
+ @may.uniq!
1009
1019
  (@must+@may).each do |attr|
1010
1020
  # Update attr_method with appropriate
1011
1021
  define_attribute_methods(attr)
@@ -1116,31 +1126,40 @@ module ActiveLDAP
1116
1126
  # Performs the actually connection. This separate so that it may
1117
1127
  # be called to refresh stale connections.
1118
1128
  def Base.do_connect()
1119
- # Connect to LDAP
1120
- begin
1121
- # SSL using START_TLS
1122
- @@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], true)
1123
- rescue
1124
- @@logger.warn "Warning: Failed to connect using TLS!"
1125
- begin
1126
- @@logger.warn "Warning: Attempting SSL connection . . ."
1127
- @@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], false)
1128
- # HACK: Load the schema here because otherwise you can't tell if the
1129
- # HACK: SSLConn is a real SSL connection.
1130
- @@schema = @@conn.schema() if @@schema.nil?
1131
- rescue
1132
- @@logger.warn "Warning: Attempting unencrypted connection . . ."
1133
- @@conn = LDAP::Conn.new(@@config[:host], @@config[:port])
1134
- end
1135
- end
1136
-
1137
-
1138
- # Enforce LDAPv3
1139
- @@conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
1140
-
1141
- # Authenticate
1142
- do_bind
1143
- end
1129
+ # Wrap the whole thing an try retries times
1130
+ tries = 0
1131
+ begin
1132
+ # Connect to LDAP
1133
+ begin
1134
+ # SSL using START_TLS
1135
+ @@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], true)
1136
+ rescue
1137
+ @@logger.warn "Warning: Failed to connect using TLS!"
1138
+ begin
1139
+ @@logger.warn "Warning: Attempting SSL connection . . ."
1140
+ @@conn = LDAP::SSLConn.new(@@config[:host], @@config[:port], false)
1141
+ # HACK: Load the schema here because otherwise you can't tell if the
1142
+ # HACK: SSLConn is a real SSL connection.
1143
+ @@schema = @@conn.schema() if @@schema.nil?
1144
+ rescue
1145
+ @@logger.warn "Warning: Attempting unencrypted connection . . ."
1146
+ @@conn = LDAP::Conn.new(@@config[:host], @@config[:port])
1147
+ end
1148
+ end
1149
+
1150
+
1151
+ # Enforce LDAPv3
1152
+ @@conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
1153
+
1154
+ # Authenticate
1155
+ do_bind
1156
+ rescue => e
1157
+ # Retry
1158
+ tries += 1
1159
+ raise e if tries > @@config[:retries]
1160
+ retry
1161
+ end
1162
+ end
1144
1163
 
1145
1164
 
1146
1165
  # Wrapper all bind activity
@@ -1205,7 +1224,7 @@ module ActiveLDAP
1205
1224
  mechanisms = @@conn.root_dse[0]['supportedSASLMechanisms']
1206
1225
  # Use GSSAPI if available
1207
1226
  # Currently only GSSAPI is supported with Ruby/LDAP from
1208
- # http://caliban.com/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm
1227
+ # http://caliban.org/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm
1209
1228
  # TODO: Investigate further SASL support
1210
1229
  if mechanisms.respond_to? :member? and mechanisms.member? 'GSSAPI'
1211
1230
  begin
@@ -8,11 +8,11 @@ module ActiveLDAP
8
8
  module Configuration
9
9
  @@host = "127.0.0.1"
10
10
  @@port = 389
11
- @@bind_format = "uid=%s,ou=People,dc=example,dc=com"
11
+ @@bind_format = "uid=%s,ou=People,dc=localdomain"
12
12
 
13
13
  # Make the return value the string that is your LDAP base
14
14
  def Base.base
15
- 'dc=example,dc=com'
15
+ 'dc=localdomain'
16
16
  end
17
17
 
18
18
  # This is optionally set to the array of objectClass names
@@ -14,9 +14,9 @@ module LDAP
14
14
  # attr('attributeTypes', 'cn', 'DESC')
15
15
  # attr('ldapSyntaxes', '1.3.6.1.4.1.1466.115.121.1.5', 'DESC')
16
16
  def attr(sub, type, at)
17
- return '' if sub.empty?
18
- return '' if type.empty?
19
- return '' if at.empty?
17
+ return [] if sub.empty?
18
+ return [] if type.empty?
19
+ return [] if at.empty?
20
20
 
21
21
  # Check already parsed options first
22
22
  if @@attr_cache.has_key? sub \
@@ -169,6 +169,7 @@ module LDAP
169
169
  sups.each do |sup|
170
170
  new_sups += attr('objectClasses', sup, 'SUP')
171
171
  end
172
+
172
173
  sups += new_sups
173
174
  sups.uniq!
174
175
  break if sups.size == start_size
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.4
3
3
  specification_version: 1
4
4
  name: ruby-activeldap
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.5.8
7
- date: 2005-10-31
6
+ version: 0.5.9
7
+ date: 2005-11-01
8
8
  summary: Ruby/ActiveLDAP is a object-oriented API to LDAP
9
9
  require_paths:
10
10
  - lib