Skip to content

Commit 528ef30

Browse files
committed
Merge pull request #1 from partnerpedia/add_delete_tree
added the ability to do a delete_tree
2 parents 6bb9fa6 + 58bd212 commit 528ef30

File tree

6 files changed

+70
-11
lines changed

6 files changed

+70
-11
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ publish/
99
coverage/
1010
coverage.info
1111
.rake_tasks~
12+
Gemfile.lock

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
source :rubygems
2+
gemspec

lib/net/ber/core_ext/array.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,18 @@ def to_ber_oid
7979
oid = ary.pack("w*")
8080
[6, oid.length].pack("CC") + oid
8181
end
82+
83+
##
84+
# Converts an array into a set of ber control codes
85+
# The expected format is [[control_oid, criticality, control_value(optional)]]
86+
# [['1.2.840.113556.1.4.805',true]]
87+
#
88+
def to_ber_control
89+
#if our array does not contain at least one array then wrap it in an array before going forward
90+
ary = self[0].kind_of?(Array) ? self : [self]
91+
ary = ary.collect do |control_sequence|
92+
control_sequence.collect{|element| element.to_ber}.to_ber_sequence.reject_empty_ber_arrays
93+
end
94+
ary.to_ber_sequence.reject_empty_ber_arrays
95+
end
8296
end

lib/net/ber/core_ext/string.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,19 @@ def to_ber_contextspecific(code)
4646
def read_ber(syntax = nil)
4747
StringIO.new(self).read_ber(syntax)
4848
end
49-
49+
5050
##
51-
# Destructively reads a BER object from the string.
51+
# Destructively reads a BER object from the string.
5252
def read_ber!(syntax = nil)
5353
io = StringIO.new(self)
5454

5555
result = io.read_ber(syntax)
5656
self.slice!(0...io.pos)
57-
57+
5858
return result
5959
end
60+
61+
def reject_empty_ber_arrays
62+
self.gsub(/0\000/n,'')
63+
end
6064
end

lib/net/ldap.rb

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,9 @@ class LdapError < StandardError; end
334334
68 => "Entry Already Exists"
335335
}
336336

337-
module LdapControls
338-
PagedResults = "1.2.840.113556.1.4.319" # Microsoft evil from RFC 2696
337+
module LDAPControls
338+
PAGED_RESULTS = "1.2.840.113556.1.4.319" # Microsoft evil from RFC 2696
339+
DELETE_TREE = "1.2.840.113556.1.4.805"
339340
end
340341

341342
def self.result2string(code) #:nodoc:
@@ -552,7 +553,7 @@ def open
552553
# anything with the bind results. We then pass self to the caller's
553554
# block, where he will execute his LDAP operations. Of course they will
554555
# all generate auth failures if the bind was unsuccessful.
555-
raise Net::LDAP::LdapError, "Open already in progress" if @open_connection
556+
raise LdapError, "Open already in progress" if @open_connection
556557

557558
begin
558559
@open_connection = Net::LDAP::Connection.new(:host => @host,
@@ -1022,6 +1023,19 @@ def delete(args)
10221023
@result == 0
10231024
end
10241025

1026+
# Delete an entry from the LDAP directory along with all subordinate entries.
1027+
# the regular delete method will fail to delete an entry if it has subordinate
1028+
# entries. This method sends an extra control code to tell the LDAP server
1029+
# to do a tree delete. ('1.2.840.113556.1.4.805')
1030+
#
1031+
# Returns True or False to indicate whether the delete succeeded. Extended
1032+
# status information is available by calling #get_operation_result.
1033+
#
1034+
# dn = "[email protected], ou=people, dc=example, dc=com"
1035+
# ldap.delete_tree :dn => dn
1036+
def delete_tree(args)
1037+
delete(args.merge(:control_codes => [[LDAPControls::DELETE_TREE, true]]))
1038+
end
10251039
# This method is experimental and subject to change. Return the rootDSE
10261040
# record from the LDAP server as a Net::LDAP::Entry, or an empty Entry if
10271041
# the server doesn't return the record.
@@ -1092,7 +1106,7 @@ def search_subschema_entry
10921106
#++
10931107
def paged_searches_supported?
10941108
@server_caps ||= search_root_dse
1095-
@server_caps[:supportedcontrol].include?(Net::LDAP::LdapControls::PagedResults)
1109+
@server_caps[:supportedcontrol].include?(LDAPControls::PAGED_RESULTS)
10961110
end
10971111
end # class LDAP
10981112

@@ -1389,7 +1403,7 @@ def search(args = {})
13891403
controls = []
13901404
controls <<
13911405
[
1392-
Net::LDAP::LdapControls::PagedResults.to_ber,
1406+
LDAPControls::PAGED_RESULTS.to_ber,
13931407
# Criticality MUST be false to interoperate with normal LDAPs.
13941408
false.to_ber,
13951409
rfc2696_cookie.map{ |v| v.to_ber}.to_ber_sequence.to_s.to_ber
@@ -1437,7 +1451,7 @@ def search(args = {})
14371451
more_pages = false
14381452
if result_code == 0 and controls
14391453
controls.each do |c|
1440-
if c.oid == Net::LDAP::LdapControls::PagedResults
1454+
if c.oid == LDAPControls::PAGED_RESULTS
14411455
# just in case some bogus server sends us more than 1 of these.
14421456
more_pages = false
14431457
if c.value and c.value.length > 0
@@ -1545,9 +1559,9 @@ def rename args
15451559
#++
15461560
def delete(args)
15471561
dn = args[:dn] or raise "Unable to delete empty DN"
1548-
1562+
controls = args.include?(:control_codes) ? args[:control_codes].to_ber_control : nil #use nil so we can compact later
15491563
request = dn.to_s.to_ber_application_string(10)
1550-
pkt = [next_msgid.to_ber, request].to_ber_sequence
1564+
pkt = [next_msgid.to_ber, request, controls].compact.to_ber_sequence
15511565
@conn.write pkt
15521566

15531567
(be = @conn.read_ber(Net::LDAP::AsnSyntax)) && (pdu = Net::LDAP::PDU.new(be)) && (pdu.app_tag == 11) or raise Net::LDAP::LdapError, "response missing or invalid"

spec/unit/ber/core_ext/array_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
require 'spec_helper'
2+
require 'metaid'
3+
4+
describe Array, "when extended with BER core extensions" do
5+
6+
it "should correctly convert a control code array" do
7+
control_codes = []
8+
control_codes << ['1.2.3'.to_ber, true.to_ber].to_ber_sequence
9+
control_codes << ['1.7.9'.to_ber, false.to_ber].to_ber_sequence
10+
control_codes = control_codes.to_ber_sequence
11+
res = [['1.2.3', true],['1.7.9',false]].to_ber_control
12+
res.should eq(control_codes)
13+
end
14+
15+
it "should wrap the array in another array if a nested array is not passed" do
16+
result1 = ['1.2.3', true].to_ber_control
17+
result2 = [['1.2.3', true]].to_ber_control
18+
result1.should eq(result2)
19+
end
20+
21+
it "should return an empty string if an empty array is passed" do
22+
[].to_ber_control.should be_empty
23+
end
24+
end

0 commit comments

Comments
 (0)