ruby-activeldap 0.7.4 → 0.8.0

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 (74) hide show
  1. data/CHANGES +375 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +58 -0
  4. data/Manifest.txt +33 -0
  5. data/README +63 -0
  6. data/Rakefile +37 -0
  7. data/TODO +31 -0
  8. data/benchmark/bench-al.rb +152 -0
  9. data/lib/{activeldap.rb → active_ldap.rb} +280 -263
  10. data/lib/active_ldap/adaptor/base.rb +29 -0
  11. data/lib/active_ldap/adaptor/ldap.rb +466 -0
  12. data/lib/active_ldap/association/belongs_to.rb +38 -0
  13. data/lib/active_ldap/association/belongs_to_many.rb +40 -0
  14. data/lib/active_ldap/association/collection.rb +80 -0
  15. data/lib/active_ldap/association/has_many.rb +48 -0
  16. data/lib/active_ldap/association/has_many_wrap.rb +56 -0
  17. data/lib/active_ldap/association/proxy.rb +89 -0
  18. data/lib/active_ldap/associations.rb +162 -0
  19. data/lib/active_ldap/attributes.rb +199 -0
  20. data/lib/active_ldap/base.rb +1343 -0
  21. data/lib/active_ldap/callbacks.rb +19 -0
  22. data/lib/active_ldap/command.rb +46 -0
  23. data/lib/active_ldap/configuration.rb +96 -0
  24. data/lib/active_ldap/connection.rb +137 -0
  25. data/lib/{activeldap → active_ldap}/ldap.rb +1 -1
  26. data/lib/active_ldap/object_class.rb +70 -0
  27. data/lib/active_ldap/schema.rb +258 -0
  28. data/lib/{activeldap → active_ldap}/timeout.rb +0 -0
  29. data/lib/{activeldap → active_ldap}/timeout_stub.rb +0 -0
  30. data/lib/active_ldap/user_password.rb +92 -0
  31. data/lib/active_ldap/validations.rb +78 -0
  32. data/rails/plugin/active_ldap/README +54 -0
  33. data/rails/plugin/active_ldap/init.rb +6 -0
  34. data/test/TODO +2 -0
  35. data/test/al-test-utils.rb +337 -0
  36. data/test/command.rb +62 -0
  37. data/test/config.yaml +8 -0
  38. data/test/config.yaml.sample +6 -0
  39. data/test/run-test.rb +17 -0
  40. data/test/test-unit-ext.rb +2 -0
  41. data/test/test_associations.rb +334 -0
  42. data/test/test_attributes.rb +71 -0
  43. data/test/test_base.rb +345 -0
  44. data/test/test_base_per_instance.rb +32 -0
  45. data/test/test_bind.rb +53 -0
  46. data/test/test_callback.rb +35 -0
  47. data/test/test_connection.rb +38 -0
  48. data/test/test_connection_per_class.rb +50 -0
  49. data/test/test_find.rb +36 -0
  50. data/test/test_groupadd.rb +50 -0
  51. data/test/test_groupdel.rb +46 -0
  52. data/test/test_groupls.rb +107 -0
  53. data/test/test_groupmod.rb +51 -0
  54. data/test/test_lpasswd.rb +75 -0
  55. data/test/test_object_class.rb +32 -0
  56. data/test/test_reflection.rb +173 -0
  57. data/test/test_schema.rb +166 -0
  58. data/test/test_user.rb +209 -0
  59. data/test/test_user_password.rb +93 -0
  60. data/test/test_useradd-binary.rb +59 -0
  61. data/test/test_useradd.rb +55 -0
  62. data/test/test_userdel.rb +48 -0
  63. data/test/test_userls.rb +86 -0
  64. data/test/test_usermod-binary-add-time.rb +62 -0
  65. data/test/test_usermod-binary-add.rb +61 -0
  66. data/test/test_usermod-binary-del.rb +64 -0
  67. data/test/test_usermod-lang-add.rb +57 -0
  68. data/test/test_usermod.rb +56 -0
  69. data/test/test_validation.rb +38 -0
  70. metadata +94 -21
  71. data/lib/activeldap/associations.rb +0 -170
  72. data/lib/activeldap/base.rb +0 -1456
  73. data/lib/activeldap/configuration.rb +0 -59
  74. data/lib/activeldap/schema2.rb +0 -217
data/LICENSE ADDED
@@ -0,0 +1,58 @@
1
+ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.co.jp>.
2
+ You can redistribute it and/or modify it under either the terms of the GPL
3
+ (see COPYING file), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) rename any non-standard executables so the names do not conflict
21
+ with standard executables, which must also be provided.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or executable
26
+ form, provided that you do at least ONE of the following:
27
+
28
+ a) distribute the executables and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard executables non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under this terms.
43
+
44
+ They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
45
+ files under the ./missing directory. See each file for the copying
46
+ condition.
47
+
48
+ 5. The scripts and library files supplied as input to or produced as
49
+ output from the software do not automatically fall under the
50
+ copyright of the software, but belong to whomever generated them,
51
+ and may be sold commercially, and may be aggregated with this
52
+ software.
53
+
54
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
55
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
56
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57
+ PURPOSE.
58
+
data/Manifest.txt ADDED
@@ -0,0 +1,33 @@
1
+ Rakefile
2
+ LICENSE
3
+ TODO
4
+ COPYING
5
+ CHANGES
6
+ README
7
+ Manifest.txt
8
+ lib/active_ldap/adaptor/ldap.rb
9
+ lib/active_ldap/adaptor/base.rb
10
+ lib/active_ldap/association/has_many_wrap.rb
11
+ lib/active_ldap/association/has_many.rb
12
+ lib/active_ldap/association/proxy.rb
13
+ lib/active_ldap/association/belongs_to_many.rb
14
+ lib/active_ldap/association/collection.rb
15
+ lib/active_ldap/association/belongs_to.rb
16
+ lib/active_ldap/validations.rb
17
+ lib/active_ldap/command.rb
18
+ lib/active_ldap/callbacks.rb
19
+ lib/active_ldap/ldap.rb
20
+ lib/active_ldap/timeout_stub.rb
21
+ lib/active_ldap/timeout.rb
22
+ lib/active_ldap/attributes.rb
23
+ lib/active_ldap/object_class.rb
24
+ lib/active_ldap/connection.rb
25
+ lib/active_ldap/associations.rb
26
+ lib/active_ldap/user_password.rb
27
+ lib/active_ldap/schema.rb
28
+ lib/active_ldap/configuration.rb
29
+ lib/active_ldap/base.rb
30
+ lib/active_ldap.rb
31
+ benchmark/bench-al.rb
32
+ rails/plugin/active_ldap/init.rb
33
+ rails/plugin/active_ldap/README
data/README ADDED
@@ -0,0 +1,63 @@
1
+ Ruby/ActiveLdap -- ruby library for object-oriented LDAP interction
2
+ Copyright (C) 2004-2006 Will Drewry <will@alum.bu.edu>, Kouhei Sutou <kou@cozmixng.org>
3
+ Contributors:
4
+ * Dick Davies <rasputnik@hellooperator.net>
5
+ * Nathan Kinder <quicksilver02@mac.com>
6
+ * Patrick Cole <pac@independent.com.au>
7
+ * Google Inc.
8
+
9
+ DESCRIPTION
10
+ 'Ruby/ActiveLdap' is a ruby extension library which provides a clean objected
11
+ oriented interface to the Ruby/LDAP[0] library. It was inspired by
12
+ ActivRecord[2]. This is not nearly as clean or as flexible as ActiveRecord, but
13
+ it is still trivial to define new objects and manipulate them with minimal
14
+ difficulty.
15
+
16
+ For example and usage - read the rdoc in doc/ from lib/activeldap.rb.
17
+ It is also available on the web at:
18
+
19
+ http://static.dataspill.org/projects/libraries/ruby/activeldap/rdoc/
20
+
21
+ PREREQUISITES
22
+
23
+ * Ruby 1.8.x [1]
24
+ * Ruby/LDAP [0]
25
+ * Log4r [4]
26
+ * ActiveRecord [2]
27
+ * (Optional) Ruby/LDAP+GSSAPI [3]
28
+ * An LDAP server compatible with Ruby/LDAP: OpenLDAP, etc
29
+
30
+
31
+ NOTES
32
+
33
+ * Only GSSAPI SASL support exists due to Ruby/LDAP limitations
34
+ * The API is subject to change as this package slowly approaches 1.0.0
35
+
36
+
37
+ INSTALL
38
+
39
+ - Edit active_ldap/configuration.rb with your LDAP preferences
40
+ - Run -
41
+ sudo rake install
42
+
43
+
44
+ LICENCE
45
+
46
+ This program is free software; you can redistribute it and/or modify
47
+ it under the terms of the GNU General Public License as published by
48
+ the Free Software Foundation; either version 2, or (at your option)
49
+ any later version.
50
+
51
+ Please see the file LICENSE for the terms of the licence.
52
+
53
+
54
+ REFERENCES
55
+
56
+ [0] - http://ruby-ldap.sourceforge.net
57
+ [1] - http://www.ruby-lang.org
58
+ [2] - http://activerecord.rubyonrails.org
59
+ [3] - http://caliban.org/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm
60
+ [4] - http://log4r.sourceforge.net/
61
+
62
+
63
+
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ $:<<'./lib'
6
+ require 'active_ldap'
7
+
8
+ Hoe.new('ruby-activeldap', ActiveLdap::VERSION) do |project|
9
+ project.rubyforge_name = 'ruby-activeldap'
10
+ project.author = ['Will Drewry', 'Kouhei Sutou']
11
+ project.email = ['will@alum.bu.edu', 'kou@cozmixng.org']
12
+ project.summary = 'Ruby/ActiveLdap is a object-oriented API to LDAP'
13
+ project.url = 'http://rubyforge.org/projects/ruby-activeldap/'
14
+ project.test_globs = ['test/**']
15
+ project.changes = project.paragraphs_of('CHANGES', 0..1).join("\n\n")
16
+ project.extra_deps = [['log4r','>= 1.0.4'], 'activerecord']
17
+ project.spec_extras = {
18
+ :requirements => ['ruby-ldap >= 0.8.2', '(Open)LDAP server'],
19
+ :autorequire => 'active_ldap'
20
+ }
21
+ project.description = String.new(<<-EOF)
22
+ 'Ruby/ActiveLdap' is a ruby extension library which provides a clean
23
+ objected oriented interface to the Ruby/LDAP library. It was inspired
24
+ by ActiveRecord. This is not nearly as clean or as flexible as
25
+ ActiveRecord, but it is still trivial to define new objects and manipulate
26
+ them with minimal difficulty.
27
+ EOF
28
+ end
29
+
30
+ desc 'Tag the repository for release.'
31
+ task :tag do
32
+ system "svn copy -m 'New release tag' https://ruby-activeldap.googlecode.com/svn/trunk https://ruby-activeldap.googlecode.com/svn/tags/r#{ActiveLdap::VERSION}"
33
+ end
34
+
35
+
36
+
37
+ # vim: syntax=Ruby
data/TODO ADDED
@@ -0,0 +1,31 @@
1
+ - Fix case sensitivity in object classes
2
+ - Add result pagination via LDAP::Controls
3
+ - serialize & serialized_attributes
4
+ - schema mgmt - how does AR handle it?
5
+ - columns() -- ?
6
+ http://api.rubyonrails.com/classes/ActiveRecord/Base.html#M000865
7
+ - provide full documentation for new API.
8
+ - handle all exception raised from Ruby/LDAP and wrap as
9
+ ActiveLdap exception. I think we need to develop an
10
+ application using ActiveLdap.
11
+ - support Reloadable::Subclasses. I think we need to
12
+ develop a Rails + ActiveLdap application to improve
13
+ Rails support.
14
+ - support Ruby/GetText.
15
+ - support Net::LDAP as LDAP backend after Net::LDAP
16
+ supports START_TLS. (I made a patch and submitted to the
17
+ bug tracker of Net::LDAP)
18
+ - Add locking around Timeout.alarm() to ensure a multithreaded ruby
19
+ app doesn't hit any race conditions
20
+ - Add AR matching exceptions:
21
+ * ActiveRecordError -- ActiveLdapError as base
22
+ * AssociationTypeMismatch
23
+ * SerializationTypeMismatch
24
+ * ConnectionNotEstablished
25
+ * RecordNotFound
26
+ * LdapActionInvalid - like StatementInvalid
27
+ * MultiparameterAssignmentErrors
28
+ * AttributeAssignmentError
29
+ * RecordNotSaved
30
+
31
+
@@ -0,0 +1,152 @@
1
+ base = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(File.expand_path(base))
3
+ $LOAD_PATH.unshift(File.expand_path(File.join(base, "..", "lib")))
4
+
5
+ require "active_ldap"
6
+ require "benchmark"
7
+
8
+ LDAP_SERVER = "127.0.0.1"
9
+ LDAP_PORT = 389
10
+ LDAP_BASE = ENV["LDAP_BASE"] || "dc=localdomain"
11
+ LDAP_PREFIX = "ou=People"
12
+ LDAP_USER = nil
13
+ LDAP_PASSWORD = nil
14
+
15
+ class ALUser < ActiveLdap::Base
16
+ ldap_mapping :dn_attribute => 'uid', :prefix => LDAP_PREFIX,
17
+ :classes => ['posixAccount', 'person']
18
+ end
19
+
20
+ # === search_al
21
+ #
22
+ def search_al
23
+ count = 0
24
+ ALUser.find(:all).each do |e|
25
+ count += 1
26
+ end
27
+ return count
28
+ end # -- search_al
29
+
30
+ def search_al_without_object_creation
31
+ count = 0
32
+ ALUser.search.each do |e|
33
+ count += 1
34
+ end
35
+ return count
36
+ end
37
+
38
+ # === search_ldap
39
+ #
40
+ def search_ldap(conn)
41
+ count = 0
42
+ conn.search("#{LDAP_PREFIX},#{LDAP_BASE}",
43
+ LDAP::LDAP_SCOPE_SUBTREE,
44
+ "(uid=*)") do |e|
45
+ count += 1
46
+ end
47
+ count
48
+ end # -- search_ldap
49
+
50
+ def populate_base
51
+ suffixes = []
52
+ ActiveLdap::Base.base.split(/,/).reverse_each do |suffix|
53
+ prefix = suffixes.join(",")
54
+ suffixes.unshift(suffix)
55
+ name, value = suffix.split(/=/, 2)
56
+ next unless name == "dc"
57
+ dc_class = Class.new(ActiveLdap::Base)
58
+ dc_class.ldap_mapping :dnattr => "dc",
59
+ :prefix => "",
60
+ :scope => :base,
61
+ :classes => ["top", "dcObject", "organization"]
62
+ dc_class.instance_variable_set("@base", prefix)
63
+ next if dc_class.exists?(value, :prefix => "dc=#{value}")
64
+ dc = dc_class.new(value)
65
+ dc.o = dc.dc
66
+ dc.save
67
+ end
68
+
69
+ if ActiveLdap::Base.search.empty?
70
+ raise "Can't populate #{ActiveLdap::Base.base}"
71
+ end
72
+ end
73
+
74
+ def populate_users
75
+ ou_class = Class.new(ActiveLdap::Base)
76
+ ou_class.ldap_mapping :dnattr => "ou",
77
+ :prefix => "",
78
+ :classes => ["top", "organizationalUnit"]
79
+ ou_class.new(LDAP_PREFIX.split(/=/)[1]).save!
80
+
81
+ 100.times do |i|
82
+ name = i.to_s
83
+ user = ALUser.new(name)
84
+ user.uid_number = 100000 + i
85
+ user.gid_number = 100000 + i
86
+ user.cn = name
87
+ user.sn = name
88
+ user.home_directory = "/nonexistent"
89
+ user.save!
90
+ end
91
+ end
92
+
93
+ def populate
94
+ populate_base
95
+ populate_users
96
+ end
97
+
98
+ # === main(argv)
99
+ #
100
+ def main(argv)
101
+ # Connect with AL
102
+ #
103
+ config = {
104
+ :host => LDAP_SERVER,
105
+ :port => LDAP_PORT,
106
+ :base => LDAP_BASE,
107
+ }
108
+
109
+ do_populate = LDAP_USER && LDAP_PASSWORD
110
+
111
+ if do_populate
112
+ config[:bind_format] = LDAP_USER
113
+ config[:password] = LDAP_PASSWORD
114
+ end
115
+ ActiveLdap::Base.establish_connection(config)
116
+
117
+ if do_populate
118
+ puts "populating..."
119
+ dumped_data = ActiveLdap::Base.dump(:scope => :sub)
120
+ ActiveLdap::Base.delete_all(nil, :scope => :sub)
121
+ populate
122
+ end
123
+
124
+ # Standard connection
125
+ #
126
+ conn = LDAP::Conn.new(LDAP_SERVER, LDAP_PORT)
127
+ al_count = 0
128
+ al_count_without_object_creation = 0
129
+ ldap_count = 0
130
+ Benchmark.bm(10) do |x|
131
+ x.report("AL") { al_count = search_al }
132
+ x.report("AL(No Obj)") do
133
+ al_count_without_object_creation = search_al_without_object_creation
134
+ end
135
+ x.report("LDAP") { ldap_count = search_ldap(conn) }
136
+ end
137
+ print "Entries processed by Ruby/ActiveLdap: #{al_count}\n"
138
+ print "Entries processed by Ruby/ActiveLdap (without object creation)" +
139
+ ": #{al_count_without_object_creation}\n"
140
+ print "Entries processed by Ruby/LDAP: #{ldap_count}\n"
141
+
142
+ 0
143
+ ensure
144
+ if do_populate
145
+ ActiveLdap::Base.delete_all(nil, :scope => :sub)
146
+ ActiveLdap::Base.load(dumped_data)
147
+ end
148
+ end
149
+
150
+ if $0 == __FILE__ then
151
+ exit(main(ARGV) || 1)
152
+ end
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/ruby
2
- # = Ruby/ActiveLDAP
2
+ # = Ruby/ActiveLdap
3
3
  #
4
- # "Ruby/ActiveLDAP" Copyright (C) 2004,2005 Will Drewry mailto:will@alum.bu.edu
4
+ # "Ruby/ActiveLdap" Copyright (C) 2004,2005 Will Drewry mailto:will@alum.bu.edu
5
5
  #
6
6
  # == Introduction
7
7
  #
8
- # Ruby/ActiveLDAP is a novel way of interacting with LDAP. Most interaction with
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
- # required a thick reference manual nearby. Ruby/ActiveLDAP aims to fix that.
11
- # Inspired by ActiveRecord[http://activerecord.rubyonrails.org], Ruby/ActiveLDAP provides an
10
+ # required a thick reference manual nearby. Ruby/ActiveLdap aims to fix that.
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
@@ -29,22 +29,22 @@
29
29
  # * RFC1777[http://www.faqs.org/rfcs/rfc1777.html] - Lightweight Directory Access Protocol
30
30
  # * OpenLDAP[http://www.openldap.org]
31
31
  #
32
- # === So why use Ruby/ActiveLDAP?
32
+ # === So why use Ruby/ActiveLdap?
33
33
  #
34
34
  # Well if you like to fumble around in the dark, dank innards of LDAP, you can
35
35
  # quit reading now. However, if you'd like a cleaner way to integrate LDAP in to
36
- # your existing code, hopefully that's why you'll want to use Ruby/ActiveLDAP.
36
+ # your existing code, hopefully that's why you'll want to use Ruby/ActiveLdap.
37
37
  #
38
38
  # Using LDAP directly (even with the excellent Ruby/LDAP), leaves you bound to
39
39
  # the world of the predefined LDAP API. While this API is important for many
40
40
  # reasons, having to extract code out of LDAP search blocks and create huge
41
41
  # arrays of LDAP.mod entries make code harder to read, less intuitive, and just
42
- # less fun to write. Hopefully, Ruby/ActiveLDAP will remedy all of these
42
+ # less fun to write. Hopefully, Ruby/ActiveLdap will remedy all of these
43
43
  # problems!
44
44
  #
45
45
  # == Getting Started
46
46
  #
47
- # Ruby/ActiveLDAP does have some overhead when you get started. You must not
47
+ # Ruby/ActiveLdap does have some overhead when you get started. You must not
48
48
  # only install the package and all of it's requirements, but you must also make
49
49
  # customizations that will let it work in your environment.
50
50
  #
@@ -63,12 +63,12 @@
63
63
  # Assuming all the requirements are installed, you can install by grabbing the latest tgz file from
64
64
  # the download site[http://projects.dataspill.org/libraries/ruby/activeldap/download.html].
65
65
  #
66
- # The following steps will get the Ruby/ActiveLDAP installed in no time!
66
+ # The following steps will get the Ruby/ActiveLdap installed in no time!
67
67
  #
68
68
  # $ tar -xzvf ruby-activeldap-current.tgz
69
69
  # $ cd ruby-activeldap-VERSION
70
70
  #
71
- # Edit lib/activeldap/configuration.rb replacing values to match what will work
71
+ # Edit lib/active_ldap/configuration.rb replacing values to match what will work
72
72
  # with your LDAP servers. Please note that those variables are required, but can
73
73
  # be overridden in any program as detailed later in this document. Also make
74
74
  # sure that "ROOT" stays all upcase.
@@ -82,7 +82,7 @@
82
82
  # Now as a quick test, you can run:
83
83
  #
84
84
  # $ irb
85
- # irb> require 'activeldap'
85
+ # irb> require 'active_ldap'
86
86
  # => true
87
87
  # irb> exit
88
88
  #
@@ -93,7 +93,7 @@
93
93
  #
94
94
  # === Customizations
95
95
  #
96
- # Now that Ruby/ActiveLDAP is installed and working, we still have a few more
96
+ # Now that Ruby/ActiveLdap is installed and working, we still have a few more
97
97
  # steps to make it useful for programming.
98
98
  #
99
99
  # Let's say that you are writing a Ruby program for managing user and group
@@ -119,46 +119,46 @@
119
119
  #
120
120
  # == Usage
121
121
  #
122
- # This section covers using Ruby/ActiveLDAP from writing extension classes to
122
+ # This section covers using Ruby/ActiveLdap from writing extension classes to
123
123
  # writing applications that use them.
124
124
  #
125
125
  # Just to give a taste of what's to come, here is a quick example using irb:
126
126
  #
127
- # irb> require 'activeldap'
127
+ # irb> require 'active_ldap'
128
128
  #
129
129
  # Here's an extension class that maps to the LDAP Group objects:
130
130
  #
131
- # irb> class Group < ActiveLDAP::Base
131
+ # irb> class Group < ActiveLdap::Base
132
132
  # irb* ldap_mapping
133
133
  # irb* end
134
134
  #
135
135
  # Here is the Group class in use:
136
136
  #
137
- # irb> all_groups = Group.find_all('*')
137
+ # irb> all_groups = Group.find(:all, '*').collect {|group| group.cn}
138
138
  # => ["root", "daemon", "bin", "sys", "adm", "tty", ..., "develop"]
139
139
  #
140
- # irb> group = Group.new("develop")
140
+ # irb> group = Group.find("develop")
141
141
  # => #<Group:0x..........>
142
142
  #
143
- # irb> group.members
143
+ # irb> group.members.collect {|member| member.uid}
144
144
  # => ["drewry"]
145
145
  #
146
146
  # irb> group.cn
147
147
  # => "develop"
148
148
  #
149
- # irb> group.gidNumber
149
+ # irb> group.gid_number
150
150
  # => "1003"
151
151
  #
152
152
  # That's it! No let's get back in to it.
153
153
  #
154
154
  # === Extension Classes
155
155
  #
156
- # Extension classes are classes that are subclassed from ActiveLDAP::Base. They
156
+ # Extension classes are classes that are subclassed from ActiveLdap::Base. They
157
157
  # are used to represent objects in your LDAP server abstractly.
158
158
  #
159
159
  # ==== Why do I need them?
160
160
  #
161
- # Extension classes are what make Ruby/ActiveLDAP "active"! They do all the
161
+ # Extension classes are what make Ruby/ActiveLdap "active"! They do all the
162
162
  # background work to make easy-to-use objects by mapping the LDAP object's
163
163
  # attributes on to a Ruby class.
164
164
  #
@@ -172,12 +172,13 @@
172
172
  # ===== ldap_mapping
173
173
  #
174
174
  # ldap_mapping is the only required method to setup an extension class for use
175
- # with Ruby/ActiveLDAP. It must be called inside of a subclass as shown above.
175
+ # with Ruby/ActiveLdap. It must be called inside of a subclass as shown above.
176
176
  #
177
177
  # Below is a much more realistic Group class:
178
178
  #
179
- # class Group < ActiveLDAP::Base
180
- # ldap_mapping :dnattr => 'cn', :prefix => 'ou=Groups', :classes => ['top', 'posixGroup']<
179
+ # class Group < ActiveLdap::Base
180
+ # ldap_mapping :dn_attribute => 'cn',
181
+ # :prefix => 'ou=Groups', :classes => ['top', 'posixGroup']
181
182
  # :scope => LDAP::LDAP_SCOPE_ONELEVEL
182
183
  # end
183
184
  #
@@ -202,35 +203,33 @@
202
203
  #
203
204
  # cn=develop,ou=Groups,dc=dataspill,dc=org
204
205
  # ^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
205
- # :dnattr | |
206
+ # :dn_attribute | |
206
207
  # :prefix |
207
208
  # :base from configuration.rb
208
209
  #
209
- # :scope tells ActiveLDAP to only search under ou=Groups, and not to look deeper
210
+ # :scope tells ActiveLdap to only search under ou=Groups, and not to look deeper
210
211
  # for dnattr matches. (e.g. cn=develop,ou=DevGroups,ou=Groups,dc=dataspill,dc=org)
211
212
  #
212
- # Something's missing: :classes. :classes is used to tell Ruby/ActiveLDAP what
213
+ # Something's missing: :classes. :classes is used to tell Ruby/ActiveLdap what
213
214
  # the minimum requirement is when creating a new object. LDAP uses objectClasses
214
- # to define what attributes a LDAP object may have. Ruby/ActiveLDAP needs to know
215
+ # to define what attributes a LDAP object may have. Ruby/ActiveLdap needs to know
215
216
  # what classes are required when creating a new object. Of course, you can leave
216
217
  # that field out to default to ['top'] only. Then you can let each application
217
218
  # choose what objectClasses their objects should have by calling the method e.g.
218
- # Group#objectClass=(value) or by modifying the value returned by the accessor,
219
- # e.g. Group#objectClass.
219
+ # Group#add_class(*values).
220
220
  #
221
221
  # Note that is can be very important to define the default :classes value. Due to
222
222
  # implementation choices with most LDAP servers, once an object is created, its
223
223
  # structural objectclasses may not be removed (or replaced). Setting a sane default
224
224
  # may help avoid programmer error later.
225
225
  #
226
- # :classes isn't the only optional argument. If :dnattr is left off, it defaults
227
- # to 'cn'. If :prefix is left off, it will default to 'ou=CLASSNAME'. In this
228
- # case, it would be 'ou=Group'. There is also a :parent_class option which, when
229
- # specified, adds a method call parent() which will return the
230
- # parent_class.new(parent_dn). The parent_dn is the objects dn without the dnattr
231
- # pair.
226
+ # :classes isn't the only optional argument. If :dn_attribute is left off,
227
+ # it defaults to underscored class name or 'cn'. If :prefix is left off,
228
+ # it will default to 'ou=PLURALIZED_CLASSNAME'. In this
229
+ # case, it would be 'ou=Groups'.
232
230
  #
233
- # :classes should be an Array. :dnattr should be a String and so should :prefix.
231
+ # :classes should be an Array. :dn_attribute should be a String and so should
232
+ # :prefix.
234
233
  #
235
234
  #
236
235
  # ===== belongs_to
@@ -249,7 +248,7 @@
249
248
  # In the above tree, one such example would be user 'drewry' who is a part of the
250
249
  # group 'develop'. You can see this by looking at the 'memberUid' field of 'develop'.
251
250
  #
252
- # irb> develop = Group.new('develop')
251
+ # irb> develop = Group.find('develop')
253
252
  # => ...
254
253
  # irb> develop.memberUid
255
254
  # => ['drewry', 'builder']
@@ -257,35 +256,28 @@
257
256
  # If we look at the LDAP entry for 'drewry', we do not see any references to
258
257
  # group 'develop'. In order to remedy that, we can use belongs_to
259
258
  #
260
- # irb> class User < ActiveLDAP::Base
261
- # irb* ldap_mapping :dnattr => 'uid', :prefix => 'People', :classes => ['top','account']
262
- # irb* belongs_to :groups, :class_name => 'Group', :foreign_key => 'memberUid', :local_key => 'uid'
259
+ # irb> class User < ActiveLdap::Base
260
+ # irb* ldap_mapping :dn_attribute => 'uid', :prefix => 'People', :classes => ['top','account']
261
+ # irb* belongs_to :groups, :class => 'Group', :many => 'memberUid', :foreign_key => 'uid'
263
262
  # irb* end
264
263
  #
265
264
  # Now, class User will have a method called 'groups' which will retrieve all
266
265
  # Group objects that a user is in.
267
266
  #
268
- # irb> me = User.new('drewry')
267
+ # irb> me = User.find('drewry')
269
268
  # irb> me.groups
270
269
  # => [#<Group:0x000001 ...>, #<Group:0x000002 ...>, ...]
271
- # irb> me.groups(true).each { |group| p group.cn };nil
270
+ # irb> me.groups.each { |group| p group.cn };nil
272
271
  # "cdrom"
273
272
  # "audio"
274
273
  # "develop"
275
274
  # => nil
276
275
  # (Note: nil is just there to make the output cleaner...)
277
276
  #
278
- # Methods created with belongs_to also take an optional argument: objects. This
279
- # argument specifies whether it will return the value of the 'dnattr' of the
280
- # objects, or whether it will return Group objects.
281
- #
282
- # irb> me.groups(false)
283
- # => ["cdrom", "audio", "develop"]
284
- #
285
277
  # TIP: If you weren't sure what the distinguished name attribute was for Group,
286
278
  # you could also do the following:
287
279
  #
288
- # irb> me.groups.each { |group| p group.dnattr };nil
280
+ # irb> me.groups.each { |group| p group.id };nil
289
281
  # "cdrom"
290
282
  # "audio"
291
283
  # "develop"
@@ -294,18 +286,18 @@
294
286
  # Now let's talk about the arguments. The first argument is the name of the
295
287
  # method you wish to create. In this case, we created a method called groups
296
288
  # using the symbol :groups. The next collection of arguments are actually a Hash
297
- # (as with ldap_mapping). :class_name should be a string that has the name of a
289
+ # (as with ldap_mapping). :class should be a string that has the name of a
298
290
  # class you've already included. If you class is inside of a module, be sure to
299
- # put the whole name, e.g. :class_name => "MyLdapModule::Group". :foreign_key
300
- # tells belongs_to what attribute Group objects have that match the :local_key.
301
- # :local_key is the name of the local attribute whose value should be looked up
302
- # in Group under the foreign key. If :local_key is left off of the argument list,
303
- # it is assumed to be the dnattr. With this in mind, the above definition could
304
- # become:
305
- #
306
- # irb> class User < ActiveLDAP::Base
291
+ # put the whole name, e.g. :class => "MyLdapModule::Group". :primary_key
292
+ # tells belongs_to what attribute Group objects have that match the
293
+ # :many. :many is the name of the local attribute whose value
294
+ # should be looked up in Group under the primary key. If :foreign_key is left
295
+ # off of the argument list, it is assumed to be the dn_attribute. With this in
296
+ # mind, the above definition could become:
297
+ #
298
+ # irb> class User < ActiveLdap::Base
307
299
  # irb* ldap_mapping :dnattr => 'uid', :prefix => 'People', :classes => ['top','account']
308
- # irb* belongs_to :groups, :class_name => 'Group', :foreign_key => 'memberUid'
300
+ # irb* belongs_to :groups, :class => 'Group', :many => 'memberUid'
309
301
  # irb* end
310
302
  #
311
303
  # In addition, you can do simple membership tests by doing the following:
@@ -322,26 +314,24 @@
322
314
  # objects from other trees listed in your object. To show this, we can just
323
315
  # invert the example from above:
324
316
  #
325
- # class Group < ActiveLDAP::Base
326
- # ldap_mapping :dnattr => 'cn', :prefix => 'ou=Groups', :classes => ['top', 'posixGroup']
327
- # has_many :members, :class_name => "User", :local_key => "memberUid", :foreign_key => 'uid'
317
+ # class Group < ActiveLdap::Base
318
+ # ldap_mapping :dn_attribute => 'cn', :prefix => 'ou=Groups', :classes => ['top', 'posixGroup']
319
+ # has_many :members, :class => "User", :wrap => "memberUid", :primary_key => 'uid'
328
320
  # end
329
321
  #
330
322
  # Now we can see that group develop has user 'drewry' as a member, and it can
331
323
  # even return all responses in object form just like belongs_to methods.
332
324
  #
333
- # irb> develop = Group.new('develop')
325
+ # irb> develop = Group.find('develop')
334
326
  # => ...
335
327
  # irb> develop.members
336
328
  # => [#<User:0x000001 ...>, #<User:...>]
337
- # irb> develop.members(false)
338
- # => ["drewry", "builder"]
339
329
  #
340
330
  #
341
331
  # The arguments for has_many follow the exact same idea that belongs_to's
342
- # arguments followed. :local_key's contents are used to search for matching
343
- # :foreign_key content. If :foreign_key is not specified, it defaults to the
344
- # dnattr of the specified :class_name.
332
+ # arguments followed. :wrap's contents are used to search for matching
333
+ # :primary_key content. If :primary_key is not specified, it defaults to the
334
+ # dn_attribute of the specified :class.
345
335
  #
346
336
  # === Using these new classes
347
337
  #
@@ -351,37 +341,35 @@
351
341
  # methods that do not fall in to these categories.
352
342
  #
353
343
  #
354
- # ==== .find and .find_all
344
+ # ==== .find
355
345
  #
356
346
  # .find is a class method that is accessible from any subclass of Base that has
357
347
  # 'ldap_mapping' called. When called it returns the first match of the given
358
348
  # class.
359
349
  #
360
- # irb> Group.find('*')
350
+ # irb> Group.find('*').cn
361
351
  # => "root"
362
352
  #
363
353
  # In this simple example, Group.find took the search string of 'deve*' and
364
354
  # searched for the first match in Group where the dnattr matched the query. This
365
355
  # is the simplest example of .find.
366
356
  #
367
- # irb> Group.find_all('*')
357
+ # irb> Group.find(:all, '*').collect {|group| group.cn}
368
358
  # => ["root", "daemon", "bin", "sys", "adm", "tty", ..., "develop"]
369
359
  #
370
- # Here .find_all returns all matches to the same query. Both .find and .find_all
371
- # also can take more expressive arguments:
360
+ # Here .find(:all) returns all matches to the same query. Both .find and
361
+ # .find(:all) also can take more expressive arguments:
372
362
  #
373
- # irb> Group.find_all(:attribute => 'gidNumber', :value => '1003', :objects => false)
363
+ # irb> Group.find(:all, :attribute => 'gidNumber', :value => '1003').collect {|group| group.cn}
374
364
  # => ["develop"]
375
365
  #
376
366
  # So it is pretty clear what :attribute and :value do - they are used to query as
377
- # :attribute=:value. :objects is used to return precreated objects of the given
378
- # Class when it is set to true.
379
- #
380
- # irb> Group.find_all(:attribute => 'gidNumber', :value => '1003', :objects => false)
381
- # => [#<Group:0x40674a70 ..>]
367
+ # :attribute=:value.
382
368
  #
383
- # If :objects is unspecified, it defaults to false. If :attribute is unspecified,
384
- # it defaults to the dnattr.
369
+ # If :attribute is unspecified, it defaults to the dn_attribute.
370
+ #
371
+ # It is also possible to override :attribute and :value by specifying :filter. This
372
+ # argument allows the direct specification of a LDAP filter to retrieve objects by.
385
373
  #
386
374
  # ==== .search
387
375
  # .search is a class method that is accessible from any subclass of Base, and Base.
@@ -390,63 +378,59 @@
390
378
  # to cover 80% of the cases where a user would want to use Base.connection directly.
391
379
  #
392
380
  # irb> Base.search(:base => 'dc=example,dc=com', :filter => '(uid=roo*)',
393
- # :scope => LDAP::LDAP_SCOPE_SUBTREE, :attrs => ['uid', 'cn'])
394
- # => [{"dn"=>"uid=root,ou=People,dc=dataspill,dc=org","cn"=>["root"], "uidNumber"=>["0"]}]
395
- # You can specify the :filter, :base, :scope, and :attrs, but they all have defaults --
381
+ # :scope => :sub, :attributes => ['uid', 'cn'])
382
+ # => [["uid=root,ou=People,dc=dataspill,dc=org",{"cn"=>["root"], "uidNumber"=>["0"]}]
383
+ # You can specify the :filter, :base, :scope, and :attributes, but they all have defaults --
396
384
  # * :filter defaults to objectClass=* - usually this isn't what you want
397
385
  # * :base defaults to the base of the class this is executed from (as set in ldap_mapping)
398
- # * :scope defaults to LDAP::LDAP_SCOPE_SUBTREE. Usually you won't need to change it
399
- # * :attrs defaults to [] and is the list of attrs you want back. Empty means all of them.
386
+ # * :scope defaults to :sub. Usually you won't need to change it
387
+ # * :attributes defaults to [] and is the list of attributes you want back. Empty means all of them.
400
388
  #
401
- # ==== #validate
389
+ # ==== #valid?
402
390
  #
403
- # validate is a method that verifies that all attributes that are required by the
404
- # objects current objectClasses are populated. This also will call the
405
- # private "#enforce_types" method. This will make sure that all values defined are
406
- # valid to be written to LDAP. #validate is called by #write prior to
407
- # performing any action. Its explicit use in an application is unnecessary, and
408
- # it may become a private method in the future.
391
+ # valid? is a method that verifies that all attributes that are required by the
392
+ # objects current objectClasses are populated.
409
393
  #
410
- # ==== #write
394
+ # ==== #save
411
395
  #
412
- # write is a method that writes any changes to an object back to the LDAP server.
396
+ # save is a method that writes any changes to an object back to the LDAP server.
413
397
  # It automatically handles the addition of new objects, and the modification of
414
398
  # existing ones.
415
399
  #
416
- # ==== #exists?
400
+ # ==== .exists?
417
401
  #
418
402
  # exists? is a simple method which returns true is the current object exists in
419
403
  # LDAP, or false if it does not.
420
404
  #
421
- # irb> newuser = User.new("dshadsadsa")
422
- # => ...
423
- # irb> newuser.exists?
405
+ # irb> User.exists?("dshadsadsa")
424
406
  # => false
425
407
  #
426
408
  #
427
- # === ActiveLDAP::Base
409
+ # === ActiveLdap::Base
428
410
  #
429
- # ActiveLDAP::Base has come up a number of times in the examples above. Every
411
+ # ActiveLdap::Base has come up a number of times in the examples above. Every
430
412
  # time, it was being used as the super class for the wrapper objects. While this
431
413
  # is it's main purpose, it also handles quite a bit more in the background.
432
414
  #
433
415
  # ==== What is it?
434
416
  #
435
- # ActiveLDAP::Base is the heart of Ruby/ActiveLDAP. It does all the schema
417
+ # ActiveLdap::Base is the heart of Ruby/ActiveLdap. It does all the schema
436
418
  # parsing for validation and attribute-to-method mangling as well as manage the
437
419
  # connection to LDAP.
438
420
  #
439
- # ===== connect
421
+ # ===== establish_connection
440
422
  #
441
- # Base.connect takes many (optional) arguments and is used to connect to the LDAP
442
- # server. Sometimes you will want to connect anonymously and other times over TLS
443
- # with user credentials. Base.connect is here to do all of that for you.
423
+ # Base.establish_connection takes many (optional) arguments and is used to
424
+ # connect to the LDAP server. Sometimes you will want to connect anonymously
425
+ # and other times over TLS with user credentials. Base.connect is here to do
426
+ # all of that for you.
444
427
  #
445
428
  #
446
- # By default, if you call any subclass of Base, such as Group, it will call
447
- # Base.connect() if these is no active LDAP connection. If your server allows
448
- # anonymous binding, and you only want to access data in a read-only fashion, you
449
- # won't need to call Base.connect. Here is a fully parameterized call:
429
+ # By default, if you call any subclass of Base, such as Group, it will call
430
+ # Base.establish_connection() if these is no active LDAP connection. If your
431
+ # server allows anonymous binding, and you only want to access data in a
432
+ # read-only fashion, you won't need to call Base.establish_connection. Here
433
+ # is a fully parameterized call:
450
434
  #
451
435
  # Base.connect(
452
436
  # :host => 'ldap.dataspill.org',
@@ -490,18 +474,18 @@
490
474
  # whether the :password_block should be called on each reconnect.
491
475
  # * :allow_anonymous determines whether anonymous binding is allowed if other
492
476
  # bind methods fail
493
- # * :try_sasl, when true, tells ActiveLDAP to attempt a SASL-GSSAPI bind
477
+ # * :try_sasl, when true, tells ActiveLdap to attempt a SASL-GSSAPI bind
494
478
  # * :sasl_quiet, when true, tells the SASL libraries to not spew messages to STDOUT
495
479
  # * :method indicates whether to use :ssl, :tls, or :plain
496
480
  # * :retries - indicates the number of attempts to reconnect that will be undertaken when a stale connection occurs. -1 means infinite.
497
481
  # * :retry_wait - seconds to wait before retrying a connection
498
482
  # * :ldap_scope - dictates how to find objects. (Default: ONELEVEL)
499
- # * :return_objects - indicates whether find/find_all will return objects or just the distinguished name attribute value of the matches. Rails users will find this useful.
500
483
  # * :timeout - time in seconds - defaults to disabled. This CAN interrupt search() requests. Be warned.
501
484
  # * :retry_on_timeout - whether to reconnect when timeouts occur. Defaults to true
502
485
  # See lib/configuration.rb for defaults for each option
503
486
  #
504
- # Base.connect both connects and binds in one step. It follows roughly the following approach:
487
+ # Base.establish_connection both connects and binds in one step. It follows
488
+ # roughly the following approach:
505
489
  #
506
490
  # * Connect to host:port using :method
507
491
  #
@@ -511,41 +495,32 @@
511
495
  # * If that fails, error out.
512
496
  #
513
497
  # On connect, the configuration options passed in are stored in an internal class variable
514
- # @@config which is used to cache the information without ditching the defaults passed in
498
+ # @configuration which is used to cache the information without ditching the defaults passed in
515
499
  # from configuration.rb
516
500
  #
517
- # ===== close
518
- #
519
- # Base.close discards the current LDAP connection.
520
- #
521
501
  # ===== connection
522
502
  #
523
- # Base.connection returns the raw LDAP connection object.
503
+ # Base.connection returns the ActiveLdap::Connection object.
524
504
  #
525
505
  # === Exceptions
526
506
  #
527
- # There are a few custom exceptions used in Ruby/ActiveLDAP. They are detailed below.
528
- #
529
- # ==== AttributeEmpty
530
- #
531
- # This exception is raised when a required attribute is empty. It is only raised
532
- # by #validate, and by proxy, #write.
507
+ # There are a few custom exceptions used in Ruby/ActiveLdap. They are detailed below.
533
508
  #
534
509
  # ==== DeleteError
535
510
  #
536
511
  # This exception is raised when #delete fails. It will include LDAP error
537
512
  # information that was passed up during the error.
538
513
  #
539
- # ==== WriteError
514
+ # ==== SaveError
540
515
  #
541
- # This exception is raised when there is a problem in #write updating or creating
516
+ # This exception is raised when there is a problem in #save updating or creating
542
517
  # an LDAP entry. Often the error messages are cryptic. Looking at the server
543
518
  # logs or doing an Ethereal[http://www.ethereal.com] dump of the connection will
544
519
  # often provide better insight.
545
520
  #
546
521
  # ==== AuthenticationError
547
522
  #
548
- # This exception is raised during Base.connect if no valid authentication methods
523
+ # This exception is raised during Base.establish_connection if no valid authentication methods
549
524
  # succeeded.
550
525
  #
551
526
  # ==== ConnectionError
@@ -569,7 +544,7 @@
569
544
  #
570
545
  # === Putting it all together
571
546
  #
572
- # Now that all of the components of Ruby/ActiveLDAP have been covered, it's time
547
+ # Now that all of the components of Ruby/ActiveLdap have been covered, it's time
573
548
  # to put it all together! The rest of this section will show the steps to setup
574
549
  # example user and group management scripts for use with the LDAP tree described
575
550
  # above.
@@ -580,18 +555,18 @@
580
555
  #
581
556
  # In ldapadmin/lib/ create the file user.rb:
582
557
  # cat <<EOF
583
- # class User < ActiveLDAP::Base
584
- # ldap_mapping :dnattr => 'uid', :prefix => 'ou=People', :classes => ['top', 'account', 'posixAccount']
585
- # belongs_to :groups, :class_name => 'Group', :foreign_key => 'memberUid'
558
+ # class User < ActiveLdap::Base
559
+ # ldap_mapping :dn_attribute => 'uid', :prefix => 'ou=People', :classes => ['top', 'account', 'posixAccount']
560
+ # belongs_to :groups, :class => 'Group', :wrap => 'memberUid'
586
561
  # end
587
562
  # EOF
588
563
  #
589
564
  # In ldapadmin/lib/ create the file group.rb:
590
565
  # cat <<EOF
591
- # class Group < ActiveLDAP::Base
566
+ # class Group < ActiveLdap::Base
592
567
  # ldap_mapping :classes => ['top', 'posixGroup'], :prefix => 'ou=Group'
593
- # has_many :members, :class_name => "User", :local_key => "memberUid"
594
- # belongs_to :primary_members, :class_name => 'User', :foreign_key => 'gidNumber', :local_key => 'gidNumber'
568
+ # has_many :members, :class => "User", :many => "memberUid"
569
+ # has_many :primary_members, :class => 'User', :foreign_key => 'gidNumber', :primary_key => 'gidNumber'
595
570
  # end # Group
596
571
  # EOF
597
572
  #
@@ -603,57 +578,90 @@
603
578
  #
604
579
  # #!/usr/bin/ruby -W0
605
580
  #
606
- # require 'activeldap'
581
+ # require 'active_ldap'
607
582
  # require 'lib/user'
608
583
  # require 'lib/group'
609
584
  # require 'password'
610
585
  #
611
- # (printf($stderr, "Usage:\n%s name cn uid\n", $0); exit 1) if ARGV.size != 3
612
- #
613
- # puts "Adding user #{ARGV[0]}"
614
- # pwb = Proc.new {
615
- # Password.get('Password: ')
616
- # }
617
- # ActiveLDAP::Base.connect(:password_block => pwb, :allow_anonymous => false)
618
- # user = User.new(ARGV[0])
619
- # user.objectClass = user.objectClass << 'posixAccount' << 'shadowAccount'
620
- # user.cn = ARGV[1]
621
- # user.uidNumber = ARGV[2]
622
- # user.gidNumber = ARGV[2]
623
- # user.homeDirectory = "/home/#{ARGV[0]}"
624
- # user.write
625
- # puts "User created!"
626
- # exit 0
627
- #
628
- #
586
+ # argv, opts, options = ActiveLdap::Command.parse_options do |opts, options|
587
+ # opts.banner += " USER_NAME CN UID"
588
+ # end
589
+ #
590
+ # if argv.size == 3
591
+ # name, cn, uid = argv
592
+ # else
593
+ # $stderr.puts opts
594
+ # exit 1
595
+ # end
596
+ #
597
+ # pwb = Proc.new do |user|
598
+ # ActiveLdap::Command.read_password("[#{user}] Password: ")
599
+ # end
600
+ #
601
+ # ActiveLdap::Base.establish_connection(:password_block => pwb,
602
+ # :allow_anonymous => false)
603
+ #
604
+ # if User.exists?(name)
605
+ # $stderr.puts("User #{name} already exists.")
606
+ # exit 1
607
+ # end
608
+ #
609
+ # user = User.new(name)
610
+ # user.add_class('shadowAccount')
611
+ # user.cn = cn
612
+ # user.uid_number = uid
613
+ # user.gid_number = uid
614
+ # user.home_directory = "/home/#{name}"
615
+ # user.sn = "somesn"
616
+ # unless user.save
617
+ # puts "failed"
618
+ # puts user.errors.full_messages
619
+ # exit 1
620
+ # end
621
+ #
629
622
  # ==== Managing LDAP entries
630
623
  #
631
624
  # Now let's create another dumb script for modifying users - ldapadmin/usermod:
632
625
  #
633
626
  # #!/usr/bin/ruby -W0
634
627
  #
635
- # require 'activeldap'
628
+ # require 'active_ldap'
636
629
  # require 'lib/user'
637
630
  # require 'lib/group'
638
- # require 'password'
639
- #
640
- # (printf($stderr, "Usage:\n%s name cn uid\n", $0); exit 1) if ARGV.size != 3
641
- #
642
- # puts "Changing user #{ARGV[0]}"
643
- # pwb = Proc.new {
644
- # Password.get('Password: ')
645
- # }
646
- # ActiveLDAP::Base.connect(:password_block => pwb, :allow_anonymous => false)
647
- # user = User.new(ARGV[0])
648
- # user.cn = ARGV[1]
649
- # user.uidNumber = ARGV[2]
650
- # user.gidNumber = ARGV[2]
651
- # user.write
652
- # puts "Modification successful!"
653
- # exit 0
654
- #
655
- #
656
631
  #
632
+ # argv, opts, options = ActiveLdap::Command.parse_options do |opts, options|
633
+ # opts.banner += " USER_NAME CN UID"
634
+ # end
635
+ #
636
+ # if argv.size == 3
637
+ # name, cn, uid = argv
638
+ # else
639
+ # $stderr.puts opts
640
+ # exit 1
641
+ # end
642
+ #
643
+ # pwb = Proc.new do |user|
644
+ # ActiveLdap::Command.read_password("[#{user}] Password: ")
645
+ # end
646
+ #
647
+ # ActiveLdap::Base.establish_connection(:password_block => pwb,
648
+ # :allow_anonymous => false)
649
+ #
650
+ # unless User.exists?(name)
651
+ # $stderr.puts("User #{name} doesn't exist.")
652
+ # exit 1
653
+ # end
654
+ #
655
+ # user = User.find(name)
656
+ # user.cn = cn
657
+ # user.uid_number = uid
658
+ # user.gid_number = uid
659
+ # unless user.save
660
+ # puts "failed"
661
+ # puts user.errors.full_messages
662
+ # exit 1
663
+ # end
664
+ #
657
665
  # ==== Removing LDAP entries
658
666
  #
659
667
  # And finally, a dumb script for removing user - ldapadmin/userdel:
@@ -661,26 +669,38 @@
661
669
  #
662
670
  # #!/usr/bin/ruby -W0
663
671
  #
664
- # require 'activeldap'
672
+ # require 'active_ldap'
665
673
  # require 'lib/user'
666
674
  # require 'lib/group'
667
- # require 'password'
668
- #
669
- # (printf($stderr, "Usage:\n%s name\n", $0); exit 1) if ARGV.size != 1
670
- #
671
- # puts "Changing user #{ARGV[0]}"
672
- # pwb = Proc.new {
673
- # Password.get('Password: ')
674
- # }
675
- # ActiveLDAP::Base.connect(:password_block => pwb, :allow_anonymous => false)
676
- # user = User.new(ARGV[0])
677
- # user.delete
678
- # puts "User has been delete"
679
- # exit 0
680
675
  #
676
+ # argv, opts, options = ActiveLdap::Command.parse_options do |opts, options|
677
+ # opts.banner += " USER_NAME"
678
+ # end
679
+ #
680
+ # if argv.size == 1
681
+ # name = argv.shift
682
+ # else
683
+ # $stderr.puts opts
684
+ # exit 1
685
+ # end
686
+ #
687
+ # pwb = Proc.new do |user|
688
+ # ActiveLdap::Command.read_password("[#{user}] Password: ")
689
+ # end
690
+ #
691
+ # ActiveLdap::Base.establish_connection(:password_block => pwb,
692
+ # :allow_anonymous => false)
693
+ #
694
+ # unless User.exists?(name)
695
+ # $stderr.puts("User #{name} doesn't exist.")
696
+ # exit 1
697
+ # end
698
+ #
699
+ # User.destroy(name)
700
+ #
681
701
  # === Advanced Topics
682
702
  #
683
- # Below are some situation tips and tricks to get the most out of Ruby/ActiveLDAP.
703
+ # Below are some situation tips and tricks to get the most out of Ruby/ActiveLdap.
684
704
  #
685
705
  #
686
706
  # ==== Binary data and other subtypes
@@ -697,9 +717,9 @@
697
717
  # irb> user.cn
698
718
  # => ["wad", {"lang-en-us" => ["wad", "Will Drewry"]}]
699
719
  # # Now let's add a binary X.509 certificate (assume objectClass is correct)
700
- # irb> user.userCertificate = File.read('example.der')
720
+ # irb> user.user_certificate = File.read('example.der')
701
721
  # => ...
702
- # irb> user.write
722
+ # irb> user.save
703
723
  #
704
724
  # So that's a lot to take in. Here's what is going on. I just set the LDAP
705
725
  # object's cn to "wad" and cn:lang-en-us to ["wad", "Will Drewry"].
@@ -711,11 +731,11 @@
711
731
  # from breaking, and my code from crying. For correctness, I could have easily
712
732
  # done the following:
713
733
  #
714
- # irb> user.userCertificate = {'binary' => File.read('example.der')}
734
+ # irb> user.user_certificate = {'binary' => File.read('example.der')}
715
735
  #
716
736
  # You should note that some binary data does not use the binary subtype all the time.
717
737
  # One example is jpegPhoto. You can use it as jpegPhoto;binary or just as jpegPhoto.
718
- # Since the schema dictates that it is a binary value, Ruby/ActiveLDAP will write
738
+ # Since the schema dictates that it is a binary value, Ruby/ActiveLdap will write
719
739
  # it as binary, but the subtype will not be automatically appended as above. The
720
740
  # use of the subtype on attributes like jpegPhoto is ultimately decided by the
721
741
  # LDAP site policy and not by any programmatic means.
@@ -742,7 +762,7 @@
742
762
  # Example:
743
763
  #
744
764
  # ./myldap.rb:
745
- # require 'activeldap'
765
+ # require 'active_ldap'
746
766
  # require 'myldap/user'
747
767
  # require 'myldap/group'
748
768
  # module MyLDAP
@@ -750,18 +770,18 @@
750
770
  #
751
771
  # ./myldap/user.rb:
752
772
  # module MyLDAP
753
- # class User < ActiveLDAP::Base
754
- # ldap_mapping :dnattr => 'uid', :prefix => 'ou=People', :classes => ['top', 'account', 'posixAccount']
755
- # belongs_to :groups, :class_name => 'MyLDAP::Group', :foreign_key => 'memberUid'
773
+ # class User < ActiveLdap::Base
774
+ # ldap_mapping :dn_attribute => 'uid', :prefix => 'ou=People', :classes => ['top', 'account', 'posixAccount']
775
+ # belongs_to :groups, :class => 'MyLDAP::Group', :many => 'memberUid'
756
776
  # end
757
777
  # end
758
778
  #
759
779
  # ./myldap/group.rb:
760
780
  # module MyLDAP
761
- # class Group < ActiveLDAP::Base
781
+ # class Group < ActiveLdap::Base
762
782
  # ldap_mapping :classes => ['top', 'posixGroup'], :prefix => 'ou=Group'
763
- # has_many :members, :class_name => 'MyLDAP::User', :local_key => 'memberUid'
764
- # belongs_to :primary_members, :class_name => 'MyLDAP::User', :foreign_key => 'gidNumber', :local_key => 'gidNumber'
783
+ # has_many :members, :class => 'MyLDAP::User', :wrap => 'memberUid'
784
+ # has_many :primary_members, :class => 'MyLDAP::User', :foreign_key => 'gidNumber', :primary_key => 'gidNumber'
765
785
  # end
766
786
  # end
767
787
  #
@@ -775,68 +795,43 @@
775
795
  # and everything should work well.
776
796
  #
777
797
  #
778
- # ==== Non-array results for single values
798
+ # ==== force array results for single values
779
799
  #
780
- # Even though Ruby/ActiveLDAP attempts to maintain programmatic ease by
781
- # returning Array values only. By specifying 'false' as an argument to
782
- # any attribute method you will get back a String if it is single value.
783
- # This is useful when you are just dumping values for human reading.
800
+ # Even though Ruby/ActiveLdap attempts to maintain programmatic ease by
801
+ # returning Array values only. By specifying 'true' as an argument to
802
+ # any attribute method you will get back a Array if it is single value.
784
803
  # Here's an example:
785
804
  #
786
805
  # irb> user = User.new('drewry')
787
806
  # => ...
788
- # irb> user.cn(false)
789
- # => "Will Drewry"
807
+ # irb> user.cn(true)
808
+ # => ["Will Drewry"]
790
809
  #
791
- # That's it. Now you can make human-readable output faster.
792
- #
793
810
  # ==== Dynamic attribute crawling
794
811
  #
795
812
  # If you use tab completion in irb, you'll notice that you /can/ tab complete the dynamic
796
813
  # attribute methods. You can still see which methods are for attributes using
797
- # Base#attributes:
814
+ # Base#attribute_names:
798
815
  #
799
816
  # irb> d = Group.new('develop')
800
817
  # => ...
801
- # irb> d.attributes
818
+ # irb> d.attribute_names
802
819
  # => ["gidNumber", "cn", "memberUid", "commonName", "description", "userPassword", "objectClass"]
803
820
  #
804
821
  #
805
- # ==== Advanced LDAP queries
806
- #
807
- # With the addition of Base.search, you can do arbitrary queries against LDAP
808
- # without needed to understand how to use Ruby/LDAP.
809
- #
810
- # If that's still not enough, you can access the
811
- # Ruby/LDAP connection object using the class method Base.connection. You can
812
- # do all of your LDAP specific calls here and then continue about your normal
813
- # Ruby/ActiveLDAP business afterward.
814
- #
815
- #
816
- # ==== Reusing LDAP::Entry objects without reusing the LDAP connection
817
- #
818
- # You can call Klass.new(entry) where Klass is some subclass of Base and
819
- # enty is an LDAP::entry. This use of 'new' is is meant for use from
820
- # within find_all and find, but you can also use it in tandem with advanced
821
- # LDAP queries.
822
- #
823
- # See tests/benchmark for more insight.
824
- #
825
822
  # ==== Juggling multiple LDAP connections
826
823
  #
827
824
  # In the same vein as the last tip, you can use multiple LDAP connections by
828
- # saving old connections and then resetting them as follows:
825
+ # per class as follows:
829
826
  #
830
- # irb> Base.connect()
827
+ # irb> anon_class = Class.new(Base)
831
828
  # => ...
832
- # irb> anon_conn = Base.connection
829
+ # irb> anon_class.establish_connection
833
830
  # => ...
834
- # irb> Base.connect(password_block => {'mypass'})
831
+ # irb> auth_class = Class.new(Base)
835
832
  # => ...
836
- # irb> auth_conn = Base.connection
833
+ # irb> auth_class.establish_connection(password_block => {'mypass'})
837
834
  # => ...
838
- # irb> Base.connection = anon_conn
839
- # ...
840
835
  #
841
836
  # This can be useful for doing authentication tests and other such tricks.
842
837
  #
@@ -855,7 +850,7 @@
855
850
  # experiment. That's exactly how I ended up with this package. If you come up
856
851
  # with something cool, please share it!
857
852
  #
858
- # The internal structure of ActiveLDAP::Base, and thus all its subclasses, is
853
+ # The internal structure of ActiveLdap::Base, and thus all its subclasses, is
859
854
  # still in flux. I've tried to minimize the changes to the overall API, but
860
855
  # the internals are still rough around the edges.
861
856
  #
@@ -865,37 +860,37 @@
865
860
  # from Base:
866
861
  # * Base.base()
867
862
  # * Base.required_classes()
868
- # * Base.dnattr()
863
+ # * Base.dn_attribute()
869
864
  # You can access these from custom class methods by calling MyClass.base(),
870
865
  # or whatever. There are predefined instance methods for getting to these
871
866
  # from any new instance methods you define:
872
867
  # * Base#base()
873
868
  # * Base#required_classes()
874
- # * Base#dnattr()
869
+ # * Base#dn_attribute()
875
870
  #
876
871
  # ===== What else?
877
872
  #
878
873
  # Well if you want to use the LDAP connection for anything, I'd suggest still
879
874
  # calling Base.connection to get it. There really aren't many other internals
880
- # that need to be worried about. You could rebind with Base.do_bind, and get
881
- # the LDAP schema with Base.schema.
875
+ # that need to be worried about. You could get the LDAP schema with
876
+ # Base.schema.
882
877
  #
883
878
  # The only other useful tricks are dereferencing and accessing the stored
884
879
  # data. Since LDAP attributes can have multiple names, e.g. cn or commonName,
885
880
  # any methods you write might need to figure it out. I'd suggest just
886
- # calling self.[attribname] to get the value, but if that's not good enough,
887
- # you can call look up the stored name in the @attr_method Array as follows:
888
- # irb> @attr_method['commonName']
881
+ # calling self[attribname] to get the value, but if that's not good enough,
882
+ # you can call look up the stored name by #to_real_attribute_name as follows:
883
+ # irb> to_real_attribute_name('commonName')
889
884
  # => 'cn'
890
885
  #
891
886
  # This tells you the name the attribute is stored in behind the scenes (@data).
892
- # Again, self.[attribname] should be enough for most extensions, but if not,
887
+ # Again, self[attribname] should be enough for most extensions, but if not,
893
888
  # it's probably safe to dabble here.
894
889
  #
895
890
  # Also, if you like to look up all aliases for an attribute, you can call the
896
891
  # following:
897
892
  #
898
- # irb> attribute_aliases('cn')
893
+ # irb> schema.attribute_aliases('cn')
899
894
  # => ['cn','commonName']
900
895
  #
901
896
  # This is discovered automagically from the LDAP server's schema.
@@ -904,7 +899,7 @@
904
899
  #
905
900
  # === Speed
906
901
  #
907
- # Currently, Ruby/ActiveLDAP could be faster. I have some recursive type
902
+ # Currently, Ruby/ActiveLdap could be faster. I have some recursive type
908
903
  # checking going on which slows object creation down, and I'm sure there
909
904
  # are many, many other places optimizations can be done. Feel free
910
905
  # to send patches, or just hang in there until I can optimize away the
@@ -919,25 +914,47 @@
919
914
  # Blanket warning hiding. Remove for debugging
920
915
  $VERBOSE, verbose = false, $VERBOSE
921
916
 
922
- require 'activeldap/ldap'
923
- require 'activeldap/schema2'
924
917
  if RUBY_PLATFORM.match('linux')
925
- require 'activeldap/timeout'
918
+ require 'active_ldap/timeout'
926
919
  else
927
- require 'activeldap/timeout_stub'
920
+ require 'active_ldap/timeout_stub'
921
+ end
922
+
923
+ require_gem_if_need = Proc.new do |library_name, gem_name|
924
+ begin
925
+ require library_name
926
+ rescue LoadError
927
+ require 'rubygems'
928
+ require_gem gem_name
929
+ require library_name
930
+ end
928
931
  end
929
- require 'activeldap/base'
930
- require 'activeldap/associations'
931
- require 'activeldap/configuration'
932
932
 
933
+ require_gem_if_need.call("active_support", "activesupport")
934
+ require 'active_ldap/base'
935
+ require 'active_ldap/associations'
936
+ require 'active_ldap/configuration'
937
+ require 'active_ldap/connection'
938
+ require 'active_ldap/attributes'
939
+ require 'active_ldap/object_class'
940
+ require 'active_ldap/adaptor/ldap'
941
+
942
+ require_gem_if_need.call("active_record/base", "activerecord")
943
+ require 'active_ldap/validations'
944
+ require 'active_ldap/callbacks'
933
945
 
934
- module ActiveLDAP
935
- VERSION = "0.7.4"
946
+ module ActiveLdap
947
+ VERSION = "0.8.0"
936
948
  end
937
949
 
938
- ActiveLDAP::Base.class_eval do
939
- include ActiveLDAP::Configuration
940
- include ActiveLDAP::Associations
950
+ ActiveLdap::Base.class_eval do
951
+ include ActiveLdap::Configuration
952
+ include ActiveLdap::Connection
953
+ include ActiveLdap::Attributes
954
+ include ActiveLdap::ObjectClass
955
+ include ActiveLdap::Associations
956
+ include ActiveLdap::Validations
957
+ include ActiveLdap::Callbacks
941
958
  end
942
959
 
943
960
  $VERBOSE = verbose