datacom_active_directory 1.5.5.datacom

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 886034b6e2fad40d5739b27a5076cc7212c73c3a
4
+ data.tar.gz: cbcdda9012834a738fb4589a43ecdc33d3dda089
5
+ SHA512:
6
+ metadata.gz: 3f6bea786c086e7b9de8dc9e8fd7e4aa240c7c6c781b752c38b19e22b8a5799fc75eac9681e3a8e71fc1ad2e6808e52bcefbc314bc6888b9669965a6bc6f03cf
7
+ data.tar.gz: 68cd96583ea6aadb305e0ff161666de0ba00ac471112b18bd5e1cee13f704a0943c2308dfd60f6419bb15b3d3b3d0502f3b59fdd94c413284b7348b7b083be04
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ html
2
+ pkg
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ **Forked on Datacom account in order to tag it to a fixed point - Brad Murray**
2
+
3
+ = Active Directory
4
+
5
+ Ruby Integration with Microsoft's Active Directory system based on original code by Justin Mecham and James Hunt at http://rubyforge.org/projects/activedirectory
6
+
7
+ See documentation on ActiveDirectory::Base for more information.
8
+
9
+ Caching:
10
+ Queries for membership and group membership are based on the distinguished name of objects. Doing a lot of queries, especially for a Rails app, is a sizable slowdown. To alleviate the problem, I've implemented a very basic cache for queries which search by :distinguishedname. This is disabled by default. All other queries are unaffected.
11
+
12
+
13
+ A code example is worth a thousand words:
14
+
15
+ <pre>
16
+ require 'rubygems'
17
+ require 'active_directory'
18
+
19
+ # Uses the same settings as net/ldap
20
+ settings = {
21
+ :host => 'domain-controller.example.local',
22
+ :base => 'dc=example,dc=local',
23
+ :port => 636,
24
+ :encryption => :simple_tls,
25
+ :auth => {
26
+ :method => :simple,
27
+ :username => "username",
28
+ :password => "password"
29
+ }
30
+ }
31
+
32
+ # Basic usage
33
+ ActiveDirectory::Base.setup(settings)
34
+
35
+ ActiveDirectory::User.find(:all)
36
+ ActiveDirectory::User.find(:first, :userprincipalname => "john.smith@domain.com")
37
+
38
+ ActiveDirectory::Group.find(:all)
39
+
40
+ #Caching is disabled by default, to enable:
41
+ ActiveDirectory::Base.enable_cache
42
+ ActiveDirectory::Base.disable_cache
43
+ ActiveDirectory::Base.cache?
44
+
45
+ </pre>
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require 'psych'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ gem.name = "active_directory"
7
+ gem.summary = "An interface library for accessing Microsoft's Active Directory."
8
+ gem.description = "ActiveDirectory uses Net::LDAP to provide a means of accessing and modifying an Active Directory data store. This is a fork of the activedirectory gem."
9
+ gem.author = "Adam T Kerr"
10
+ gem.email = "ajrkerr@gmail.com"
11
+ gem.homepage = "http://github.com/ajrkerr/active_directory"
12
+
13
+ # gem.files = FileList["lib/**/*.rb"]
14
+ # gem.require_path = "lib"
15
+ gem.add_dependency 'net-ldap', '>= 0.1.1'
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_directory/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'datacom_active_directory'
8
+ spec.version = ActiveDirectory::VERSION
9
+ spec.authors = ["Brad Murray"]
10
+ spec.email = ["wyaeld@gmail.com"]
11
+ spec.description = %q{ Datacom NZ fork of ActiveDirectory. Uses Net::LDAP to provide a means of accessing and modifying an Active Directory data store. This is a fork of the activedirectory gem.}
12
+ spec.summary = %q{An interface library for accessing Microsoft's Active Directory.}
13
+ spec.homepage = %q{http://github.com/datacom/active_directory}
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "datacom-net-ldap", '0.5.0.datacom'
21
+ end
22
+
@@ -0,0 +1,95 @@
1
+ #-- license
2
+ #
3
+ # Based on original code by Justin Mecham and James Hunt
4
+ # at http://rubyforge.org/projects/activedirectory
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+ #++ license
20
+
21
+ require 'net/ldap'
22
+
23
+ require 'active_directory/base.rb'
24
+ require 'active_directory/container.rb'
25
+ require 'active_directory/member.rb'
26
+
27
+ require 'active_directory/user.rb'
28
+ require 'active_directory/group.rb'
29
+ require 'active_directory/computer.rb'
30
+
31
+ require 'active_directory/field_type/password.rb'
32
+ require 'active_directory/field_type/binary.rb'
33
+ require 'active_directory/field_type/date.rb'
34
+ require 'active_directory/field_type/timestamp.rb'
35
+ require 'active_directory/field_type/dn_array.rb'
36
+ require 'active_directory/field_type/user_dn_array.rb'
37
+ require 'active_directory/field_type/group_dn_array.rb'
38
+ require 'active_directory/field_type/member_dn_array.rb'
39
+
40
+ module ActiveDirectory
41
+
42
+ #Special Fields
43
+ def self.special_fields
44
+ @@special_fields
45
+ end
46
+
47
+ def self.special_fields= sp_fields
48
+ @@special_fields = sp_fields
49
+ end
50
+
51
+ @@special_fields = {
52
+
53
+ #All objects in the AD
54
+ :Base => {
55
+ :objectguid => :Binary,
56
+ :whencreated => :Date,
57
+ :whenchanged => :Date,
58
+ :memberof => :DnArray,
59
+ },
60
+
61
+ #User objects
62
+ :User => {
63
+ :objectguid => :Binary,
64
+ :whencreated => :Date,
65
+ :whenchanged => :Date,
66
+ :objectsid => :Binary,
67
+ :msexchmailboxguid => :Binary,
68
+ :msexchmailboxsecuritydescriptor => :Binary,
69
+ :lastlogontimestamp => :Timestamp,
70
+ :pwdlastset => :Timestamp,
71
+ :accountexpires => :Timestamp,
72
+ :memberof => :MemberDnArray,
73
+ },
74
+
75
+ #Group objects
76
+ :Group => {
77
+ :objectguid => :Binary,
78
+ :whencreated => :Date,
79
+ :whenchanged => :Date,
80
+ :objectsid => :Binary,
81
+ :memberof => :GroupDnArray,
82
+ :member => :MemberDnArray,
83
+ },
84
+
85
+ #Computer objects
86
+ :Computer => {
87
+ :objectguid => :Binary,
88
+ :whencreated => :Date,
89
+ :whenchanged => :Date,
90
+ :objectsid => :Binary,
91
+ :memberof => :GroupDnArray,
92
+ :member => :MemberDnArray,
93
+ },
94
+ }
95
+ end
@@ -0,0 +1,587 @@
1
+ #-- license
2
+ #
3
+ # Based on original code by Justin Mecham and James Hunt
4
+ # at http://rubyforge.org/projects/activedirectory
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ #
19
+ #++ license
20
+
21
+ module ActiveDirectory
22
+ #
23
+ # Base class for all Ruby/ActiveDirectory Entry Objects (like User and Group)
24
+ #
25
+ class Base
26
+ #
27
+ # A Net::LDAP::Filter object that doesn't do any filtering
28
+ # (outside of check that the CN attribute is present. This
29
+ # is used internally for specifying a 'no filter' condition
30
+ # for methods that require a filter object.
31
+ #
32
+ NIL_FILTER = Net::LDAP::Filter.pres('cn')
33
+
34
+ @@ldap = nil
35
+ @@ldap_connected = false
36
+ @@caching = false
37
+ @@cache = {}
38
+
39
+ #
40
+ # Configures the connection for the Ruby/ActiveDirectory library.
41
+ #
42
+ # For example:
43
+ #
44
+ # ActiveDirectory::Base.setup(
45
+ # :host => 'domain_controller1.example.org',
46
+ # :port => 389,
47
+ # :base => 'dc=example,dc=org',
48
+ # :auth => {
49
+ # :method => :simple,
50
+ # :username => 'querying_user@example.org',
51
+ # :password => 'querying_users_password'
52
+ # }
53
+ # )
54
+ #
55
+ # This will configure Ruby/ActiveDirectory to connect to the domain
56
+ # controller at domain_controller1.example.org, using port 389. The
57
+ # domain's base LDAP dn is expected to be 'dc=example,dc=org', and
58
+ # Ruby/ActiveDirectory will try to bind as the
59
+ # querying_user@example.org user, using the supplied password.
60
+ #
61
+ # Currently, there can be only one active connection per
62
+ # execution context.
63
+ #
64
+ # For more advanced options, refer to the Net::LDAP.new
65
+ # documentation.
66
+ #
67
+ def self.setup(settings)
68
+ @@settings = settings
69
+ @@ldap_connected = false
70
+ @@ldap = Net::LDAP.new(settings)
71
+ end
72
+
73
+ def self.error
74
+ "#{@@ldap.get_operation_result.code}: #{@@ldap.get_operation_result.message}"
75
+ end
76
+
77
+ ##
78
+ # Return the last errorcode that ldap generated
79
+ def self.error_code
80
+ @@ldap.get_operation_result.code
81
+ end
82
+
83
+ ##
84
+ # Check to see if the last query produced an error
85
+ # Note: Invalid username/password combinations will not
86
+ # produce errors
87
+ def self.error?
88
+ @@ldap.nil? ? false : @@ldap.get_operation_result.code != 0
89
+ end
90
+
91
+ ##
92
+ # Check to see if we are connected to the LDAP server
93
+ # This method will try to connect, if we haven't already
94
+ def self.connected?
95
+ begin
96
+ @@ldap_connected ||= @@ldap.bind unless @@ldap.nil?
97
+ @@ldap_connected
98
+ rescue Net::LDAP::LdapError => e
99
+ false
100
+ end
101
+ end
102
+
103
+ ##
104
+ # Check to see if result caching is enabled
105
+ def self.cache?
106
+ @@caching
107
+ end
108
+
109
+ ##
110
+ # Clears the cache
111
+ def self.clear_cache
112
+ @@cache = {}
113
+ end
114
+
115
+ ##
116
+ # Enable caching for queries against the DN only
117
+ # This is to prevent membership lookups from hitting the
118
+ # AD unnecessarilly
119
+ def self.enable_cache
120
+ @@caching = true
121
+ end
122
+
123
+ ##
124
+ # Disable caching
125
+ def self.disable_cache
126
+ @@caching = false
127
+ end
128
+
129
+ def self.filter # :nodoc:
130
+ NIL_FILTER
131
+ end
132
+
133
+ def self.required_attributes # :nodoc:
134
+ {}
135
+ end
136
+
137
+ #
138
+ # Check to see if any entries matching the passed criteria exists.
139
+ #
140
+ # Filters should be passed as a hash of
141
+ # attribute_name => expected_value, like:
142
+ #
143
+ # User.exists?(
144
+ # :sn => 'Hunt',
145
+ # :givenName => 'James'
146
+ # )
147
+ #
148
+ # which will return true if one or more User entries have an
149
+ # sn (surname) of exactly 'Hunt' and a givenName (first name)
150
+ # of exactly 'James'.
151
+ #
152
+ # Partial attribute matches are available. For instance,
153
+ #
154
+ # Group.exists?(
155
+ # :description => 'OldGroup_*'
156
+ # )
157
+ #
158
+ # would return true if there are any Group objects in
159
+ # Active Directory whose descriptions start with OldGroup_,
160
+ # like OldGroup_Reporting, or OldGroup_Admins.
161
+ #
162
+ # Note that the * wildcard matches zero or more characters,
163
+ # so the above query would also return true if a group named
164
+ # 'OldGroup_' exists.
165
+ #
166
+ def self.exists?(filter_as_hash)
167
+ criteria = make_filter_from_hash(filter_as_hash) & filter
168
+ (@@ldap.search(:filter => criteria).size > 0)
169
+ end
170
+
171
+ #
172
+ # Whether or not the entry has local changes that have not yet been
173
+ # replicated to the Active Directory server via a call to Base#save
174
+ #
175
+ def changed?
176
+ !@attributes.empty?
177
+ end
178
+
179
+ ##
180
+ # Makes a single filter from a given key and value
181
+ # It will try to encode an array if there is a process for it
182
+ # Otherwise, it will treat it as an or condition
183
+ def self.make_filter(key, value)
184
+ #Join arrays using OR condition
185
+ if value.is_a? Array
186
+ filter = ~NIL_FILTER
187
+
188
+ value.each do |v|
189
+ filter |= Net::LDAP::Filter.eq(key, encode_field(key, v).to_s)
190
+ end
191
+ else
192
+ filter = Net::LDAP::Filter.eq(key, encode_field(key, value).to_s)
193
+ end
194
+
195
+ return filter
196
+ end
197
+
198
+ def self.make_filter_from_hash(hash) # :nodoc:
199
+ return NIL_FILTER if hash.nil? || hash.empty?
200
+
201
+ filter = NIL_FILTER
202
+
203
+ hash.each do |key, value|
204
+ filter &= make_filter(key, value)
205
+ end
206
+
207
+ return filter
208
+ end
209
+
210
+ #
211
+ # Performs a search on the Active Directory store, with similar
212
+ # syntax to the Rails ActiveRecord#find method.
213
+ #
214
+ # The first argument passed should be
215
+ # either :first or :all, to indicate that we want only one
216
+ # (:first) or all (:all) results back from the resultant set.
217
+ #
218
+ # The second argument should be a hash of attribute_name =>
219
+ # expected_value pairs.
220
+ #
221
+ # User.find(:all, :sn => 'Hunt')
222
+ #
223
+ # would find all of the User objects in Active Directory that
224
+ # have a surname of exactly 'Hunt'. As with the Base.exists?
225
+ # method, partial searches are allowed.
226
+ #
227
+ # This method always returns an array if the caller specifies
228
+ # :all for the search e (first argument). If no results
229
+ # are found, the array will be empty.
230
+ #
231
+ # If you call find(:first, ...), you will either get an object
232
+ # (a User or a Group) back, or nil, if there were no entries
233
+ # matching your filter.
234
+ #
235
+ def self.find(*args)
236
+ return false unless connected?
237
+
238
+ options = {
239
+ :filter => (args[1].nil?) ? NIL_FILTER : args[1],
240
+ :in => ''
241
+ }
242
+
243
+ cached_results = find_cached_results(args[1])
244
+ return cached_results if cached_results or cached_results.nil?
245
+
246
+ options[:in] = [ options[:in].to_s, @@settings[:base] ].delete_if { |part| part.empty? }.join(",")
247
+
248
+ if options[:filter].is_a? Hash
249
+ options[:filter] = make_filter_from_hash(options[:filter])
250
+ end
251
+
252
+ options[:filter] = options[:filter] & filter unless self.filter == NIL_FILTER
253
+
254
+ if (args.first == :all)
255
+ find_all(options)
256
+ elsif (args.first == :first)
257
+ find_first(options)
258
+ else
259
+ raise ArgumentError, 'Invalid specifier (not :all, and not :first) passed to find()'
260
+ end
261
+ end
262
+
263
+ ##
264
+ # Searches the cache and returns the result
265
+ # Returns false on failure, nil on wrong object type
266
+ #
267
+ def self.find_cached_results(filters)
268
+ return false unless cache?
269
+
270
+ #Check to see if we're only looking for :distinguishedname
271
+ return false unless filters.is_a? Hash and filters.keys == [:distinguishedname]
272
+
273
+ #Find keys we're looking up
274
+ dns = filters[:distinguishedname]
275
+
276
+ if dns.kind_of? Array
277
+ result = []
278
+
279
+ dns.each do |dn|
280
+ entry = @@cache[dn]
281
+
282
+ #If the object isn't in the cache just run the query
283
+ return false if entry.nil?
284
+
285
+ #Only permit objects of the type we're looking for
286
+ result << entry if entry.kind_of? self
287
+ end
288
+
289
+ return result
290
+ else
291
+ return false unless @@cache.key? dns
292
+ return @@cache[dns] if @@cache[dns].is_a? self
293
+ end
294
+ end
295
+
296
+ def self.find_all(options)
297
+ results = []
298
+ ldap_objs = @@ldap.search(:filter => options[:filter], :base => options[:in]) || []
299
+
300
+ ldap_objs.each do |entry|
301
+ ad_obj = new(entry)
302
+ @@cache[entry.dn] = ad_obj unless ad_obj.instance_of? Base
303
+ results << ad_obj
304
+ end
305
+
306
+ results
307
+ end
308
+
309
+ def self.find_first(options)
310
+ ldap_result = @@ldap.search(:filter => options[:filter], :base => options[:in])
311
+ return nil if ldap_result.empty?
312
+
313
+ ad_obj = new(ldap_result[0])
314
+ @@cache[ad_obj.dn] = ad_obj unless ad_obj.instance_of? Base
315
+ return ad_obj
316
+ end
317
+
318
+ def self.method_missing(name, *args) # :nodoc:
319
+ name = name.to_s
320
+ if (name[0,5] == 'find_')
321
+ find_spec, attribute_spec = parse_finder_spec(name)
322
+ raise ArgumentError, "find: Wrong number of arguments (#{args.size} for #{attribute_spec.size})" unless args.size == attribute_spec.size
323
+ filters = {}
324
+ [attribute_spec,args].transpose.each { |pr| filters[pr[0]] = pr[1] }
325
+ find(find_spec, :filter => filters)
326
+ else
327
+ super name.to_sym, args
328
+ end
329
+ end
330
+
331
+ def self.parse_finder_spec(method_name) # :nodoc:
332
+ # FIXME: This is a prime candidate for a
333
+ # first-class object, FinderSpec
334
+
335
+ method_name = method_name.gsub(/^find_/,'').gsub(/^by_/,'first_by_')
336
+ find_spec, attribute_spec = *(method_name.split('_by_'))
337
+ find_spec = find_spec.to_sym
338
+ attribute_spec = attribute_spec.split('_and_').collect { |s| s.to_sym }
339
+
340
+ return find_spec, attribute_spec
341
+ end
342
+
343
+ def ==(other) # :nodoc:
344
+ return false if other.nil?
345
+ other[:objectguid] == get_attr(:objectguid)
346
+ end
347
+
348
+ #
349
+ # Returns true if this entry does not yet exist in Active Directory.
350
+ #
351
+ def new_record?
352
+ @entry.nil?
353
+ end
354
+
355
+ #
356
+ # Refreshes the attributes for the entry with updated data from the
357
+ # domain controller.
358
+ #
359
+ def reload
360
+ return false if new_record?
361
+
362
+ @entry = @@ldap.search(:filter => Net::LDAP::Filter.eq('distinguishedName',distinguishedName))[0]
363
+ return !@entry.nil?
364
+ end
365
+
366
+ #
367
+ # Updates a single attribute (name) with one or more values
368
+ # (value), by immediately contacting the Active Directory
369
+ # server and initiating the update remotely.
370
+ #
371
+ # Entries are always reloaded (via Base.reload) after calling
372
+ # this method.
373
+ #
374
+ def update_attribute(name, value)
375
+ update_attributes(name.to_s => value)
376
+ end
377
+
378
+ #
379
+ # Updates multiple attributes, like ActiveRecord#update_attributes.
380
+ # The updates are immediately sent to the server for processing,
381
+ # and the entry is reloaded after the update (if all went well).
382
+ #
383
+ def update_attributes(attributes_to_update)
384
+ return true if attributes_to_update.empty?
385
+
386
+ operations = []
387
+ attributes_to_update.each do |attribute, values|
388
+ if values.nil? || values.empty?
389
+ operations << [ :delete, attribute, nil ]
390
+ else
391
+ values = [values] unless values.is_a? Array
392
+ values = values.collect { |v| v.to_s }
393
+
394
+ current_value = begin
395
+ @entry[attribute]
396
+ rescue NoMethodError
397
+ nil
398
+ end
399
+
400
+ operations << [ (current_value.nil? ? :add : :replace), attribute, values ]
401
+ end
402
+ end
403
+
404
+ @@ldap.modify(
405
+ :dn => distinguishedName,
406
+ :operations => operations
407
+ ) && reload
408
+ end
409
+
410
+ #
411
+ # Create a new entry in the Active Record store.
412
+ #
413
+ # dn is the Distinguished Name for the new entry. This must be
414
+ # a unique identifier, and can be passed as either a Container
415
+ # or a plain string.
416
+ #
417
+ # attributes is a symbol-keyed hash of attribute_name => value
418
+ # pairs.
419
+ #
420
+ def self.create(dn,attributes)
421
+ return nil if dn.nil? || attributes.nil?
422
+ begin
423
+ attributes.merge!(required_attributes)
424
+ if @@ldap.add(:dn => dn.to_s, :attributes => attributes)
425
+ return find_by_distinguishedName(dn.to_s)
426
+ else
427
+ return nil
428
+ end
429
+ rescue
430
+ return nil
431
+ end
432
+ end
433
+
434
+ #
435
+ # Deletes the entry from the Active Record store and returns true
436
+ # if the operation was successfully.
437
+ #
438
+ def destroy
439
+ return false if new_record?
440
+
441
+ if @@ldap.delete(:dn => distinguishedName)
442
+ @entry = nil
443
+ @attributes = {}
444
+ return true
445
+ else
446
+ return false
447
+ end
448
+ end
449
+
450
+ #
451
+ # Saves any pending changes to the entry by updating the remote
452
+ # entry.
453
+ #
454
+ def save
455
+ if update_attributes(@attributes)
456
+ @attributes = {}
457
+ return true
458
+ else
459
+ return false
460
+ end
461
+ end
462
+
463
+ #
464
+ # This method may one day provide the ability to move entries from
465
+ # container to container. Currently, it does nothing, as we are
466
+ # waiting on the Net::LDAP folks to either document the
467
+ # Net::LDAP#modrdn method, or provide a similar method for
468
+ # moving / renaming LDAP entries.
469
+ #
470
+ def move(new_rdn)
471
+ return false if new_record?
472
+ puts "Moving #{distinguishedName} to RDN: #{new_rdn}"
473
+
474
+ settings = @@settings.dup
475
+ settings[:port] = 636
476
+ settings[:encryption] = { :method => :simple_tls }
477
+
478
+ ldap = Net::LDAP.new(settings)
479
+
480
+ if ldap.rename(
481
+ :olddn => distinguishedName,
482
+ :newrdn => new_rdn,
483
+ :delete_attributes => false
484
+ )
485
+ return true
486
+ else
487
+ puts Base.error
488
+ return false
489
+ end
490
+ end
491
+
492
+ # FIXME: Need to document the Base::new
493
+ def initialize(attributes = {}) # :nodoc:
494
+ if attributes.is_a? Net::LDAP::Entry
495
+ @entry = attributes
496
+ @attributes = {}
497
+ else
498
+ @entry = nil
499
+ @attributes = attributes
500
+ end
501
+ end
502
+
503
+ ##
504
+ # Pull the class we're in
505
+ # This isn't quite right, as extending the object does funny things to how we
506
+ # lookup objects
507
+ def self.class_name
508
+ @klass ||= (self.name.include?('::') ? self.name[/.*::(.*)/, 1] : self.name)
509
+ end
510
+
511
+ ##
512
+ # Grabs the field type depending on the class it is called from
513
+ # Takes the field name as a parameter
514
+ def self.get_field_type(name)
515
+ #Extract class name
516
+ throw "Invalid field name" if name.nil?
517
+ type = ::ActiveDirectory.special_fields[class_name.to_sym][name.to_s.downcase.to_sym]
518
+ type.to_s unless type.nil?
519
+ end
520
+
521
+ @types = {}
522
+
523
+ def self.decode_field(name, value) # :nodoc:
524
+ type = get_field_type name
525
+ if !type.nil? and ::ActiveDirectory::FieldType::const_defined? type
526
+ return ::ActiveDirectory::FieldType::const_get(type).decode(value)
527
+ end
528
+ return value
529
+ end
530
+
531
+ def self.encode_field(name, value) # :nodoc:
532
+ type = get_field_type name
533
+ if !type.nil? and ::ActiveDirectory::FieldType::const_defined? type
534
+ return ::ActiveDirectory::FieldType::const_get(type).encode(value)
535
+ end
536
+ return value
537
+ end
538
+
539
+ def valid_attribute? name
540
+ @attributes.has_key?(name) || @entry.attribute_names.include?(name)
541
+ end
542
+
543
+ def get_attr(name)
544
+ name = name.to_s.downcase
545
+
546
+ return decode_field(name, @attributes[name.to_sym]) if @attributes.has_key?(name.to_sym)
547
+
548
+ if @entry.attribute_names.include? name.to_sym
549
+ value = @entry[name.to_sym]
550
+ value = value.first if value.kind_of?(Array) && value.size == 1
551
+ value = value.to_s if value.nil? || value.size == 1
552
+ value = nil.to_s if value.empty?
553
+ return self.class.decode_field(name, value)
554
+ end
555
+ end
556
+
557
+ def set_attr(name, value)
558
+ @attributes[name.to_sym] = encode_field(name, value)
559
+ end
560
+
561
+ ##
562
+ # Reads the array of values for the provided attribute. The attribute name
563
+ # is canonicalized prior to reading. Returns an empty array if the
564
+ # attribute does not exist.
565
+ alias [] get_attr
566
+ alias []= set_attr
567
+
568
+ ##
569
+ # Weird fluke with flattening, probably because of above attribute
570
+ def to_ary
571
+ end
572
+
573
+
574
+ def method_missing(name, args = []) # :nodoc:
575
+ name = name.to_s.downcase
576
+
577
+ return set_attr(name.chop, args) if name[-1] == '='
578
+
579
+ if valid_attribute? name.to_sym
580
+ get_attr(name)
581
+ else
582
+ super
583
+ end
584
+ end
585
+
586
+ end
587
+ end