ruby-activeldap-debug 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/activeldap.rb ADDED
@@ -0,0 +1,916 @@
1
+ #!/usr/bin/ruby -W0
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.com], 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.com/rfcs/rfc1777.html] - Lightweight Directory Access Protocol
30
+ # * OpenLDAP[http://www.openldap.com]
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.com] 1.8.x
54
+ # * Ruby/LDAP[http://ruby-ldap.sourcefcome.net]
55
+ # * Log4r[http://log4r.sourcefcome.net]
56
+ # * (Optional) Ruby/LDAP+GSSAPI[http://caliban.com/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm]
57
+ # * An LDAP server compatible with Ruby/LDAP: OpenLDAP[http://www.openldap.com], etc
58
+ # - Your LDAP server must allow root_dse queries to allow for schema queries
59
+ # * Examples also require: Ruby/Password[http://raa.ruby-lang.com/project/ruby-password/]
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.example.com/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/activeldap/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 'activeldap'
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 'activeldap'
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('*')
138
+ # => ["root", "daemon", "bin", "sys", "adm", "tty", ..., "develop"]
139
+ #
140
+ # irb> group = Group.new("develop")
141
+ # => #<Group:0x..........>
142
+ #
143
+ # irb> group.members
144
+ # => ["drewry"]
145
+ #
146
+ # irb> group.cn
147
+ # => "develop"
148
+ #
149
+ # irb> group.gidNumber
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 :dnattr => 'cn', :prefix => 'ou=Groups', :classes => ['top', 'posixGroup']
181
+ # end
182
+ #
183
+ # As you can see, this method is used for defining how this class maps in to LDAP. Let's say that
184
+ # my LDAP tree looks something like this:
185
+ #
186
+ # * dc=example,dc=com
187
+ # |- ou=People,dc=example,dc=com
188
+ # |+ ou=Groups,dc=example,dc=com
189
+ # \
190
+ # |- cn=develop,ou=Groups,dc=example,dc=com
191
+ # |- cn=root,ou=Groups,dc=example,dc=com
192
+ # |- ...
193
+ #
194
+ # Under ou=People I store user objects, and under ou=Groups, I store group
195
+ # objects. What |ldap_mapping| has done is mapped the class in to the LDAP tree
196
+ # abstractly. With the given :dnattr and :prefix, it will only work for entries
197
+ # under ou=Groups,dc=example,dc=com using the primary attribute 'cn' as the
198
+ # beginning of the distinguished name.
199
+ #
200
+ # Just for clarity, here's how the arguments map out:
201
+ #
202
+ # cn=develop,ou=Groups,dc=example,dc=com
203
+ # ^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
204
+ # :dnattr | |
205
+ # :prefix |
206
+ # :base from configuration.rb
207
+ #
208
+ #
209
+ # Something's missing: :classes. :classes is used to tell Ruby/ActiveLDAP what
210
+ # the minimum requirement is when creating a new object. LDAP uses objectClasses
211
+ # to define what attributes a LDAP object may have. Ruby/ActiveLDAP needs to know
212
+ # what classes are required when creating a new object. Of course, you can leave
213
+ # that field out to default to ['top'] only. Then you can let each application
214
+ # choose what objectClasses their objects should have by calling the method e.g.
215
+ # Group#objectClass=(value) or by modifying the value returned by the accessor,
216
+ # e.g. Group#objectClass.
217
+ #
218
+ # Note that is can be very important to define the default :classes value. Due to
219
+ # implementation choices with most LDAP servers, once an object is created, its
220
+ # structural objectclasses may not be removed (or replaced). Setting a sane default
221
+ # may help avoid programmer error later.
222
+ #
223
+ # :classes isn't the only optional argument. If :dnattr is left off, it defaults
224
+ # to 'cn'. If :prefix is left off, it will default to 'ou=CLASSNAME'. In this
225
+ # case, it would be 'ou=Group'.
226
+ #
227
+ # :classes should be an Array. :dnattr should be a String and so should :prefix.
228
+ #
229
+ #
230
+ # ===== belongs_to
231
+ #
232
+ # This method allows an extension class to make use of other extension classes
233
+ # tying objects together across the LDAP tree. Often, user objects will be
234
+ # members of, or belong_to, Group objects.
235
+ #
236
+ # * dc=example,dc=com
237
+ # |+ ou=People,dc=example,dc=com
238
+ # \
239
+ # |- uid=drewry,ou=People,dc=example,dc=com
240
+ # |- ou=Groups,dc=example,dc=com
241
+ #
242
+ #
243
+ # In the above tree, one such example would be user 'drewry' who is a part of the
244
+ # group 'develop'. You can see this by looking at the 'memberUid' field of 'develop'.
245
+ #
246
+ # irb> develop = Group.new('develop')
247
+ # => ...
248
+ # irb> develop.memberUid
249
+ # => ['drewry', 'builder']
250
+ #
251
+ # If we look at the LDAP entry for 'drewry', we do not see any references to
252
+ # group 'develop'. In order to remedy that, we can use belongs_to
253
+ #
254
+ # irb> class User < ActiveLDAP::Base
255
+ # irb* ldap_mapping :dnattr => 'uid', :prefix => 'People', :classes => ['top','account']
256
+ # irb* belongs_to :groups, :class_name => 'Group', :foreign_key => 'memberUid', :local_key => 'uid'
257
+ # irb* end
258
+ #
259
+ # Now, class User will have a method called 'groups' which will retrieve all
260
+ # Group objects that a user is in.
261
+ #
262
+ # irb> me = User.new('drewry')
263
+ # irb> me.groups
264
+ # => [#<Group:0x000001 ...>, #<Group:0x000002 ...>, ...]
265
+ # irb> me.groups(true).each { |group| p group.cn };nil
266
+ # "cdrom"
267
+ # "audio"
268
+ # "develop"
269
+ # => nil
270
+ # (Note: nil is just there to make the output cleaner...)
271
+ #
272
+ # Methods created with belongs_to also take an optional argument: objects. This
273
+ # argument specifies whether it will return the value of the 'dnattr' of the
274
+ # objects, or whether it will return Group objects.
275
+ #
276
+ # irb> me.groups(false)
277
+ # => ["cdrom", "audio", "develop"]
278
+ #
279
+ # TIP: If you weren't sure what the distinguished name attribute was for Group,
280
+ # you could also do the following:
281
+ #
282
+ # irb> me.groups.each { |group| p group.dnattr };nil
283
+ # "cdrom"
284
+ # "audio"
285
+ # "develop"
286
+ # => nil
287
+ #
288
+ # Now let's talk about the arguments. The first argument is the name of the
289
+ # method you wish to create. In this case, we created a method called groups
290
+ # using the symbol :groups. The next collection of arguments are actually a Hash
291
+ # (as with ldap_mapping). :class_name should be a string that has the name of a
292
+ # class you've already included. If you class is inside of a module, be sure to
293
+ # put the whole name, e.g. :class_name => "MyLdapModule::Group". :foreign_key
294
+ # tells belongs_to what attribute Group objects have that match the :local_key.
295
+ # :local_key is the name of the local attribute whose value should be looked up
296
+ # in Group under the foreign key. If :local_key is left off of the argument list,
297
+ # it is assumed to be the dnattr. With this in mind, the above definition could
298
+ # become:
299
+ #
300
+ # irb> class User < ActiveLDAP::Base
301
+ # irb* ldap_mapping :dnattr => 'uid', :prefix => 'People', :classes => ['top','account']
302
+ # irb* belongs_to :groups, :class_name => 'Group', :foreign_key => 'memberUid'
303
+ # irb* end
304
+ #
305
+ # In addition, you can do simple membership tests by doing the following:
306
+ #
307
+ # irb> me.groups.member? 'root'
308
+ # => false
309
+ # irb> me.groups.member? 'develop'
310
+ # => true
311
+ #
312
+ # ===== has_many
313
+ #
314
+ # This method is the opposite of belongs_to. Instead of checking other objects in
315
+ # other parts of the LDAP tree to see if you belong to them, you have multiple
316
+ # objects from other trees listed in your object. To show this, we can just
317
+ # invert the example from above:
318
+ #
319
+ # class Group < ActiveLDAP::Base
320
+ # ldap_mapping :dnattr => 'cn', :prefix => 'ou=Groups', :classes => ['top', 'posixGroup']
321
+ # has_many :members, :class_name => "User", :local_key => "memberUid", :foreign_key => 'uid'
322
+ # end
323
+ #
324
+ # Now we can see that group develop has user 'drewry' as a member, and it can
325
+ # even return all responses in object form just like belongs_to methods.
326
+ #
327
+ # irb> develop = Group.new('develop')
328
+ # => ...
329
+ # irb> develop.members
330
+ # => [#<User:0x000001 ...>, #<User:...>]
331
+ # irb> develop.members(false)
332
+ # => ["drewry", "builder"]
333
+ #
334
+ #
335
+ # The arguments for has_many follow the exact same idea that belongs_to's
336
+ # arguments followed. :local_key's contents are used to search for matching
337
+ # :foreign_key content. If :foreign_key is not specified, it defaults to the
338
+ # dnattr of the specified :class_name.
339
+ #
340
+ # === Using these new classes
341
+ #
342
+ # These new classes have many method calls. Many of them are automatically
343
+ # generated to provide access to the LDAP object's attributes. Other were defined
344
+ # during class creation by special methods like belongs_to. There are a few other
345
+ # methods that do not fall in to these categories.
346
+ #
347
+ #
348
+ # ==== .find and .find_all
349
+ #
350
+ # .find is a class method that is accessible from any subclass of Base that has
351
+ # 'ldap_mapping' called. When called it returns the first match of the given
352
+ # class.
353
+ #
354
+ # irb> Group.find('*')
355
+ # => "root"
356
+ #
357
+ # In this simple example, Group.find took the search string of 'deve*' and
358
+ # searched for the first match in Group where the dnattr matched the query. This
359
+ # is the simplest example of .find.
360
+ #
361
+ # irb> Group.find_all('*')
362
+ # => ["root", "daemon", "bin", "sys", "adm", "tty", ..., "develop"]
363
+ #
364
+ # Here .find_all returns all matches to the same query. Both .find and .find_all
365
+ # also can take more expressive arguments:
366
+ #
367
+ # irb> Group.find_all(:attribute => 'gidNumber', :value => '1003', :objects => false)
368
+ # => ["develop"]
369
+ #
370
+ # So it is pretty clear what :attribute and :value do - they are used to query as
371
+ # :attribute=:value. :objects is used to return precreated objects of the given
372
+ # Class when it is set to true.
373
+ #
374
+ # irb> Group.find_all(:attribute => 'gidNumber', :value => '1003', :objects => false)
375
+ # => [#<Group:0x40674a70 ..>]
376
+ #
377
+ # If :objects is unspecified, it defaults to false. If :attribute is unspecified,
378
+ # it defaults to the dnattr.
379
+ #
380
+ # ==== .search
381
+ # .search is a class method that is accessible from any subclass of Base, and Base.
382
+ # It lets the user perform an arbitrary search against the current LDAP connection
383
+ # irrespetive of LDAP mapping data. This is meant to be useful as a utility method
384
+ # to cover 80% of the cases where a user would want to use Base.connection directly.
385
+ #
386
+ # irb> Base.search(:base => 'dc=example,dc=com', :filter => '(uid=roo*)',
387
+ # :scope => LDAP::LDAP_SCOPE_SUBTREE, :attrs => ['uid', 'cn'])
388
+ # => [{"dn"=>"uid=root,ou=People,dc=example,dc=com","cn"=>["root"], "uidNumber"=>["0"]}]
389
+ # You can specify the :filter, :base, :scope, and :attrs, but they all have defaults --
390
+ # * :filter defaults to objectClass=* - usually this isn't what you want
391
+ # * :base defaults to the base of the class this is executed from (as set in ldap_mapping)
392
+ # * :scope defaults to LDAP::LDAP_SCOPE_SUBTREE. Usually you won't need to change it
393
+ # * :attrs defaults to [] and is the list of attrs you want back. Empty means all of them.
394
+ #
395
+ # ==== #validate
396
+ #
397
+ # validate is a method that verifies that all attributes that are required by the
398
+ # objects current objectClasses are populated. This also will call the
399
+ # private "#enforce_types" method. This will make sure that all values defined are
400
+ # valid to be written to LDAP. #validate is called by #write prior to
401
+ # performing any action. Its explicit use in an application is unnecessary, and
402
+ # it may become a private method in the future.
403
+ #
404
+ # ==== #write
405
+ #
406
+ # write is a method that writes any changes to an object back to the LDAP server.
407
+ # It automatically handles the addition of new objects, and the modification of
408
+ # existing ones.
409
+ #
410
+ # ==== #exists?
411
+ #
412
+ # exists? is a simple method which returns true is the current object exists in
413
+ # LDAP, or false if it does not.
414
+ #
415
+ # irb> newuser = User.new("dshadsadsa")
416
+ # => ...
417
+ # irb> newuser.exists?
418
+ # => false
419
+ #
420
+ #
421
+ # === ActiveLDAP::Base
422
+ #
423
+ # ActiveLDAP::Base has come up a number of times in the examples above. Every
424
+ # time, it was being used as the super class for the wrapper objects. While this
425
+ # is it's main purpose, it also handles quite a bit more in the background.
426
+ #
427
+ # ==== What is it?
428
+ #
429
+ # ActiveLDAP::Base is the heart of Ruby/ActiveLDAP. It does all the schema
430
+ # parsing for validation and attribute-to-method mangling as well as manage the
431
+ # connection to LDAP.
432
+ #
433
+ # ===== connect
434
+ #
435
+ # Base.connect takes many (optional) arguments and is used to connect to the LDAP
436
+ # server. Sometimes you will want to connect anonymously and other times over TLS
437
+ # with user credentials. Base.connect is here to do all of that for you.
438
+ #
439
+ #
440
+ # By default, if you call any subclass of Base, such as Group, it will call
441
+ # Base.connect() if these is no active LDAP connection. If your server allows
442
+ # anonymous binding, and you only want to access data in a read-only fashion, you
443
+ # won't need to call Base.connect. Here is a fully parameterized call:
444
+ #
445
+ # Base.connect(
446
+ # :host => 'ldap.example.com',
447
+ # :port => 389,
448
+ # :base => 'dc=example,dc=com',
449
+ # :bind_format => "uid=%s,ou=People,dc=example,dc=com",
450
+ # :logger => log4r_obj,
451
+ # :user => 'drewry',
452
+ # :password_block => Proc.new { 'password12345' },
453
+ # :allow_anonymous => false,
454
+ # :try_sasl => false
455
+ # )
456
+ #
457
+ # There are quite a few arguments, but luckily many of them have safe defaults:
458
+ # * :host defaults to @@host from configuration.rb waaay back at the setup.rb stage.@
459
+ # * :port defaults to @@port from configuration.rb as well
460
+ # * :base defaults to Base.base() from configuration.rb
461
+ # * :bind_format defaults @@bind_format from configuration.rb
462
+ # * :logger defaults to a Log4r object that prints fatal messages to stderr
463
+ # * :user defaults to ENV['user']
464
+ # * :password_block defaults to nil
465
+ # * :allow_anonymous defaults to true
466
+ # * :try_sasl defaults to false - see Advanced Topics for more on this one.
467
+ #
468
+ #
469
+ # Most of these are obvious, but I'll step through them for completeness:
470
+ # * :host defines the LDAP server hostname to connect to.
471
+ # * :port defines the LDAP server port to connect to.
472
+ # * :base specifies the LDAP search base to use with the prefixes defined in all
473
+ # subclasses.
474
+ # * :bind_format specifies what your server expects when attempting to bind with
475
+ # credentials.
476
+ # * :logger accepts a custom log4r object to integrate with any other logging
477
+ # your application uses.
478
+ # * :user gives the username to substitute into bind_format for binding with
479
+ # credentials
480
+ # * :password_block, if defined, give the Proc block for acquiring the password
481
+ # * :allow_anonymous determines whether anonymous binding is allowed if other
482
+ # bind methods fail
483
+ #
484
+ # Base.connect both connects and binds in one step. It follows roughly the following approach:
485
+ #
486
+ # * Connect to host:port.
487
+ # * Try TLS.
488
+ # * If that fails try SSL.
489
+ # * If that fails try no encryption.
490
+ #
491
+ # * If user and password_block, attempt to bind with credentials.
492
+ # * If that fails or no password_block and anonymous allowed, attempt to bind
493
+ # anonymously.
494
+ # * If that fails, error out.
495
+ #
496
+ # On connect, the configuration options passed in are stored in an internal class variable
497
+ # @@config which is used to cache the information without ditching the defaults passed in
498
+ # from configuration.rb
499
+ #
500
+ # ===== close
501
+ #
502
+ # Base.close discards the current LDAP connection.
503
+ #
504
+ # ===== connection
505
+ #
506
+ # Base.connection returns the raw LDAP connection object.
507
+ #
508
+ # === Exceptions
509
+ #
510
+ # There are a few custom exceptions used in Ruby/ActiveLDAP. They are detailed below.
511
+ #
512
+ # ==== AttributeEmpty
513
+ #
514
+ # This exception is raised when a required attribute is empty. It is only raised
515
+ # by #validate, and by proxy, #write.
516
+ #
517
+ # ==== DeleteError
518
+ #
519
+ # This exception is raised when #delete fails. It will include LDAP error
520
+ # information that was passed up during the error.
521
+ #
522
+ # ==== WriteError
523
+ #
524
+ # This exception is raised when there is a problem in #write updating or creating
525
+ # an LDAP entry. Often the error messages are cryptic. Looking at the server
526
+ # logs or doing an Ethereal[http://www.ethereal.com] dump of the connection will
527
+ # often provide better insight.
528
+ #
529
+ # ==== AuthenticationError
530
+ #
531
+ # This exception is raised during Base.connect if no valid authentication methods
532
+ # succeeded.
533
+ #
534
+ # ==== ConnectionError
535
+ #
536
+ # This exception is raised during Base.connect if no valid connection to the
537
+ # LDAP server could be created. Check you configuration.rb, Base.connect
538
+ # arguments, and network connectivity! Also check your LDAP server logs to see
539
+ # if it ever saw the request.
540
+ #
541
+ # ==== ObjectClassError
542
+ #
543
+ # This exception is raised when an object class is used that is not defined
544
+ # in the schema.
545
+ #
546
+ # === Others
547
+ #
548
+ # Other exceptions may be raised by the Ruby/LDAP module, or by other subsystems.
549
+ # If you get one of these exceptions and thing it should be wrapped, write me an
550
+ # email and let me know where it is and what you expected. For faster results,
551
+ # email a patch!
552
+ #
553
+ # === Putting it all together
554
+ #
555
+ # Now that all of the components of Ruby/ActiveLDAP have been covered, it's time
556
+ # to put it all together! The rest of this section will show the steps to setup
557
+ # example user and group management scripts for use with the LDAP tree described
558
+ # above.
559
+ #
560
+ # All of the scripts here are in the package's examples/ directory.
561
+ #
562
+ # ==== Setting up lib/
563
+ #
564
+ # In ldapadmin/lib/ create the file user.rb:
565
+ # cat <<EOF
566
+ # class User < ActiveLDAP::Base
567
+ # ldap_mapping :dnattr => 'uid', :prefix => 'ou=People', :classes => ['top', 'account', 'posixAccount']
568
+ # belongs_to :groups, :class_name => 'Group', :foreign_key => 'memberUid'
569
+ # end
570
+ # EOF
571
+ #
572
+ # In ldapadmin/lib/ create the file group.rb:
573
+ # cat <<EOF
574
+ # class Group < ActiveLDAP::Base
575
+ # ldap_mapping :classes => ['top', 'posixGroup'], :prefix => 'ou=Group'
576
+ # has_many :members, :class_name => "User", :local_key => "memberUid"
577
+ # belongs_to :primary_members, :class_name => 'User', :foreign_key => 'gidNumber', :local_key => 'gidNumber'
578
+ # end # Group
579
+ # EOF
580
+ #
581
+ # Now, we can write some small scripts to do simple management tasks.
582
+ #
583
+ # ==== Creating LDAP entries
584
+ #
585
+ # Now let's create a really dumb script for adding users - ldapadmin/useradd:
586
+ #
587
+ # #!/usr/bin/ruby -W0
588
+ #
589
+ # require 'activeldap'
590
+ # require 'lib/user'
591
+ # require 'lib/group'
592
+ # require 'password'
593
+ #
594
+ # (printf($stderr, "Usage:\n%s name cn uid\n", $0); exit 1) if ARGV.size != 3
595
+ #
596
+ # puts "Adding user #{ARGV[0]}"
597
+ # pwb = Proc.new {
598
+ # Password.get('Password: ')
599
+ # }
600
+ # ActiveLDAP::Base.connect(:password_block => pwb, :allow_anonymous => false)
601
+ # user = User.new(ARGV[0])
602
+ # user.objectClass = user.objectClass << 'posixAccount' << 'shadowAccount'
603
+ # user.cn = ARGV[1]
604
+ # user.uidNumber = ARGV[2]
605
+ # user.gidNumber = ARGV[2]
606
+ # user.homeDirectory = "/home/#{ARGV[0]}"
607
+ # user.write
608
+ # puts "User created!"
609
+ # exit 0
610
+ #
611
+ #
612
+ # ==== Managing LDAP entries
613
+ #
614
+ # Now let's create another dumb script for modifying users - ldapadmin/usermod:
615
+ #
616
+ # #!/usr/bin/ruby -W0
617
+ #
618
+ # require 'activeldap'
619
+ # require 'lib/user'
620
+ # require 'lib/group'
621
+ # require 'password'
622
+ #
623
+ # (printf($stderr, "Usage:\n%s name cn uid\n", $0); exit 1) if ARGV.size != 3
624
+ #
625
+ # puts "Changing user #{ARGV[0]}"
626
+ # pwb = Proc.new {
627
+ # Password.get('Password: ')
628
+ # }
629
+ # ActiveLDAP::Base.connect(:password_block => pwb, :allow_anonymous => false)
630
+ # user = User.new(ARGV[0])
631
+ # user.cn = ARGV[1]
632
+ # user.uidNumber = ARGV[2]
633
+ # user.gidNumber = ARGV[2]
634
+ # user.write
635
+ # puts "Modification successful!"
636
+ # exit 0
637
+ #
638
+ #
639
+ #
640
+ # ==== Removing LDAP entries
641
+ #
642
+ # And finally, a dumb script for removing user - ldapadmin/userdel:
643
+ #
644
+ #
645
+ # #!/usr/bin/ruby -W0
646
+ #
647
+ # require 'activeldap'
648
+ # require 'lib/user'
649
+ # require 'lib/group'
650
+ # require 'password'
651
+ #
652
+ # (printf($stderr, "Usage:\n%s name\n", $0); exit 1) if ARGV.size != 1
653
+ #
654
+ # puts "Changing user #{ARGV[0]}"
655
+ # pwb = Proc.new {
656
+ # Password.get('Password: ')
657
+ # }
658
+ # ActiveLDAP::Base.connect(:password_block => pwb, :allow_anonymous => false)
659
+ # user = User.new(ARGV[0])
660
+ # user.delete
661
+ # puts "User has been delete"
662
+ # exit 0
663
+ #
664
+ # === Advanced Topics
665
+ #
666
+ # Below are some situation tips and tricks to get the most out of Ruby/ActiveLDAP.
667
+ #
668
+ #
669
+ # ==== Binary data and other subtypes
670
+ #
671
+ # Sometimes, you may want to store attributes with language specifiers, or
672
+ # perhaps in binary form. This is (finally!) fully supported. To do so,
673
+ # follow the examples below:
674
+ #
675
+ # irb> user = User.new('drewry')
676
+ # => ...
677
+ # # This adds a cn entry in lang-en and whatever the server default is.
678
+ # irb> user.cn = [ 'wad', {'lang-en' => ['wad', 'foo']} ]
679
+ # => ...
680
+ # irb> user.cn
681
+ # => ["wad", {"lang-en-us" => ["wad", "Will Drewry"]}]
682
+ # # Now let's add a binary X.509 certificate (assume objectClass is correct)
683
+ # irb> user.userCertificate = File.read('example.der')
684
+ # => ...
685
+ # irb> user.write
686
+ #
687
+ # So that's a lot to take in. Here's what is going on. I just set the LDAP
688
+ # object's cn to "wad" and cn:lang-en-us to ["wad", "Will Drewry"].
689
+ # Anytime a LDAP subtype is required, you must encapsulate the data in a Hash.
690
+ #
691
+ # But wait a minute, I just read in a binary certificate without wrapping it up.
692
+ # So any binary attribute _that requires ;binary subtyping_ will automagically
693
+ # get wrapped in {'binary' => value} if you don't do it. This keeps your #writes
694
+ # from breaking, and my code from crying. For correctness, I could have easily
695
+ # done the following:
696
+ #
697
+ # irb> user.userCertificate = {'binary' => File.read('example.der')}
698
+ #
699
+ # You should note that some binary data does not use the binary subtype all the time.
700
+ # One example is jpegPhoto. You can use it as jpegPhoto;binary or just as jpegPhoto.
701
+ # Since the schema dictates that it is a binary value, Ruby/ActiveLDAP will write
702
+ # it as binary, but the subtype will not be automatically appended as above. The
703
+ # use of the subtype on attributes like jpegPhoto is ultimately decided by the
704
+ # LDAP site policy and not by any programmatic means.
705
+ #
706
+ # The only subtypes defined in LDAPv3 are lang-* and binary. These can be nested
707
+ # though:
708
+ #
709
+ # irb> user.cn = [{'lang-JP-jp' => {'binary' => 'somejp'}}]
710
+ #
711
+ # As I understand it, OpenLDAP does not support nested subtypes, but some
712
+ # documentation I've read suggests that Netscape's LDAP server does. I only
713
+ # have access to OpenLDAP. If anyone tests this out, please let me know how it
714
+ # goes!
715
+ #
716
+ #
717
+ # And that pretty much wraps up this section.
718
+ #
719
+ # ==== Further integration with your environment aka namespacing
720
+ #
721
+ # If you want this to cleanly integrate into your system-wide Ruby include path,
722
+ # you should put your extension classes inside a custom module.
723
+ #
724
+ #
725
+ # Example:
726
+ #
727
+ # ./myldap.rb:
728
+ # require 'activeldap'
729
+ # require 'myldap/user'
730
+ # require 'myldap/group'
731
+ # module MyLDAP
732
+ # end
733
+ #
734
+ # ./myldap/user.rb:
735
+ # module MyLDAP
736
+ # class User < ActiveLDAP::Base
737
+ # ldap_mapping :dnattr => 'uid', :prefix => 'ou=People', :classes => ['top', 'account', 'posixAccount']
738
+ # belongs_to :groups, :class_name => 'MyLDAP::Group', :foreign_key => 'memberUid'
739
+ # end
740
+ # end
741
+ #
742
+ # ./myldap/group.rb:
743
+ # module MyLDAP
744
+ # class Group < ActiveLDAP::Base
745
+ # ldap_mapping :classes => ['top', 'posixGroup'], :prefix => 'ou=Group'
746
+ # has_many :members, :class_name => 'MyLDAP::User', :local_key => 'memberUid'
747
+ # belongs_to :primary_members, :class_name => 'MyLDAP::User', :foreign_key => 'gidNumber', :local_key => 'gidNumber'
748
+ # end
749
+ # end
750
+ #
751
+ # Now in your local applications, you can call
752
+ #
753
+ # require 'myldap'
754
+ #
755
+ # MyLDAP::Group.new('foo')
756
+ # ...
757
+ #
758
+ # and everything should work well.
759
+ #
760
+ #
761
+ # ==== Non-array results for single values
762
+ #
763
+ # Even though Ruby/ActiveLDAP attempts to maintain programmatic ease by
764
+ # returning Array values only. By specifying 'false' as an argument to
765
+ # any attribute method you will get back a String if it is single value.
766
+ # This is useful when you are just dumping values for human reading.
767
+ # Here's an example:
768
+ #
769
+ # irb> user = User.new('drewry')
770
+ # => ...
771
+ # irb> user.cn(false)
772
+ # => "Will Drewry"
773
+ #
774
+ # That's it. Now you can make human-readable output faster.
775
+ #
776
+ # ==== Dynamic attribute crawling
777
+ #
778
+ # If you use tab completion in irb, you'll notice that you /can/ tab complete the dynamic
779
+ # attribute methods. You can still see which methods are for attributes using
780
+ # Base#attributes:
781
+ #
782
+ # irb> d = Group.new('develop')
783
+ # => ...
784
+ # irb> d.attributes
785
+ # => ["gidNumber", "cn", "memberUid", "commonName", "description", "userPassword", "objectClass"]
786
+ #
787
+ #
788
+ # ==== Advanced LDAP queries
789
+ #
790
+ # With the addition of Base.search, you can do arbitrary queries against LDAP
791
+ # without needed to understand how to use Ruby/LDAP.
792
+ #
793
+ # If that's still not enough, you can access the
794
+ # Ruby/LDAP connection object using the class method Base.connection. You can
795
+ # do all of your LDAP specific calls here and then continue about your normal
796
+ # Ruby/ActiveLDAP business afterward.
797
+ #
798
+ #
799
+ # ==== Reusing LDAP::Entry objects without reusing the LDAP connection
800
+ #
801
+ # You can call Klass.new(entry) where Klass is some subclass of Base and
802
+ # enty is an LDAP::entry. This use of 'new' is is meant for use from
803
+ # within find_all and find, but you can also use it in tandem with advanced
804
+ # LDAP queries.
805
+ #
806
+ # See tests/benchmark for more insight.
807
+ #
808
+ # ==== Juggling multiple LDAP connections
809
+ #
810
+ # In the same vein as the last tip, you can use multiple LDAP connections by
811
+ # saving old connections and then resetting them as follows:
812
+ #
813
+ # irb> Base.connect()
814
+ # => ...
815
+ # irb> anon_conn = Base.connection
816
+ # => ...
817
+ # irb> Base.connect(password_block => {'mypass'})
818
+ # => ...
819
+ # irb> auth_conn = Base.connection
820
+ # => ...
821
+ # irb> Base.connection = anon_conn
822
+ # ...
823
+ #
824
+ # This can be useful for doing authentication tests and other such tricks.
825
+ #
826
+ # ==== :try_sasl
827
+ #
828
+ # If you have the Ruby/LDAP package with the SASL/GSSAPI patch from Ian
829
+ # MacDonald's web site, you can use Kerberos to bind to your LDAP server. By
830
+ # default, :try_sasl is false.
831
+ #
832
+ # Also note that you must be using OpenLDAP 2.1.29 or higher to use SASL/GSSAPI
833
+ # due to some bugs in older versions of OpenLDAP.
834
+ #
835
+ # ==== Don't be afraid! [Internals]
836
+ #
837
+ # Don't be afraid to add more methods to the extensions classes and to
838
+ # experiment. That's exactly how I ended up with this package. If you come up
839
+ # with something cool, please share it!
840
+ #
841
+ # The internal structure of ActiveLDAP::Base, and thus all its subclasses, is
842
+ # still in flux. I've tried to minimize the changes to the overall API, but
843
+ # the internals are still rough around the edges.
844
+ #
845
+ # ===== Where's ldap_mapping data stored? How can I get to it?
846
+ #
847
+ # When you call ldap_mapping, it overwrites several class methods inherited
848
+ # from Base:
849
+ # * Base.base()
850
+ # * Base.required_classes()
851
+ # * Base.dnattr()
852
+ # You can access these from custom class methods by calling MyClass.base(),
853
+ # or whatever. There are predefined instance methods for getting to these
854
+ # from any new instance methods you define:
855
+ # * Base#base()
856
+ # * Base#required_classes()
857
+ # * Base#dnattr()
858
+ #
859
+ # ===== What else?
860
+ #
861
+ # Well if you want to use the LDAP connection for anything, I'd suggest still
862
+ # calling Base.connection to get it. There really aren't many other internals
863
+ # that need to be worried about. You could rebind with Base.do_bind, and get
864
+ # the LDAP schema with Base.schema.
865
+ #
866
+ # The only other useful tricks are dereferencing and accessing the stored
867
+ # data. Since LDAP attributes can have multiple names, e.g. cn or commonName,
868
+ # any methods you write might need to figure it out. I'd suggest just
869
+ # calling self.[attribname] to get the value, but if that's not good enough,
870
+ # you can call look up the stored name in the @attr_method Array as follows:
871
+ # irb> @attr_method['commonName']
872
+ # => 'cn'
873
+ #
874
+ # This tells you the name the attribute is stored in behind the scenes (@data).
875
+ # Again, self.[attribname] should be enough for most extensions, but if not,
876
+ # it's probably safe to dabble here.
877
+ #
878
+ # Also, if you like to look up all aliases for an attribute, you can call the
879
+ # following:
880
+ #
881
+ # irb> attribute_aliases('cn')
882
+ # => ['cn','commonName']
883
+ #
884
+ # This is discovered automagically from the LDAP server's schema.
885
+ #
886
+ # == Limitations
887
+ #
888
+ # === Speed
889
+ #
890
+ # Currently, Ruby/ActiveLDAP could be faster. I have some recursive type
891
+ # checking going on which slows object creation down, and I'm sure there
892
+ # are many, many other places optimizations can be done. Feel free
893
+ # to send patches, or just hang in there until I can optimize away the
894
+ # slowness.
895
+ #
896
+ # == Feedback
897
+ #
898
+ # Any and all feedback and patches are welcome. I am very excited about this
899
+ # package, and I'd like to see it prove helpful to more people than just myself.
900
+ #
901
+
902
+
903
+
904
+ require 'activeldap/base'
905
+ require 'activeldap/associations'
906
+ require 'activeldap/configuration'
907
+ require 'activeldap/schema2'
908
+
909
+ module ActiveLDAP
910
+ VERSION = "0.5.5"
911
+ end
912
+
913
+ ActiveLDAP::Base.class_eval do
914
+ include ActiveLDAP::Configuration
915
+ include ActiveLDAP::Associations
916
+ end