Refactored ldap/pdu and ldap/filter scripts
This commit is contained in:
parent
a25e74e1e3
commit
4c9532f432
6 changed files with 12 additions and 9 deletions
185
lib/net/ldap/filter.rb
Normal file
185
lib/net/ldap/filter.rb
Normal file
|
@ -0,0 +1,185 @@
|
|||
# $Id$
|
||||
#
|
||||
#
|
||||
#----------------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
||||
#
|
||||
# Gmail: garbagecat10
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
#---------------------------------------------------------------------------
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
module Net
|
||||
class LDAP
|
||||
|
||||
class Filter
|
||||
|
||||
def initialize op, a, b
|
||||
@op = op
|
||||
@left = a
|
||||
@right = b
|
||||
end
|
||||
|
||||
def Filter::eq a, b; Filter.new :eq, a, b; end
|
||||
def Filter::ne a, b; Filter.new :ne, a, b; end
|
||||
def Filter::gt a, b; Filter.new :gt, a, b; end
|
||||
def Filter::lt a, b; Filter.new :lt, a, b; end
|
||||
def Filter::ge a, b; Filter.new :ge, a, b; end
|
||||
def Filter::le a, b; Filter.new :le, a, b; end
|
||||
|
||||
def & a; Filter.new :and, self, a; end
|
||||
def | a; Filter.new :or, self, a; end
|
||||
|
||||
# This operator can't be !, evidently. Try it.
|
||||
def ~@; Filter.new :not, self, nil; end
|
||||
|
||||
def to_s
|
||||
case @op
|
||||
when :ne
|
||||
"(!(#{@left}=#{@right}))"
|
||||
when :eq
|
||||
"(#{@left}=#{@right})"
|
||||
when :gt
|
||||
"#{@left}>#{@right}"
|
||||
when :lt
|
||||
"#{@left}<#{@right}"
|
||||
when :ge
|
||||
"#{@left}>=#{@right}"
|
||||
when :le
|
||||
"#{@left}<=#{@right}"
|
||||
when :and
|
||||
"(&(#{@left})(#{@right}))"
|
||||
when :or
|
||||
"(|(#{@left})(#{@right}))"
|
||||
when :not
|
||||
"(!(#{@left}))"
|
||||
else
|
||||
raise "invalid or unsupported operator in LDAP Filter"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# to_ber
|
||||
# Filter ::=
|
||||
# CHOICE {
|
||||
# and [0] SET OF Filter,
|
||||
# or [1] SET OF Filter,
|
||||
# not [2] Filter,
|
||||
# equalityMatch [3] AttributeValueAssertion,
|
||||
# substrings [4] SubstringFilter,
|
||||
# greaterOrEqual [5] AttributeValueAssertion,
|
||||
# lessOrEqual [6] AttributeValueAssertion,
|
||||
# present [7] AttributeType,
|
||||
# approxMatch [8] AttributeValueAssertion
|
||||
# }
|
||||
#
|
||||
# SubstringFilter
|
||||
# SEQUENCE {
|
||||
# type AttributeType,
|
||||
# SEQUENCE OF CHOICE {
|
||||
# initial [0] LDAPString,
|
||||
# any [1] LDAPString,
|
||||
# final [2] LDAPString
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# Parsing substrings is a little tricky.
|
||||
# We use the split method to break a string into substrings
|
||||
# delimited by the * (star) character. But we also need
|
||||
# to know whether there is a star at the head and tail
|
||||
# of the string. A Ruby particularity comes into play here:
|
||||
# if you split on * and the first character of the string is
|
||||
# a star, then split will return an array whose first element
|
||||
# is an _empty_ string. But if the _last_ character of the
|
||||
# string is star, then split will return an array that does
|
||||
# _not_ add an empty string at the end. So we have to deal
|
||||
# with all that specifically.
|
||||
#
|
||||
def to_ber
|
||||
case @op
|
||||
when :eq
|
||||
if @right == "*" # present
|
||||
@left.to_ber_contextspecific 7
|
||||
elsif @right =~ /[\*]/ #substring
|
||||
ary = @right.split( /[\*]+/ )
|
||||
final_star = @right =~ /[\*]$/
|
||||
initial_star = ary.first == "" and ary.shift
|
||||
|
||||
seq = []
|
||||
unless initial_star
|
||||
seq << ary.shift.to_ber_contextspecific(0)
|
||||
end
|
||||
n_any_strings = ary.length - (final_star ? 0 : 1)
|
||||
p n_any_strings
|
||||
n_any_strings.times {
|
||||
seq << ary.shift.to_ber_contextspecific(1)
|
||||
}
|
||||
unless final_star
|
||||
seq << ary.shift.to_ber_contextspecific(2)
|
||||
end
|
||||
[@left.to_ber, seq.to_ber].to_ber_contextspecific 4
|
||||
else #equality
|
||||
[@left.to_ber, @right.to_ber].to_ber_contextspecific 3
|
||||
end
|
||||
when :and
|
||||
ary = [@left.coalesce(:and), @right.coalesce(:and)].flatten
|
||||
ary.map {|a| a.to_ber}.to_ber_contextspecific( 0 )
|
||||
when :or
|
||||
ary = [@left.coalesce(:or), @right.coalesce(:or)].flatten
|
||||
ary.map {|a| a.to_ber}.to_ber_contextspecific( 1 )
|
||||
when :not
|
||||
[@left.to_ber].to_ber_contextspecific 2
|
||||
else
|
||||
# ERROR, we'll return objectclass=* to keep things from blowing up,
|
||||
# but that ain't a good answer and we need to kick out an error of some kind.
|
||||
raise "unimplemented search filter"
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# coalesce
|
||||
# This is a private helper method for dealing with chains of ANDs and ORs
|
||||
# that are longer than two. If BOTH of our branches are of the specified
|
||||
# type of joining operator, then return both of them as an array (calling
|
||||
# coalesce recursively). If they're not, then return an array consisting
|
||||
# only of self.
|
||||
#
|
||||
def coalesce operator
|
||||
if @op == operator
|
||||
[@left.coalesce( operator ), @right.coalesce( operator )]
|
||||
else
|
||||
[self]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end # class Net::LDAP::Filter
|
||||
|
||||
end # class Net::LDAP
|
||||
end # module Net
|
||||
|
||||
|
||||
#-----------------------------------
|
||||
|
||||
if __FILE__ == $0
|
||||
puts "No default action"
|
||||
end
|
||||
|
154
lib/net/ldap/pdu.rb
Normal file
154
lib/net/ldap/pdu.rb
Normal file
|
@ -0,0 +1,154 @@
|
|||
# $Id$
|
||||
#
|
||||
# LDAP PDU support classes
|
||||
#
|
||||
#
|
||||
#----------------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
||||
#
|
||||
# Gmail: garbagecat10
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
#---------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
|
||||
|
||||
module Net
|
||||
|
||||
|
||||
class LdapPduError < Exception; end
|
||||
|
||||
|
||||
class LdapPdu
|
||||
|
||||
BindResult = 1
|
||||
SearchReturnedData = 4
|
||||
SearchResult = 5
|
||||
ModifyResponse = 7
|
||||
AddResponse = 9
|
||||
ModifyRDNResponse = 13
|
||||
|
||||
attr_reader :msg_id, :app_tag
|
||||
attr_reader :search_dn, :search_attributes
|
||||
|
||||
#
|
||||
# initialize
|
||||
# An LDAP PDU always looks like a BerSequence with
|
||||
# two elements: an integer (message-id number), and
|
||||
# an application-specific sequence.
|
||||
# The application-specific tag in the sequence tells
|
||||
# us what kind of packet it is, and each kind has its
|
||||
# own format, defined in RFC-1777.
|
||||
# Observe that many clients (such as ldapsearch)
|
||||
# do not necessarily enforce the expected application
|
||||
# tags on received protocol packets. This implementation
|
||||
# does interpret the RFC strictly in this regard, and
|
||||
# it remains to be seen whether there are servers out
|
||||
# there that will not work well with our approach.
|
||||
#
|
||||
def initialize ber_object
|
||||
begin
|
||||
@msg_id = ber_object[0].to_i
|
||||
@app_tag = ber_object[1].ber_identifier - 0x60
|
||||
rescue
|
||||
# any error becomes a data-format error
|
||||
raise LdapPduError.new( "ldap-pdu format error" )
|
||||
end
|
||||
|
||||
case @app_tag
|
||||
when BindResult
|
||||
parse_ldap_result ber_object[1]
|
||||
when SearchReturnedData
|
||||
parse_search_return ber_object[1]
|
||||
when SearchResult
|
||||
parse_ldap_result ber_object[1]
|
||||
when ModifyResponse
|
||||
parse_ldap_result ber_object[1]
|
||||
when AddResponse
|
||||
parse_ldap_result ber_object[1]
|
||||
when ModifyRDNResponse
|
||||
parse_ldap_result ber_object[1]
|
||||
else
|
||||
raise LdapPduError.new( "unknown pdu-type: #{@app_tag}" )
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# result_code
|
||||
# This returns an LDAP result code taken from the PDU,
|
||||
# but it will be nil if there wasn't a result code.
|
||||
# That can easily happen depending on the type of packet.
|
||||
#
|
||||
def result_code code = :resultCode
|
||||
@ldap_result and @ldap_result[code]
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
#
|
||||
# parse_ldap_result
|
||||
#
|
||||
def parse_ldap_result sequence
|
||||
sequence.length >= 3 or raise LdapPduError
|
||||
@ldap_result = {:resultCode => sequence[0], :matchedDN => sequence[1], :errorMessage => sequence[2]}
|
||||
end
|
||||
|
||||
#
|
||||
# parse_search_return
|
||||
# Definition from RFC 1777 (we're handling application-4 here)
|
||||
#
|
||||
# Search Response ::=
|
||||
# CHOICE {
|
||||
# entry [APPLICATION 4] SEQUENCE {
|
||||
# objectName LDAPDN,
|
||||
# attributes SEQUENCE OF SEQUENCE {
|
||||
# AttributeType,
|
||||
# SET OF AttributeValue
|
||||
# }
|
||||
# },
|
||||
# resultCode [APPLICATION 5] LDAPResult
|
||||
# }
|
||||
#
|
||||
# We concoct a search response that is a hash of the returned attribute values.
|
||||
# NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES.
|
||||
# This is to make them more predictable for user programs, but it
|
||||
# may not be a good idea. Maybe this should be configurable.
|
||||
#
|
||||
def parse_search_return sequence
|
||||
sequence.length >= 2 or raise LdapPduError
|
||||
@search_dn = sequence[0]
|
||||
@search_attributes = {}
|
||||
sequence[1].each {|seq|
|
||||
@search_attributes[seq[0].downcase.intern] = seq[1]
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end # module Net
|
||||
|
||||
#-------------------------------------------
|
||||
|
||||
if __FILE__ == $0
|
||||
puts "No default action for this file"
|
||||
end
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue