ruby-activeldap 0.5.8 → 0.5.9

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