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.
- data/test/test_adapter.rb +17 -0
- data/test/test_associations.rb +19 -0
- data/test/test_attributes.rb +2 -1
- data/test/test_base.rb +28 -1
- data/test/test_base_per_instance.rb +2 -1
- data/test/test_callback.rb +2 -2
- data/test/test_connection.rb +2 -1
- data/test/test_connection_per_dn.rb +81 -0
- data/test/test_dn.rb +3 -2
- data/test/test_find.rb +35 -1
- data/test/test_object_class.rb +12 -1
- data/test/test_reflection.rb +16 -10
- data/test/test_schema.rb +141 -2
- data/test/test_user.rb +14 -4
- metadata +7 -104
- data/CHANGES +0 -397
- data/COPYING +0 -340
- data/LICENSE +0 -58
- data/Manifest.txt +0 -99
- data/README +0 -85
- data/Rakefile +0 -70
- data/TODO +0 -23
- data/benchmark/bench-al.rb +0 -152
- data/examples/config.yaml.example +0 -5
- data/examples/example.der +0 -0
- data/examples/example.jpg +0 -0
- data/examples/groupadd +0 -41
- data/examples/groupdel +0 -35
- data/examples/groupls +0 -49
- data/examples/groupmod +0 -42
- data/examples/lpasswd +0 -55
- data/examples/objects/group.rb +0 -13
- data/examples/objects/ou.rb +0 -4
- data/examples/objects/user.rb +0 -20
- data/examples/ouadd +0 -38
- data/examples/useradd +0 -45
- data/examples/useradd-binary +0 -50
- data/examples/userdel +0 -34
- data/examples/userls +0 -50
- data/examples/usermod +0 -42
- data/examples/usermod-binary-add +0 -47
- data/examples/usermod-binary-add-time +0 -51
- data/examples/usermod-binary-del +0 -48
- data/examples/usermod-lang-add +0 -43
- data/lib/active_ldap.rb +0 -964
- data/lib/active_ldap/adapter/base.rb +0 -461
- data/lib/active_ldap/adapter/ldap.rb +0 -232
- data/lib/active_ldap/adapter/ldap_ext.rb +0 -69
- data/lib/active_ldap/adapter/net_ldap.rb +0 -288
- data/lib/active_ldap/adapter/net_ldap_ext.rb +0 -29
- data/lib/active_ldap/association/belongs_to.rb +0 -40
- data/lib/active_ldap/association/belongs_to_many.rb +0 -39
- data/lib/active_ldap/association/collection.rb +0 -80
- data/lib/active_ldap/association/has_many.rb +0 -40
- data/lib/active_ldap/association/has_many_wrap.rb +0 -55
- data/lib/active_ldap/association/proxy.rb +0 -89
- data/lib/active_ldap/associations.rb +0 -162
- data/lib/active_ldap/attributes.rb +0 -203
- data/lib/active_ldap/base.rb +0 -1510
- data/lib/active_ldap/callbacks.rb +0 -19
- data/lib/active_ldap/command.rb +0 -46
- data/lib/active_ldap/configuration.rb +0 -106
- data/lib/active_ldap/connection.rb +0 -142
- data/lib/active_ldap/distinguished_name.rb +0 -246
- data/lib/active_ldap/ldap_error.rb +0 -74
- data/lib/active_ldap/object_class.rb +0 -74
- data/lib/active_ldap/schema.rb +0 -299
- data/lib/active_ldap/timeout.rb +0 -75
- data/lib/active_ldap/timeout_stub.rb +0 -17
- data/lib/active_ldap/user_password.rb +0 -92
- data/lib/active_ldap/validations.rb +0 -76
- data/rails/plugin/active_ldap/README +0 -54
- data/rails/plugin/active_ldap/generators/scaffold_al/scaffold_al_generator.rb +0 -7
- data/rails/plugin/active_ldap/generators/scaffold_al/templates/ldap.yml +0 -21
- data/rails/plugin/active_ldap/init.rb +0 -12
- data/test/TODO +0 -2
- data/test/al-test-utils.rb +0 -381
- data/test/command.rb +0 -62
- data/test/config.yaml.sample +0 -6
- data/test/run-test.rb +0 -29
- data/test/test-unit-ext.rb +0 -2
- data/test/test-unit-ext/always-show-result.rb +0 -28
- 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
|
data/examples/usermod-binary-del
DELETED
@@ -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
|
data/examples/usermod-lang-add
DELETED
@@ -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
|