ruby-activeldap 0.7.4 → 0.8.0

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