ruby-activeldap 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/test/test_adapter.rb +17 -0
  2. data/test/test_associations.rb +19 -0
  3. data/test/test_attributes.rb +2 -1
  4. data/test/test_base.rb +28 -1
  5. data/test/test_base_per_instance.rb +2 -1
  6. data/test/test_callback.rb +2 -2
  7. data/test/test_connection.rb +2 -1
  8. data/test/test_connection_per_dn.rb +81 -0
  9. data/test/test_dn.rb +3 -2
  10. data/test/test_find.rb +35 -1
  11. data/test/test_object_class.rb +12 -1
  12. data/test/test_reflection.rb +16 -10
  13. data/test/test_schema.rb +141 -2
  14. data/test/test_user.rb +14 -4
  15. metadata +7 -104
  16. data/CHANGES +0 -397
  17. data/COPYING +0 -340
  18. data/LICENSE +0 -58
  19. data/Manifest.txt +0 -99
  20. data/README +0 -85
  21. data/Rakefile +0 -70
  22. data/TODO +0 -23
  23. data/benchmark/bench-al.rb +0 -152
  24. data/examples/config.yaml.example +0 -5
  25. data/examples/example.der +0 -0
  26. data/examples/example.jpg +0 -0
  27. data/examples/groupadd +0 -41
  28. data/examples/groupdel +0 -35
  29. data/examples/groupls +0 -49
  30. data/examples/groupmod +0 -42
  31. data/examples/lpasswd +0 -55
  32. data/examples/objects/group.rb +0 -13
  33. data/examples/objects/ou.rb +0 -4
  34. data/examples/objects/user.rb +0 -20
  35. data/examples/ouadd +0 -38
  36. data/examples/useradd +0 -45
  37. data/examples/useradd-binary +0 -50
  38. data/examples/userdel +0 -34
  39. data/examples/userls +0 -50
  40. data/examples/usermod +0 -42
  41. data/examples/usermod-binary-add +0 -47
  42. data/examples/usermod-binary-add-time +0 -51
  43. data/examples/usermod-binary-del +0 -48
  44. data/examples/usermod-lang-add +0 -43
  45. data/lib/active_ldap.rb +0 -964
  46. data/lib/active_ldap/adapter/base.rb +0 -461
  47. data/lib/active_ldap/adapter/ldap.rb +0 -232
  48. data/lib/active_ldap/adapter/ldap_ext.rb +0 -69
  49. data/lib/active_ldap/adapter/net_ldap.rb +0 -288
  50. data/lib/active_ldap/adapter/net_ldap_ext.rb +0 -29
  51. data/lib/active_ldap/association/belongs_to.rb +0 -40
  52. data/lib/active_ldap/association/belongs_to_many.rb +0 -39
  53. data/lib/active_ldap/association/collection.rb +0 -80
  54. data/lib/active_ldap/association/has_many.rb +0 -40
  55. data/lib/active_ldap/association/has_many_wrap.rb +0 -55
  56. data/lib/active_ldap/association/proxy.rb +0 -89
  57. data/lib/active_ldap/associations.rb +0 -162
  58. data/lib/active_ldap/attributes.rb +0 -203
  59. data/lib/active_ldap/base.rb +0 -1510
  60. data/lib/active_ldap/callbacks.rb +0 -19
  61. data/lib/active_ldap/command.rb +0 -46
  62. data/lib/active_ldap/configuration.rb +0 -106
  63. data/lib/active_ldap/connection.rb +0 -142
  64. data/lib/active_ldap/distinguished_name.rb +0 -246
  65. data/lib/active_ldap/ldap_error.rb +0 -74
  66. data/lib/active_ldap/object_class.rb +0 -74
  67. data/lib/active_ldap/schema.rb +0 -299
  68. data/lib/active_ldap/timeout.rb +0 -75
  69. data/lib/active_ldap/timeout_stub.rb +0 -17
  70. data/lib/active_ldap/user_password.rb +0 -92
  71. data/lib/active_ldap/validations.rb +0 -76
  72. data/rails/plugin/active_ldap/README +0 -54
  73. data/rails/plugin/active_ldap/generators/scaffold_al/scaffold_al_generator.rb +0 -7
  74. data/rails/plugin/active_ldap/generators/scaffold_al/templates/ldap.yml +0 -21
  75. data/rails/plugin/active_ldap/init.rb +0 -12
  76. data/test/TODO +0 -2
  77. data/test/al-test-utils.rb +0 -381
  78. data/test/command.rb +0 -62
  79. data/test/config.yaml.sample +0 -6
  80. data/test/run-test.rb +0 -29
  81. data/test/test-unit-ext.rb +0 -2
  82. data/test/test-unit-ext/always-show-result.rb +0 -28
  83. data/test/test-unit-ext/priority.rb +0 -163
@@ -1,51 +0,0 @@
1
- #!/usr/bin/ruby -W0
2
-
3
- base = File.expand_path(File.join(File.dirname(__FILE__), ".."))
4
- $LOAD_PATH << File.join(base, "lib")
5
- $LOAD_PATH << File.join(base, "examples")
6
-
7
- require 'active_ldap'
8
- require 'objects/user'
9
- require 'objects/group'
10
-
11
- argv, opts, options = ActiveLdap::Command.parse_options do |opts, options|
12
- opts.banner += " USER_NAME CN UID"
13
- end
14
-
15
- if argv.size == 3
16
- name, cn, uid = argv
17
- else
18
- $stderr.puts opts
19
- exit 1
20
- end
21
-
22
- pwb = Proc.new do |user|
23
- ActiveLdap::Command.read_password("[#{user}] Password: ")
24
- end
25
-
26
- ActiveLdap::Base.establish_connection(:password_block => pwb,
27
- :allow_anonymous => false)
28
-
29
- unless User.exists?(name)
30
- $stderr.puts("User #{name} doesn't exist.")
31
- exit 1
32
- end
33
-
34
- 100.times do |i|
35
- user = User.find(name)
36
- user.cn = cn
37
- user.uid_number = uid
38
- user.gid_number = uid
39
-
40
- user.add_class('strongAuthenticationUser')
41
- cert_file = File.join(File.dirname(__FILE__), 'example.der')
42
- user.user_certificate = File.read(cert_file)
43
-
44
- unless user.save
45
- puts "failed #{i}"
46
- puts user.errors.full_messages
47
- exit 1
48
- end
49
-
50
- # puts "success [#{i}]"
51
- end
@@ -1,48 +0,0 @@
1
- #!/usr/bin/ruby -W0
2
-
3
- base = File.expand_path(File.join(File.dirname(__FILE__), ".."))
4
- $LOAD_PATH << File.join(base, "lib")
5
- $LOAD_PATH << File.join(base, "examples")
6
-
7
- require 'active_ldap'
8
- require 'objects/user'
9
- require 'objects/group'
10
-
11
- argv, opts, options = ActiveLdap::Command.parse_options do |opts, options|
12
- opts.banner += " USER_NAME CN UID"
13
- end
14
-
15
- if argv.size == 3
16
- name, cn, uid = argv
17
- else
18
- $stderr.puts opts
19
- exit 1
20
- end
21
-
22
- pwb = Proc.new do |user|
23
- ActiveLdap::Command.read_password("[#{user}] Password: ")
24
- end
25
-
26
- ActiveLdap::Base.establish_connection(:password_block => pwb,
27
- :allow_anonymous => false)
28
-
29
- unless User.exists?(name)
30
- $stderr.puts("User #{name} doesn't exist.")
31
- exit 1
32
- end
33
-
34
- user = User.find(name)
35
- user.cn = cn
36
- user.uid_number = uid
37
- user.gid_number = uid
38
-
39
- if user.classes.include?('strongAuthenticationUser')
40
- user.user_certificate = nil
41
- user.remove_class('strongAuthenticationUser')
42
- end
43
-
44
- unless user.save
45
- puts "failed"
46
- puts user.errors.full_messages
47
- exit 1
48
- end
@@ -1,43 +0,0 @@
1
- #!/usr/bin/ruby -W0
2
-
3
- base = File.expand_path(File.join(File.dirname(__FILE__), ".."))
4
- $LOAD_PATH << File.join(base, "lib")
5
- $LOAD_PATH << File.join(base, "examples")
6
-
7
- require 'active_ldap'
8
- require 'objects/user'
9
- require 'objects/group'
10
-
11
- argv, opts, options = ActiveLdap::Command.parse_options do |opts, options|
12
- opts.banner += " USER_NAME CN UID"
13
- end
14
-
15
- if argv.size == 3
16
- name, cn, uid = argv
17
- else
18
- $stderr.puts opts
19
- exit 1
20
- end
21
-
22
- pwb = Proc.new do |user|
23
- ActiveLdap::Command.read_password("[#{user}] Password: ")
24
- end
25
-
26
- ActiveLdap::Base.establish_connection(:password_block => pwb,
27
- :allow_anonymous => false)
28
-
29
- unless User.exists?(name)
30
- $stderr.puts("User #{name} doesn't exist.")
31
- exit 1
32
- end
33
-
34
- user = User.find(name)
35
- user.cn = [cn, {'lang-en-us' => cn}]
36
- user.uid_number = uid
37
- user.gid_number = uid
38
-
39
- unless user.save
40
- puts "failed"
41
- puts user.errors.full_messages
42
- exit 1
43
- end
data/lib/active_ldap.rb DELETED
@@ -1,964 +0,0 @@
1
- #!/usr/bin/ruby
2
- # = Ruby/ActiveLdap
3
- #
4
- # "Ruby/ActiveLdap" Copyright (C) 2004,2005 Will Drewry mailto:will@alum.bu.edu
5
- #
6
- # == Introduction
7
- #
8
- # Ruby/ActiveLdap is a novel way of interacting with LDAP. Most interaction with
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
12
- # object oriented interface to LDAP entries.
13
- #
14
- # The target audience is system administrators and LDAP users everywhere that
15
- # need quick, clean access to LDAP in Ruby.
16
- #
17
- # === What's LDAP?
18
- #
19
- # LDAP stands for "Lightweight Directory Access Protocol." Basically this means
20
- # that it is the protocol used for accessing LDAP servers. LDAP servers
21
- # lightweight directories. An LDAP server can contain anything from a simple
22
- # digital phonebook to user accounts for computer systems. More and more
23
- # frequently, it is being used for the latter. My examples in this text will
24
- # assume some familiarity with using LDAP as a centralized authentication and
25
- # authorization server for Unix systems. (Unfortunately, I've yet to try this
26
- # against Microsoft's ActiveDirectory, despite what the name implies.)
27
- #
28
- # Further reading:
29
- # * RFC1777[http://www.faqs.org/rfcs/rfc1777.html] - Lightweight Directory Access Protocol
30
- # * OpenLDAP[http://www.openldap.org]
31
- #
32
- # === So why use Ruby/ActiveLdap?
33
- #
34
- # Well if you like to fumble around in the dark, dank innards of LDAP, you can
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.
37
- #
38
- # Using LDAP directly (even with the excellent Ruby/LDAP), leaves you bound to
39
- # the world of the predefined LDAP API. While this API is important for many
40
- # reasons, having to extract code out of LDAP search blocks and create huge
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
43
- # problems!
44
- #
45
- # == Getting Started
46
- #
47
- # Ruby/ActiveLdap does have some overhead when you get started. You must not
48
- # only install the package and all of it's requirements, but you must also make
49
- # customizations that will let it work in your environment.
50
- #
51
- # === Requirements
52
- #
53
- # * Ruby[http://www.ruby-lang.org] 1.8.x
54
- # * Ruby/LDAP[http://ruby-ldap.sourceforge.net]
55
- # * Log4r[http://log4r.sourceforge.net]
56
- # * (Optional) Ruby/LDAP+GSSAPI[http://caliban.org/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm]
57
- # * An LDAP server compatible with Ruby/LDAP: OpenLDAP[http://www.openldap.org], etc
58
- # - Your LDAP server must allow root_dse queries to allow for schema queries
59
- # * Examples also require: Ruby/Password[http://raa.ruby-lang.org/project/ruby-password/]
60
- #
61
- # === Installation
62
- #
63
- # Assuming all the requirements are installed, you can install by grabbing the latest tgz file from
64
- # the download site[http://projects.dataspill.org/libraries/ruby/activeldap/download.html].
65
- #
66
- # The following steps will get the Ruby/ActiveLdap installed in no time!
67
- #
68
- # $ tar -xzvf ruby-activeldap-current.tgz
69
- # $ cd ruby-activeldap-VERSION
70
- #
71
- # Edit lib/active_ldap/configuration.rb replacing values to match what will work
72
- # with your LDAP servers. Please note that those variables are required, but can
73
- # be overridden in any program as detailed later in this document. Also make
74
- # sure that "ROOT" stays all upcase.
75
- #
76
- # Now run:
77
- #
78
- # $ ruby setup.rb config
79
- # $ ruby setup.rb setup
80
- # $ (as root) ruby setup.rb install
81
- #
82
- # Now as a quick test, you can run:
83
- #
84
- # $ irb
85
- # irb> require 'active_ldap'
86
- # => true
87
- # irb> exit
88
- #
89
- # If the require returns false or an exception is raised, there has been a
90
- # problem with the installation. You may need to customize what setup.rb does on
91
- # install.
92
- #
93
- #
94
- # === Customizations
95
- #
96
- # Now that Ruby/ActiveLdap is installed and working, we still have a few more
97
- # steps to make it useful for programming.
98
- #
99
- # Let's say that you are writing a Ruby program for managing user and group
100
- # accounts in LDAP. I will use this as the running example throughout the
101
- # document.
102
- #
103
- # You will want to make a directory called 'ldapadmin' wherever is convenient. Under this directory,
104
- # you'll want to make sure you have a 'lib' directory.
105
- #
106
- # $ cd ~
107
- # $ mkdir ldapadmin
108
- # $ cd ldapadmin
109
- # $ mkdir lib
110
- # $ cd lib
111
- #
112
- # The lib directory is where we'll be making customizations. You can, of course,
113
- # make this changes somewhere in Ruby's default search path to make this
114
- # accessible to every Ruby scripts. Enough of my babbling, I'm sure you'd like to
115
- # know what we're going to put in lib/.
116
- #
117
- # We're going to put extension classes in there. What are extension classes you say . . .
118
- #
119
- #
120
- # == Usage
121
- #
122
- # This section covers using Ruby/ActiveLdap from writing extension classes to
123
- # writing applications that use them.
124
- #
125
- # Just to give a taste of what's to come, here is a quick example using irb:
126
- #
127
- # irb> require 'active_ldap'
128
- #
129
- # Here's an extension class that maps to the LDAP Group objects:
130
- #
131
- # irb> class Group < ActiveLdap::Base
132
- # irb* ldap_mapping
133
- # irb* end
134
- #
135
- # Here is the Group class in use:
136
- #
137
- # irb> all_groups = Group.find(:all, '*').collect {|group| group.cn}
138
- # => ["root", "daemon", "bin", "sys", "adm", "tty", ..., "develop"]
139
- #
140
- # irb> group = Group.find("develop")
141
- # => #<Group:0x..........>
142
- #
143
- # irb> group.members.collect {|member| member.uid}
144
- # => ["drewry"]
145
- #
146
- # irb> group.cn
147
- # => "develop"
148
- #
149
- # irb> group.gid_number
150
- # => "1003"
151
- #
152
- # That's it! No let's get back in to it.
153
- #
154
- # === Extension Classes
155
- #
156
- # Extension classes are classes that are subclassed from ActiveLdap::Base. They
157
- # are used to represent objects in your LDAP server abstractly.
158
- #
159
- # ==== Why do I need them?
160
- #
161
- # Extension classes are what make Ruby/ActiveLdap "active"! They do all the
162
- # background work to make easy-to-use objects by mapping the LDAP object's
163
- # attributes on to a Ruby class.
164
- #
165
- #
166
- # ==== Special Methods
167
- #
168
- # I will briefly talk about each of the methods you can use when defining an
169
- # extension class. In the above example, I only made one special method call
170
- # inside the Group class. More than likely, you will want to more than that.
171
- #
172
- # ===== ldap_mapping
173
- #
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.
176
- #
177
- # Below is a much more realistic Group class:
178
- #
179
- # class Group < ActiveLdap::Base
180
- # ldap_mapping :dn_attribute => 'cn',
181
- # :prefix => 'ou=Groups', :classes => ['top', 'posixGroup']
182
- # :scope => LDAP::LDAP_SCOPE_ONELEVEL
183
- # end
184
- #
185
- # As you can see, this method is used for defining how this class maps in to LDAP. Let's say that
186
- # my LDAP tree looks something like this:
187
- #
188
- # * dc=dataspill,dc=org
189
- # |- ou=People,dc=dataspill,dc=org
190
- # |+ ou=Groups,dc=dataspill,dc=org
191
- # \
192
- # |- cn=develop,ou=Groups,dc=dataspill,dc=org
193
- # |- cn=root,ou=Groups,dc=dataspill,dc=org
194
- # |- ...
195
- #
196
- # Under ou=People I store user objects, and under ou=Groups, I store group
197
- # objects. What |ldap_mapping| has done is mapped the class in to the LDAP tree
198
- # abstractly. With the given :dnattr and :prefix, it will only work for entries
199
- # under ou=Groups,dc=dataspill,dc=org using the primary attribute 'cn' as the
200
- # beginning of the distinguished name.
201
- #
202
- # Just for clarity, here's how the arguments map out:
203
- #
204
- # cn=develop,ou=Groups,dc=dataspill,dc=org
205
- # ^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
206
- # :dn_attribute | |
207
- # :prefix |
208
- # :base from configuration.rb
209
- #
210
- # :scope tells ActiveLdap to only search under ou=Groups, and not to look deeper
211
- # for dnattr matches. (e.g. cn=develop,ou=DevGroups,ou=Groups,dc=dataspill,dc=org)
212
- #
213
- # Something's missing: :classes. :classes is used to tell Ruby/ActiveLdap what
214
- # the minimum requirement is when creating a new object. LDAP uses objectClasses
215
- # to define what attributes a LDAP object may have. Ruby/ActiveLdap needs to know
216
- # what classes are required when creating a new object. Of course, you can leave
217
- # that field out to default to ['top'] only. Then you can let each application
218
- # choose what objectClasses their objects should have by calling the method e.g.
219
- # Group#add_class(*values).
220
- #
221
- # Note that is can be very important to define the default :classes value. Due to
222
- # implementation choices with most LDAP servers, once an object is created, its
223
- # structural objectclasses may not be removed (or replaced). Setting a sane default
224
- # may help avoid programmer error later.
225
- #
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'.
230
- #
231
- # :classes should be an Array. :dn_attribute should be a String and so should
232
- # :prefix.
233
- #
234
- #
235
- # ===== belongs_to
236
- #
237
- # This method allows an extension class to make use of other extension classes
238
- # tying objects together across the LDAP tree. Often, user objects will be
239
- # members of, or belong_to, Group objects.
240
- #
241
- # * dc=dataspill,dc=org
242
- # |+ ou=People,dc=dataspill,dc=org
243
- # \
244
- # |- uid=drewry,ou=People,dc=dataspill,dc=org
245
- # |- ou=Groups,dc=dataspill,dc=org
246
- #
247
- #
248
- # In the above tree, one such example would be user 'drewry' who is a part of the
249
- # group 'develop'. You can see this by looking at the 'memberUid' field of 'develop'.
250
- #
251
- # irb> develop = Group.find('develop')
252
- # => ...
253
- # irb> develop.memberUid
254
- # => ['drewry', 'builder']
255
- #
256
- # If we look at the LDAP entry for 'drewry', we do not see any references to
257
- # group 'develop'. In order to remedy that, we can use belongs_to
258
- #
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'
262
- # irb* end
263
- #
264
- # Now, class User will have a method called 'groups' which will retrieve all
265
- # Group objects that a user is in.
266
- #
267
- # irb> me = User.find('drewry')
268
- # irb> me.groups
269
- # => [#<Group:0x000001 ...>, #<Group:0x000002 ...>, ...]
270
- # irb> me.groups.each { |group| p group.cn };nil
271
- # "cdrom"
272
- # "audio"
273
- # "develop"
274
- # => nil
275
- # (Note: nil is just there to make the output cleaner...)
276
- #
277
- # TIP: If you weren't sure what the distinguished name attribute was for Group,
278
- # you could also do the following:
279
- #
280
- # irb> me.groups.each { |group| p group.id };nil
281
- # "cdrom"
282
- # "audio"
283
- # "develop"
284
- # => nil
285
- #
286
- # Now let's talk about the arguments. The first argument is the name of the
287
- # method you wish to create. In this case, we created a method called groups
288
- # using the symbol :groups. The next collection of arguments are actually a Hash
289
- # (as with ldap_mapping). :class should be a string that has the name of a
290
- # class you've already included. If you class is inside of a module, be sure to
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
299
- # irb* ldap_mapping :dnattr => 'uid', :prefix => 'People', :classes => ['top','account']
300
- # irb* belongs_to :groups, :class => 'Group', :many => 'memberUid'
301
- # irb* end
302
- #
303
- # In addition, you can do simple membership tests by doing the following:
304
- #
305
- # irb> me.groups.member? 'root'
306
- # => false
307
- # irb> me.groups.member? 'develop'
308
- # => true
309
- #
310
- # ===== has_many
311
- #
312
- # This method is the opposite of belongs_to. Instead of checking other objects in
313
- # other parts of the LDAP tree to see if you belong to them, you have multiple
314
- # objects from other trees listed in your object. To show this, we can just
315
- # invert the example from above:
316
- #
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'
320
- # end
321
- #
322
- # Now we can see that group develop has user 'drewry' as a member, and it can
323
- # even return all responses in object form just like belongs_to methods.
324
- #
325
- # irb> develop = Group.find('develop')
326
- # => ...
327
- # irb> develop.members
328
- # => [#<User:0x000001 ...>, #<User:...>]
329
- #
330
- #
331
- # The arguments for has_many follow the exact same idea that belongs_to's
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.
335
- #
336
- # === Using these new classes
337
- #
338
- # These new classes have many method calls. Many of them are automatically
339
- # generated to provide access to the LDAP object's attributes. Other were defined
340
- # during class creation by special methods like belongs_to. There are a few other
341
- # methods that do not fall in to these categories.
342
- #
343
- #
344
- # ==== .find
345
- #
346
- # .find is a class method that is accessible from any subclass of Base that has
347
- # 'ldap_mapping' called. When called it returns the first match of the given
348
- # class.
349
- #
350
- # irb> Group.find('*').cn
351
- # => "root"
352
- #
353
- # In this simple example, Group.find took the search string of 'deve*' and
354
- # searched for the first match in Group where the dnattr matched the query. This
355
- # is the simplest example of .find.
356
- #
357
- # irb> Group.find(:all, '*').collect {|group| group.cn}
358
- # => ["root", "daemon", "bin", "sys", "adm", "tty", ..., "develop"]
359
- #
360
- # Here .find(:all) returns all matches to the same query. Both .find and
361
- # .find(:all) also can take more expressive arguments:
362
- #
363
- # irb> Group.find(:all, :attribute => 'gidNumber', :value => '1003').collect {|group| group.cn}
364
- # => ["develop"]
365
- #
366
- # So it is pretty clear what :attribute and :value do - they are used to query as
367
- # :attribute=:value.
368
- #
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.
373
- #
374
- # ==== .search
375
- # .search is a class method that is accessible from any subclass of Base, and Base.
376
- # It lets the user perform an arbitrary search against the current LDAP connection
377
- # irrespetive of LDAP mapping data. This is meant to be useful as a utility method
378
- # to cover 80% of the cases where a user would want to use Base.connection directly.
379
- #
380
- # irb> Base.search(:base => 'dc=example,dc=com', :filter => '(uid=roo*)',
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 --
384
- # * :filter defaults to objectClass=* - usually this isn't what you want
385
- # * :base defaults to the base of the class this is executed from (as set in ldap_mapping)
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.
388
- #
389
- # ==== #valid?
390
- #
391
- # valid? is a method that verifies that all attributes that are required by the
392
- # objects current objectClasses are populated.
393
- #
394
- # ==== #save
395
- #
396
- # save is a method that writes any changes to an object back to the LDAP server.
397
- # It automatically handles the addition of new objects, and the modification of
398
- # existing ones.
399
- #
400
- # ==== .exists?
401
- #
402
- # exists? is a simple method which returns true is the current object exists in
403
- # LDAP, or false if it does not.
404
- #
405
- # irb> User.exists?("dshadsadsa")
406
- # => false
407
- #
408
- #
409
- # === ActiveLdap::Base
410
- #
411
- # ActiveLdap::Base has come up a number of times in the examples above. Every
412
- # time, it was being used as the super class for the wrapper objects. While this
413
- # is it's main purpose, it also handles quite a bit more in the background.
414
- #
415
- # ==== What is it?
416
- #
417
- # ActiveLdap::Base is the heart of Ruby/ActiveLdap. It does all the schema
418
- # parsing for validation and attribute-to-method mangling as well as manage the
419
- # connection to LDAP.
420
- #
421
- # ===== establish_connection
422
- #
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.establish_connection is
426
- # here to do all of that for you.
427
- #
428
- #
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:
434
- #
435
- # Base.establish_connection(
436
- # :host => 'ldap.dataspill.org',
437
- # :port => 389,
438
- # :base => 'dc=dataspill,dc=org',
439
- # :logger => log4r_obj,
440
- # :bind_dn => "uid=drewry,ou=People,dc=dataspill,dc=org",
441
- # :password_block => Proc.new { 'password12345' },
442
- # :allow_anonymous => false,
443
- # :try_sasl => false
444
- # )
445
- #
446
- # There are quite a few arguments, but luckily many of them have safe defaults:
447
- # * :host defaults to @@host from configuration.rb waaay back at the setup.rb stage.@
448
- # * :port defaults to @@port from configuration.rb as well
449
- # * :base defaults to Base.base() from configuration.rb
450
- # * :bind_dn defaults @@bind_format from configuration.rb
451
- # * :logger defaults to a Log4r object that prints fatal messages to stderr
452
- # * :password_block defaults to nil
453
- # * :allow_anonymous defaults to true
454
- # * :try_sasl defaults to false - see Advanced Topics for more on this one.
455
- #
456
- #
457
- # Most of these are obvious, but I'll step through them for completeness:
458
- # * :host defines the LDAP server hostname to connect to.
459
- # * :port defines the LDAP server port to connect to.
460
- # * :method defines the type of connection - :tls, :ssl, :plain
461
- # * :base specifies the LDAP search base to use with the prefixes defined in all
462
- # subclasses.
463
- # * :bind_dn specifies what your server expects when attempting to bind with
464
- # credentials.
465
- # * :logger accepts a custom log4r object to integrate with any other logging
466
- # your application uses.
467
- # * :password_block, if defined, give the Proc block for acquiring the password
468
- # * :password, if defined, give the user's password as a String
469
- # * :store_password indicates whether the password should be stored, or if used
470
- # whether the :password_block should be called on each reconnect.
471
- # * :allow_anonymous determines whether anonymous binding is allowed if other
472
- # bind methods fail
473
- # * :try_sasl, when true, tells ActiveLdap to attempt a SASL-GSSAPI bind
474
- # * :sasl_quiet, when true, tells the SASL libraries to not spew messages to STDOUT
475
- # * :method indicates whether to use :ssl, :tls, or :plain
476
- # * :retries - indicates the number of attempts to reconnect that will be undertaken when a stale connection occurs. -1 means infinite.
477
- # * :retry_wait - seconds to wait before retrying a connection
478
- # * :ldap_scope - dictates how to find objects. (Default: ONELEVEL)
479
- # * :timeout - time in seconds - defaults to disabled. This CAN interrupt search() requests. Be warned.
480
- # * :retry_on_timeout - whether to reconnect when timeouts occur. Defaults to true
481
- # See lib/configuration.rb for defaults for each option
482
- #
483
- # Base.establish_connection both connects and binds in one step. It follows
484
- # roughly the following approach:
485
- #
486
- # * Connect to host:port using :method
487
- #
488
- # * If bind_dn and password_block/password, attempt to bind with credentials.
489
- # * If that fails or no password_block and anonymous allowed, attempt to bind
490
- # anonymously.
491
- # * If that fails, error out.
492
- #
493
- # On connect, the configuration options passed in are stored in an internal class variable
494
- # @configuration which is used to cache the information without ditching the defaults passed in
495
- # from configuration.rb
496
- #
497
- # ===== connection
498
- #
499
- # Base.connection returns the ActiveLdap::Connection object.
500
- #
501
- # === Exceptions
502
- #
503
- # There are a few custom exceptions used in Ruby/ActiveLdap. They are detailed below.
504
- #
505
- # ==== DeleteError
506
- #
507
- # This exception is raised when #delete fails. It will include LDAP error
508
- # information that was passed up during the error.
509
- #
510
- # ==== SaveError
511
- #
512
- # This exception is raised when there is a problem in #save updating or creating
513
- # an LDAP entry. Often the error messages are cryptic. Looking at the server
514
- # logs or doing an Ethereal[http://www.ethereal.com] dump of the connection will
515
- # often provide better insight.
516
- #
517
- # ==== AuthenticationError
518
- #
519
- # This exception is raised during Base.establish_connection if no valid authentication methods
520
- # succeeded.
521
- #
522
- # ==== ConnectionError
523
- #
524
- # This exception is raised during Base.establish_connection if no valid
525
- # connection to the LDAP server could be created. Check you configuration.rb,
526
- # Base.establish_connection arguments, and network connectivity! Also check
527
- # your LDAP server logs to see if it ever saw the request.
528
- #
529
- # ==== ObjectClassError
530
- #
531
- # This exception is raised when an object class is used that is not defined
532
- # in the schema.
533
- #
534
- # === Others
535
- #
536
- # Other exceptions may be raised by the Ruby/LDAP module, or by other subsystems.
537
- # If you get one of these exceptions and think it should be wrapped, write me an
538
- # email and let me know where it is and what you expected. For faster results,
539
- # email a patch!
540
- #
541
- # === Putting it all together
542
- #
543
- # Now that all of the components of Ruby/ActiveLdap have been covered, it's time
544
- # to put it all together! The rest of this section will show the steps to setup
545
- # example user and group management scripts for use with the LDAP tree described
546
- # above.
547
- #
548
- # All of the scripts here are in the package's examples/ directory.
549
- #
550
- # ==== Setting up lib/
551
- #
552
- # In ldapadmin/lib/ create the file user.rb:
553
- # cat <<EOF
554
- # class User < ActiveLdap::Base
555
- # ldap_mapping :dn_attribute => 'uid', :prefix => 'ou=People', :classes => ['top', 'account', 'posixAccount']
556
- # belongs_to :groups, :class => 'Group', :wrap => 'memberUid'
557
- # end
558
- # EOF
559
- #
560
- # In ldapadmin/lib/ create the file group.rb:
561
- # cat <<EOF
562
- # class Group < ActiveLdap::Base
563
- # ldap_mapping :classes => ['top', 'posixGroup'], :prefix => 'ou=Group'
564
- # has_many :members, :class => "User", :many => "memberUid"
565
- # has_many :primary_members, :class => 'User', :foreign_key => 'gidNumber', :primary_key => 'gidNumber'
566
- # end # Group
567
- # EOF
568
- #
569
- # Now, we can write some small scripts to do simple management tasks.
570
- #
571
- # ==== Creating LDAP entries
572
- #
573
- # Now let's create a really dumb script for adding users - ldapadmin/useradd:
574
- #
575
- # #!/usr/bin/ruby -W0
576
- #
577
- # require 'active_ldap'
578
- # require 'lib/user'
579
- # require 'lib/group'
580
- # require 'password'
581
- #
582
- # argv, opts, options = ActiveLdap::Command.parse_options do |opts, options|
583
- # opts.banner += " USER_NAME CN UID"
584
- # end
585
- #
586
- # if argv.size == 3
587
- # name, cn, uid = argv
588
- # else
589
- # $stderr.puts opts
590
- # exit 1
591
- # end
592
- #
593
- # pwb = Proc.new do |user|
594
- # ActiveLdap::Command.read_password("[#{user}] Password: ")
595
- # end
596
- #
597
- # ActiveLdap::Base.establish_connection(:password_block => pwb,
598
- # :allow_anonymous => false)
599
- #
600
- # if User.exists?(name)
601
- # $stderr.puts("User #{name} already exists.")
602
- # exit 1
603
- # end
604
- #
605
- # user = User.new(name)
606
- # user.add_class('shadowAccount')
607
- # user.cn = cn
608
- # user.uid_number = uid
609
- # user.gid_number = uid
610
- # user.home_directory = "/home/#{name}"
611
- # user.sn = "somesn"
612
- # unless user.save
613
- # puts "failed"
614
- # puts user.errors.full_messages
615
- # exit 1
616
- # end
617
- #
618
- # ==== Managing LDAP entries
619
- #
620
- # Now let's create another dumb script for modifying users - ldapadmin/usermod:
621
- #
622
- # #!/usr/bin/ruby -W0
623
- #
624
- # require 'active_ldap'
625
- # require 'lib/user'
626
- # require 'lib/group'
627
- #
628
- # argv, opts, options = ActiveLdap::Command.parse_options do |opts, options|
629
- # opts.banner += " USER_NAME CN UID"
630
- # end
631
- #
632
- # if argv.size == 3
633
- # name, cn, uid = argv
634
- # else
635
- # $stderr.puts opts
636
- # exit 1
637
- # end
638
- #
639
- # pwb = Proc.new do |user|
640
- # ActiveLdap::Command.read_password("[#{user}] Password: ")
641
- # end
642
- #
643
- # ActiveLdap::Base.establish_connection(:password_block => pwb,
644
- # :allow_anonymous => false)
645
- #
646
- # unless User.exists?(name)
647
- # $stderr.puts("User #{name} doesn't exist.")
648
- # exit 1
649
- # end
650
- #
651
- # user = User.find(name)
652
- # user.cn = cn
653
- # user.uid_number = uid
654
- # user.gid_number = uid
655
- # unless user.save
656
- # puts "failed"
657
- # puts user.errors.full_messages
658
- # exit 1
659
- # end
660
- #
661
- # ==== Removing LDAP entries
662
- #
663
- # And finally, a dumb script for removing user - ldapadmin/userdel:
664
- #
665
- #
666
- # #!/usr/bin/ruby -W0
667
- #
668
- # require 'active_ldap'
669
- # require 'lib/user'
670
- # require 'lib/group'
671
- #
672
- # argv, opts, options = ActiveLdap::Command.parse_options do |opts, options|
673
- # opts.banner += " USER_NAME"
674
- # end
675
- #
676
- # if argv.size == 1
677
- # name = argv.shift
678
- # else
679
- # $stderr.puts opts
680
- # exit 1
681
- # end
682
- #
683
- # pwb = Proc.new do |user|
684
- # ActiveLdap::Command.read_password("[#{user}] Password: ")
685
- # end
686
- #
687
- # ActiveLdap::Base.establish_connection(:password_block => pwb,
688
- # :allow_anonymous => false)
689
- #
690
- # unless User.exists?(name)
691
- # $stderr.puts("User #{name} doesn't exist.")
692
- # exit 1
693
- # end
694
- #
695
- # User.destroy(name)
696
- #
697
- # === Advanced Topics
698
- #
699
- # Below are some situation tips and tricks to get the most out of Ruby/ActiveLdap.
700
- #
701
- #
702
- # ==== Binary data and other subtypes
703
- #
704
- # Sometimes, you may want to store attributes with language specifiers, or
705
- # perhaps in binary form. This is (finally!) fully supported. To do so,
706
- # follow the examples below:
707
- #
708
- # irb> user = User.new('drewry')
709
- # => ...
710
- # # This adds a cn entry in lang-en and whatever the server default is.
711
- # irb> user.cn = [ 'wad', {'lang-en' => ['wad', 'foo']} ]
712
- # => ...
713
- # irb> user.cn
714
- # => ["wad", {"lang-en-us" => ["wad", "Will Drewry"]}]
715
- # # Now let's add a binary X.509 certificate (assume objectClass is correct)
716
- # irb> user.user_certificate = File.read('example.der')
717
- # => ...
718
- # irb> user.save
719
- #
720
- # So that's a lot to take in. Here's what is going on. I just set the LDAP
721
- # object's cn to "wad" and cn:lang-en-us to ["wad", "Will Drewry"].
722
- # Anytime a LDAP subtype is required, you must encapsulate the data in a Hash.
723
- #
724
- # But wait a minute, I just read in a binary certificate without wrapping it up.
725
- # So any binary attribute _that requires ;binary subtyping_ will automagically
726
- # get wrapped in {'binary' => value} if you don't do it. This keeps your #writes
727
- # from breaking, and my code from crying. For correctness, I could have easily
728
- # done the following:
729
- #
730
- # irb> user.user_certificate = {'binary' => File.read('example.der')}
731
- #
732
- # You should note that some binary data does not use the binary subtype all the time.
733
- # One example is jpegPhoto. You can use it as jpegPhoto;binary or just as jpegPhoto.
734
- # Since the schema dictates that it is a binary value, Ruby/ActiveLdap will write
735
- # it as binary, but the subtype will not be automatically appended as above. The
736
- # use of the subtype on attributes like jpegPhoto is ultimately decided by the
737
- # LDAP site policy and not by any programmatic means.
738
- #
739
- # The only subtypes defined in LDAPv3 are lang-* and binary. These can be nested
740
- # though:
741
- #
742
- # irb> user.cn = [{'lang-JP-jp' => {'binary' => 'somejp'}}]
743
- #
744
- # As I understand it, OpenLDAP does not support nested subtypes, but some
745
- # documentation I've read suggests that Netscape's LDAP server does. I only
746
- # have access to OpenLDAP. If anyone tests this out, please let me know how it
747
- # goes!
748
- #
749
- #
750
- # And that pretty much wraps up this section.
751
- #
752
- # ==== Further integration with your environment aka namespacing
753
- #
754
- # If you want this to cleanly integrate into your system-wide Ruby include path,
755
- # you should put your extension classes inside a custom module.
756
- #
757
- #
758
- # Example:
759
- #
760
- # ./myldap.rb:
761
- # require 'active_ldap'
762
- # require 'myldap/user'
763
- # require 'myldap/group'
764
- # module MyLDAP
765
- # end
766
- #
767
- # ./myldap/user.rb:
768
- # module MyLDAP
769
- # class User < ActiveLdap::Base
770
- # ldap_mapping :dn_attribute => 'uid', :prefix => 'ou=People', :classes => ['top', 'account', 'posixAccount']
771
- # belongs_to :groups, :class => 'MyLDAP::Group', :many => 'memberUid'
772
- # end
773
- # end
774
- #
775
- # ./myldap/group.rb:
776
- # module MyLDAP
777
- # class Group < ActiveLdap::Base
778
- # ldap_mapping :classes => ['top', 'posixGroup'], :prefix => 'ou=Group'
779
- # has_many :members, :class => 'MyLDAP::User', :wrap => 'memberUid'
780
- # has_many :primary_members, :class => 'MyLDAP::User', :foreign_key => 'gidNumber', :primary_key => 'gidNumber'
781
- # end
782
- # end
783
- #
784
- # Now in your local applications, you can call
785
- #
786
- # require 'myldap'
787
- #
788
- # MyLDAP::Group.new('foo')
789
- # ...
790
- #
791
- # and everything should work well.
792
- #
793
- #
794
- # ==== force array results for single values
795
- #
796
- # Even though Ruby/ActiveLdap attempts to maintain programmatic ease by
797
- # returning Array values only. By specifying 'true' as an argument to
798
- # any attribute method you will get back a Array if it is single value.
799
- # Here's an example:
800
- #
801
- # irb> user = User.new('drewry')
802
- # => ...
803
- # irb> user.cn(true)
804
- # => ["Will Drewry"]
805
- #
806
- # ==== Dynamic attribute crawling
807
- #
808
- # If you use tab completion in irb, you'll notice that you /can/ tab complete the dynamic
809
- # attribute methods. You can still see which methods are for attributes using
810
- # Base#attribute_names:
811
- #
812
- # irb> d = Group.new('develop')
813
- # => ...
814
- # irb> d.attribute_names
815
- # => ["gidNumber", "cn", "memberUid", "commonName", "description", "userPassword", "objectClass"]
816
- #
817
- #
818
- # ==== Juggling multiple LDAP connections
819
- #
820
- # In the same vein as the last tip, you can use multiple LDAP connections by
821
- # per class as follows:
822
- #
823
- # irb> anon_class = Class.new(Base)
824
- # => ...
825
- # irb> anon_class.establish_connection
826
- # => ...
827
- # irb> auth_class = Class.new(Base)
828
- # => ...
829
- # irb> auth_class.establish_connection(:password_block => {'mypass'})
830
- # => ...
831
- #
832
- # This can be useful for doing authentication tests and other such tricks.
833
- #
834
- # ==== :try_sasl
835
- #
836
- # If you have the Ruby/LDAP package with the SASL/GSSAPI patch from Ian
837
- # MacDonald's web site, you can use Kerberos to bind to your LDAP server. By
838
- # default, :try_sasl is false.
839
- #
840
- # Also note that you must be using OpenLDAP 2.1.29 or higher to use SASL/GSSAPI
841
- # due to some bugs in older versions of OpenLDAP.
842
- #
843
- # ==== Don't be afraid! [Internals]
844
- #
845
- # Don't be afraid to add more methods to the extensions classes and to
846
- # experiment. That's exactly how I ended up with this package. If you come up
847
- # with something cool, please share it!
848
- #
849
- # The internal structure of ActiveLdap::Base, and thus all its subclasses, is
850
- # still in flux. I've tried to minimize the changes to the overall API, but
851
- # the internals are still rough around the edges.
852
- #
853
- # ===== Where's ldap_mapping data stored? How can I get to it?
854
- #
855
- # When you call ldap_mapping, it overwrites several class methods inherited
856
- # from Base:
857
- # * Base.base()
858
- # * Base.required_classes()
859
- # * Base.dn_attribute()
860
- # You can access these from custom class methods by calling MyClass.base(),
861
- # or whatever. There are predefined instance methods for getting to these
862
- # from any new instance methods you define:
863
- # * Base#base()
864
- # * Base#required_classes()
865
- # * Base#dn_attribute()
866
- #
867
- # ===== What else?
868
- #
869
- # Well if you want to use the LDAP connection for anything, I'd suggest still
870
- # calling Base.connection to get it. There really aren't many other internals
871
- # that need to be worried about. You could get the LDAP schema with
872
- # Base.schema.
873
- #
874
- # The only other useful tricks are dereferencing and accessing the stored
875
- # data. Since LDAP attributes can have multiple names, e.g. cn or commonName,
876
- # any methods you write might need to figure it out. I'd suggest just
877
- # calling self[attribname] to get the value, but if that's not good enough,
878
- # you can call look up the stored name by #to_real_attribute_name as follows:
879
- # irb> to_real_attribute_name('commonName')
880
- # => 'cn'
881
- #
882
- # This tells you the name the attribute is stored in behind the scenes (@data).
883
- # Again, self[attribname] should be enough for most extensions, but if not,
884
- # it's probably safe to dabble here.
885
- #
886
- # Also, if you like to look up all aliases for an attribute, you can call the
887
- # following:
888
- #
889
- # irb> schema.attribute_aliases('cn')
890
- # => ['cn','commonName']
891
- #
892
- # This is discovered automagically from the LDAP server's schema.
893
- #
894
- # == Limitations
895
- #
896
- # === Speed
897
- #
898
- # Currently, Ruby/ActiveLdap could be faster. I have some recursive type
899
- # checking going on which slows object creation down, and I'm sure there
900
- # are many, many other places optimizations can be done. Feel free
901
- # to send patches, or just hang in there until I can optimize away the
902
- # slowness.
903
- #
904
- # == Feedback
905
- #
906
- # Any and all feedback and patches are welcome. I am very excited about this
907
- # package, and I'd like to see it prove helpful to more people than just myself.
908
- #
909
-
910
- if RUBY_PLATFORM.match('linux')
911
- require 'active_ldap/timeout'
912
- else
913
- require 'active_ldap/timeout_stub'
914
- end
915
-
916
- require_gem_if_need = Proc.new do |library_name, gem_name|
917
- begin
918
- require library_name
919
- rescue LoadError
920
- require 'rubygems'
921
- require_gem gem_name
922
- require library_name
923
- end
924
- end
925
-
926
- require_gem_if_need.call("active_support", "activesupport")
927
-
928
- if Dependencies.respond_to?(:load_paths)
929
- Dependencies.load_paths << File.expand_path(File.dirname(__FILE__))
930
- end
931
-
932
- require 'active_ldap/base'
933
- require 'active_ldap/associations'
934
- require 'active_ldap/configuration'
935
- require 'active_ldap/connection'
936
- require 'active_ldap/attributes'
937
- require 'active_ldap/object_class'
938
- require 'active_ldap/distinguished_name'
939
-
940
- require_gem_if_need.call("active_record/base", "activerecord")
941
- require 'active_ldap/validations'
942
- require 'active_ldap/callbacks'
943
-
944
- module ActiveLdap
945
- VERSION = "0.8.2"
946
- end
947
-
948
- ActiveLdap::Base.class_eval do
949
- include ActiveLdap::Configuration
950
- include ActiveLdap::Connection
951
- include ActiveLdap::Attributes
952
- include ActiveLdap::ObjectClass
953
- include ActiveLdap::Associations
954
- include ActiveLdap::Validations
955
- include ActiveLdap::Callbacks
956
- end
957
-
958
- unless defined?(ACTIVE_LDAP_CONNECTION_ADAPTERS)
959
- ACTIVE_LDAP_CONNECTION_ADAPTERS = %w(ldap net_ldap)
960
- end
961
-
962
- ACTIVE_LDAP_CONNECTION_ADAPTERS.each do |adapter|
963
- require "active_ldap/adapter/#{adapter}"
964
- end