diff --git a/lib/net/ldap/connection.rb b/lib/net/ldap/connection.rb index 4fd55da4..88dece9c 100644 --- a/lib/net/ldap/connection.rb +++ b/lib/net/ldap/connection.rb @@ -289,21 +289,21 @@ def bind(auth) # # The format of the sort control needs to be: # - # :sort_control => ["cn"] # just a string + # :sort_controls => ["cn"] # just a string # or - # :sort_control => [["cn", "matchingRule", true]] #attribute, matchingRule, direction (true / false) + # :sort_controls => [["cn", "orderingRule", true]] #attribute, orderingRule, reverseOrder (true / false) # or - # :sort_control => ["givenname","sn"] #multiple strings or arrays + # :sort_controls => ["givenname","sn"] #multiple strings or arrays # def encode_sort_controls(sort_definitions) return sort_definitions unless sort_definitions sort_control_values = sort_definitions.map do |control| - control = Array(control) # if there is only an attribute name as a string then infer the orderinrule and reverseorder - control[0] = String(control[0]).to_ber, - control[1] = String(control[1]).to_ber, - control[2] = (control[2] == true).to_ber - control.to_ber_sequence + attribute, rule, reverse = Array(control) # a bare string is just the attribute name + key = [String(attribute).to_ber] + key << String(rule).to_ber_contextspecific(0) unless rule.nil? || rule.to_s.empty? + key << "\xFF".b.to_ber_contextspecific(1) if reverse == true + key.to_ber_sequence end [ Net::LDAP::LDAPControls::SORT_REQUEST.to_ber, diff --git a/test/test_ldap_connection.rb b/test/test_ldap_connection.rb index d5925a2a..859ca722 100644 --- a/test/test_ldap_connection.rb +++ b/test/test_ldap_connection.rb @@ -129,6 +129,55 @@ def test_modify_ops_replace assert_equal(expected, result) end + def sort_keys(control) + oid, criticality, value = control.read_ber + assert_equal(Net::LDAP::LDAPControls::SORT_REQUEST, oid) + assert_equal(false, criticality) + value.read_ber(Net::LDAP::AsnSyntax) + end + + def test_encode_sort_controls_attribute_only + conn = Net::LDAP::Connection.new + keys = sort_keys(conn.encode_sort_controls(["cn"])) + assert_equal([["cn"]], keys) + end + + def test_encode_sort_controls_rule_and_reverse + conn = Net::LDAP::Connection.new + control = conn.encode_sort_controls([["cn", "caseIgnoreOrderingMatch", true]]) + _oid, _criticality, value = control.read_ber + assert_equal("0\x22" \ + "0\x20" \ + "\x04\x02cn" \ + "\x80\x17caseIgnoreOrderingMatch" \ + "\x81\x01\xFF".b, value) + attribute, rule, reverse = sort_keys(control).first + assert_equal("cn", attribute) + assert_equal("caseIgnoreOrderingMatch", rule) + assert_equal(0x80, rule.ber_identifier) + assert_equal("\xFF".b, reverse) + assert_equal(0x81, reverse.ber_identifier) + end + + def test_encode_sort_controls_omits_defaults + conn = Net::LDAP::Connection.new + keys = sort_keys(conn.encode_sort_controls([["cn", nil, false]])) + assert_equal(["cn"], keys.first) + end + + def test_encode_sort_controls_multiple_keys + conn = Net::LDAP::Connection.new + keys = sort_keys(conn.encode_sort_controls(["givenname", ["sn", nil, true]])) + assert_equal(["givenname"], keys[0]) + assert_equal(["sn", "\xFF".b], keys[1]) + end + + def test_encode_sort_controls_passes_through_false + conn = Net::LDAP::Connection.new + assert_equal(false, conn.encode_sort_controls(false)) + assert_nil(conn.encode_sort_controls(nil)) + end + def test_write mock = flexmock("socket") mock.should_receive(:write).with([1.to_ber, "request"].to_ber_sequence).and_return(true)