removed files that now live in lib/net.
This commit is contained in:
parent
fbacb5f8e0
commit
33e248cdc3
4 changed files with 0 additions and 1007 deletions
154
lib/ldappdu.rb
154
lib/ldappdu.rb
|
@ -1,154 +0,0 @@
|
||||||
# $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
|
|
||||||
|
|
||||||
|
|
282
lib/netber.rb
282
lib/netber.rb
|
@ -1,282 +0,0 @@
|
||||||
# $Id$
|
|
||||||
#
|
|
||||||
# NET::BER
|
|
||||||
# Mixes ASN.1/BER convenience methods into several standard classes.
|
|
||||||
# Also provides BER parsing functionality.
|
|
||||||
#
|
|
||||||
#----------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
module BER
|
|
||||||
|
|
||||||
class BerError < Exception; end
|
|
||||||
|
|
||||||
|
|
||||||
# This module is for mixing into IO and IO-like objects.
|
|
||||||
module BERParser
|
|
||||||
|
|
||||||
# The order of these follows the class-codes in BER.
|
|
||||||
# Maybe this should have been a hash.
|
|
||||||
TagClasses = [:universal, :application, :context_specific, :private]
|
|
||||||
|
|
||||||
BuiltinSyntax = {
|
|
||||||
:universal => {
|
|
||||||
:primitive => {
|
|
||||||
1 => :boolean,
|
|
||||||
2 => :integer,
|
|
||||||
4 => :string,
|
|
||||||
10 => :integer,
|
|
||||||
},
|
|
||||||
:constructed => {
|
|
||||||
16 => :array,
|
|
||||||
17 => :array
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# read_ber
|
|
||||||
# TODO: clean this up so it works properly with partial
|
|
||||||
# packets coming from streams that don't block when
|
|
||||||
# we ask for more data (like StringIOs). At it is,
|
|
||||||
# this can throw TypeErrors and other nasties.
|
|
||||||
#
|
|
||||||
def read_ber syntax=nil
|
|
||||||
eof? and return nil
|
|
||||||
|
|
||||||
id = getc # don't trash this value, we'll use it later
|
|
||||||
tag = id & 31
|
|
||||||
tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
|
|
||||||
tagclass = TagClasses[ id >> 6 ]
|
|
||||||
encoding = (id & 0x20 != 0) ? :constructed : :primitive
|
|
||||||
|
|
||||||
n = getc
|
|
||||||
lengthlength,contentlength = if n <= 127
|
|
||||||
[1,n]
|
|
||||||
else
|
|
||||||
j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
|
|
||||||
[1 + (n & 127), j]
|
|
||||||
end
|
|
||||||
|
|
||||||
newobj = read contentlength
|
|
||||||
|
|
||||||
objtype = nil
|
|
||||||
[syntax, BuiltinSyntax].each {|syn|
|
|
||||||
if syn && (ot = syn[tagclass]) && (ot = ot[encoding]) && ot[tag]
|
|
||||||
objtype = ot[tag]
|
|
||||||
break
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = case objtype
|
|
||||||
when :boolean
|
|
||||||
newobj != "\000"
|
|
||||||
when :string
|
|
||||||
(newobj || "").dup
|
|
||||||
when :integer
|
|
||||||
j = 0
|
|
||||||
newobj.each_byte {|b| j = (j << 8) + b}
|
|
||||||
j
|
|
||||||
when :array
|
|
||||||
seq = []
|
|
||||||
sio = StringIO.new( newobj || "" )
|
|
||||||
while e = sio.read_ber(syntax); seq << e; end
|
|
||||||
seq
|
|
||||||
else
|
|
||||||
raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add the identifier bits into the object if it's a String or an Array.
|
|
||||||
# We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
|
|
||||||
obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
|
|
||||||
obj
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end # module BERParser
|
|
||||||
end # module BER
|
|
||||||
|
|
||||||
end # module Net
|
|
||||||
|
|
||||||
|
|
||||||
class IO
|
|
||||||
include Net::BER::BERParser
|
|
||||||
end
|
|
||||||
|
|
||||||
require "stringio"
|
|
||||||
class StringIO
|
|
||||||
include Net::BER::BERParser
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
class String
|
|
||||||
def read_ber syntax=nil
|
|
||||||
StringIO.new(self).read_ber(syntax)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
class FalseClass
|
|
||||||
#
|
|
||||||
# to_ber
|
|
||||||
#
|
|
||||||
def to_ber
|
|
||||||
"\001\001\000"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
class TrueClass
|
|
||||||
#
|
|
||||||
# to_ber
|
|
||||||
#
|
|
||||||
def to_ber
|
|
||||||
"\001\001\001"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Fixnum
|
|
||||||
#
|
|
||||||
# to_ber
|
|
||||||
#
|
|
||||||
def to_ber
|
|
||||||
i = [self].pack('w')
|
|
||||||
[2, i.length].pack("CC") + i
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# to_ber_enumerated
|
|
||||||
#
|
|
||||||
def to_ber_enumerated
|
|
||||||
i = [self].pack('w')
|
|
||||||
[10, i.length].pack("CC") + i
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# to_ber_length_encoding
|
|
||||||
#
|
|
||||||
def to_ber_length_encoding
|
|
||||||
if self <= 127
|
|
||||||
[self].pack('C')
|
|
||||||
else
|
|
||||||
i = [self].pack('N').sub(/^[\0]+/,"")
|
|
||||||
[0x80 + i.length].pack('C') + i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end # class Fixnum
|
|
||||||
|
|
||||||
|
|
||||||
class Bignum
|
|
||||||
|
|
||||||
def to_ber
|
|
||||||
i = [self].pack('w')
|
|
||||||
i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" )
|
|
||||||
[2, i.length].pack("CC") + i
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class String
|
|
||||||
#
|
|
||||||
# to_ber
|
|
||||||
# A universal octet-string is tag number 4,
|
|
||||||
# but others are possible depending on the context, so we
|
|
||||||
# let the caller give us one.
|
|
||||||
#
|
|
||||||
def to_ber code = 4
|
|
||||||
[code].pack('C') + length.to_ber_length_encoding + self
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# to_ber_application_string
|
|
||||||
# TODO. WARNING, IS THIS WRONG? Shouldn't app-specific string
|
|
||||||
# have a prefix of 0x40?
|
|
||||||
#
|
|
||||||
def to_ber_application_string code
|
|
||||||
to_ber( 0x80 + code )
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# to_ber_contextspecific
|
|
||||||
#
|
|
||||||
def to_ber_contextspecific code
|
|
||||||
to_ber( 0x80 + code )
|
|
||||||
end
|
|
||||||
|
|
||||||
end # class String
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Array
|
|
||||||
#
|
|
||||||
# to_ber_appsequence
|
|
||||||
# An application-specific sequence usually gets assigned
|
|
||||||
# a tag that is meaningful to the particular protocol being used.
|
|
||||||
# This is different from the universal sequence, which usually
|
|
||||||
# gets a tag value of 16.
|
|
||||||
# Now here's an interesting thing: We're adding the X.690
|
|
||||||
# "application constructed" code at the top of the tag byte (0x60),
|
|
||||||
# but some clients, notably ldapsearch, send "context-specific
|
|
||||||
# constructed" (0xA0). The latter would appear to violate RFC-1777,
|
|
||||||
# but what do I know? We may need to change this.
|
|
||||||
#
|
|
||||||
|
|
||||||
def to_ber id = 0; to_ber_seq_internal( 0x30 + id ); end
|
|
||||||
def to_ber_set id = 0; to_ber_seq_internal( 0x31 + id ); end
|
|
||||||
def to_ber_sequence id = 0; to_ber_seq_internal( 0x30 + id ); end
|
|
||||||
def to_ber_appsequence id = 0; to_ber_seq_internal( 0x60 + id ); end
|
|
||||||
def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end
|
|
||||||
|
|
||||||
private
|
|
||||||
def to_ber_seq_internal code
|
|
||||||
s = self.to_s
|
|
||||||
[code].pack('C') + s.length.to_ber_length_encoding + s
|
|
||||||
end
|
|
||||||
|
|
||||||
end # class Array
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------
|
|
||||||
|
|
||||||
if __FILE__ == $0
|
|
||||||
puts "No default action"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
386
lib/netldap.rb
386
lib/netldap.rb
|
@ -1,386 +0,0 @@
|
||||||
# $Id$
|
|
||||||
#
|
|
||||||
# Net::LDAP for Ruby
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# == Miscellaneous
|
|
||||||
#
|
|
||||||
# For reasons relating to the source-code layout, this file doesn't
|
|
||||||
# require all the outboard stuff it actually needs, like netber.
|
|
||||||
# Until we figure out how to do that without damaging the directory
|
|
||||||
# structure, we're reliant on user programs to explicitly require
|
|
||||||
# everything, and in the correct order too!
|
|
||||||
#
|
|
||||||
# == BUGS:
|
|
||||||
#
|
|
||||||
# Try querying the objectGUID attribute from an A/D. It's a binary value
|
|
||||||
# which we're reading correctly, but we need to make sure it gets base64-encoded
|
|
||||||
# if we're going to put it out to an LDIF.
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
#require 'rubygems'
|
|
||||||
#require_gem "eventmachine", ">= 0.3.1"
|
|
||||||
|
|
||||||
require 'socket'
|
|
||||||
|
|
||||||
|
|
||||||
module Net
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# class LDAP
|
|
||||||
#
|
|
||||||
class LDAP
|
|
||||||
|
|
||||||
class LdapError < Exception; end
|
|
||||||
|
|
||||||
AsnSyntax = {
|
|
||||||
:application => {
|
|
||||||
:constructed => {
|
|
||||||
0 => :array, # BindRequest
|
|
||||||
1 => :array, # BindResponse
|
|
||||||
2 => :array, # UnbindRequest
|
|
||||||
3 => :array, # SearchRequest
|
|
||||||
4 => :array, # SearchData
|
|
||||||
5 => :array, # SearchResult
|
|
||||||
6 => :array, # ModifyRequest
|
|
||||||
7 => :array, # ModifyResponse
|
|
||||||
8 => :array, # AddRequest
|
|
||||||
9 => :array, # AddResponse
|
|
||||||
10 => :array, # DelRequest
|
|
||||||
11 => :array, # DelResponse
|
|
||||||
12 => :array, # ModifyRdnRequest
|
|
||||||
13 => :array, # ModifyRdnResponse
|
|
||||||
14 => :array, # CompareRequest
|
|
||||||
15 => :array, # CompareResponse
|
|
||||||
16 => :array, # AbandonRequest
|
|
||||||
}
|
|
||||||
},
|
|
||||||
:context_specific => {
|
|
||||||
:primitive => {
|
|
||||||
0 => :string, # password
|
|
||||||
1 => :string, # Kerberos v4
|
|
||||||
2 => :string, # Kerberos v5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultHost = "127.0.0.1"
|
|
||||||
DefaultPort = 389
|
|
||||||
DefaultAuth = {:method => :anonymous}
|
|
||||||
|
|
||||||
|
|
||||||
ResultStrings = {
|
|
||||||
0 => "Success",
|
|
||||||
1 => "Operations Error",
|
|
||||||
16 => "No Such Attribute",
|
|
||||||
17 => "Undefined Attribute Type",
|
|
||||||
20 => "Attribute or Value Exists",
|
|
||||||
32 => "No Such Object",
|
|
||||||
34 => "Invalid DN Syntax",
|
|
||||||
48 => "Invalid DN Syntax",
|
|
||||||
48 => "Inappropriate Authentication",
|
|
||||||
49 => "Invalid Credentials",
|
|
||||||
50 => "Insufficient Access Rights",
|
|
||||||
51 => "Busy",
|
|
||||||
52 => "Unavailable",
|
|
||||||
53 => "Unwilling to perform",
|
|
||||||
68 => "Entry Already Exists"
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# LDAP::result2string
|
|
||||||
#
|
|
||||||
def LDAP::result2string code
|
|
||||||
ResultStrings[code] || "unknown result (#{code})"
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# initialize
|
|
||||||
#
|
|
||||||
def initialize args
|
|
||||||
@host = args[:host] || DefaultHost
|
|
||||||
@port = args[:port] || DefaultPort
|
|
||||||
@verbose = false # Make this configurable with a switch on the class.
|
|
||||||
@auth = args[:auth] || DefaultAuth
|
|
||||||
|
|
||||||
# This variable is only set when we are created with LDAP::open.
|
|
||||||
# All of our internal methods will connect using it, or else
|
|
||||||
# they will create their own.
|
|
||||||
@open_connection = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# open
|
|
||||||
#
|
|
||||||
def LDAP::open
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# search
|
|
||||||
#
|
|
||||||
def search args
|
|
||||||
conn = Connection.new( :host => @host, :port => @port )
|
|
||||||
# TODO, hardcoded Ldap result code in next line
|
|
||||||
(rc = conn.bind @auth) == 0 or return rc
|
|
||||||
result_code = conn.search( args ) {|values|
|
|
||||||
block_given? and yield( values )
|
|
||||||
}
|
|
||||||
result_code
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# bind
|
|
||||||
# Bind and unbind.
|
|
||||||
# Can serve as a connectivity test as well as an auth test.
|
|
||||||
#
|
|
||||||
def bind
|
|
||||||
conn = Connection.new( :host => @host, :port => @port )
|
|
||||||
conn.bind @auth
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# bind_as
|
|
||||||
# This is for testing authentication credentials.
|
|
||||||
# Most likely a "standard" name (like a CN or an email
|
|
||||||
# address) will be presented along with a password.
|
|
||||||
# We'll bind with the main credential given in the
|
|
||||||
# constructor, query the full DN of the user given
|
|
||||||
# to us as a parameter, then unbind and rebind as the
|
|
||||||
# new user.
|
|
||||||
#
|
|
||||||
def bind_as
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# add
|
|
||||||
# Add a full RDN to the remote DIS.
|
|
||||||
#
|
|
||||||
def add args
|
|
||||||
conn = Connection.new( :host => @host, :port => @port )
|
|
||||||
# TODO, hardcoded Ldap result code in next line
|
|
||||||
(rc = conn.bind @auth) == 0 or return rc
|
|
||||||
conn.add( args )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# modify
|
|
||||||
# Modify the attributes of an entry on the remote DIS.
|
|
||||||
#
|
|
||||||
def modify args
|
|
||||||
conn = Connection.new( :host => @host, :port => @port )
|
|
||||||
# TODO, hardcoded Ldap result code in next line
|
|
||||||
(rc = conn.bind @auth) == 0 or return rc
|
|
||||||
conn.modify( args )
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# rename
|
|
||||||
# Rename an entry on the remote DIS by changing the last RDN of its DN.
|
|
||||||
#
|
|
||||||
def rename args
|
|
||||||
conn = Connection.new( :host => @host, :port => @port )
|
|
||||||
# TODO, hardcoded Ldap result code in next line
|
|
||||||
(rc = conn.bind @auth) == 0 or return rc
|
|
||||||
conn.rename( args )
|
|
||||||
end
|
|
||||||
|
|
||||||
end # class LDAP
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LDAP
|
|
||||||
class Connection
|
|
||||||
|
|
||||||
LdapVersion = 3
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# initialize
|
|
||||||
#
|
|
||||||
def initialize server
|
|
||||||
begin
|
|
||||||
@conn = TCPsocket.new( server[:host], server[:port] )
|
|
||||||
rescue
|
|
||||||
raise LdapError.new( "no connection to server" )
|
|
||||||
end
|
|
||||||
|
|
||||||
block_given? and yield self
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# next_msgid
|
|
||||||
#
|
|
||||||
def next_msgid
|
|
||||||
@msgid ||= 0
|
|
||||||
@msgid += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# bind
|
|
||||||
#
|
|
||||||
def bind auth
|
|
||||||
user,psw = case auth[:method]
|
|
||||||
when :anonymous
|
|
||||||
["",""]
|
|
||||||
when :simple
|
|
||||||
[auth[:username] || auth[:dn], auth[:password]]
|
|
||||||
end
|
|
||||||
raise LdapError.new( "invalid binding information" ) unless (user && psw)
|
|
||||||
|
|
||||||
msgid = next_msgid.to_ber
|
|
||||||
request = [LdapVersion.to_ber, user.to_ber, psw.to_ber_contextspecific(0)].to_ber_appsequence(0)
|
|
||||||
request_pkt = [msgid, request].to_ber_sequence
|
|
||||||
@conn.write request_pkt
|
|
||||||
|
|
||||||
(be = @conn.read_ber(AsnSyntax) and pdu = Net::LdapPdu.new( be )) or raise LdapError.new( "no bind result" )
|
|
||||||
pdu.result_code
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# search
|
|
||||||
# TODO, certain search parameters are hardcoded.
|
|
||||||
# TODO, if we mis-parse the server results or the results are wrong, we can block
|
|
||||||
# forever. That's because we keep reading results until we get a type-5 packet,
|
|
||||||
# which might never come. We need to support the time-limit in the protocol.
|
|
||||||
#
|
|
||||||
def search args
|
|
||||||
search_filter = (args && args[:filter]) || Filter.eq( "objectclass", "*" )
|
|
||||||
search_base = (args && args[:base]) || "dc=example,dc=com"
|
|
||||||
search_attributes = ((args && args[:attributes]) || []).map {|attr| attr.to_s.to_ber}
|
|
||||||
request = [
|
|
||||||
search_base.to_ber,
|
|
||||||
2.to_ber_enumerated,
|
|
||||||
0.to_ber_enumerated,
|
|
||||||
0.to_ber,
|
|
||||||
0.to_ber,
|
|
||||||
false.to_ber,
|
|
||||||
search_filter.to_ber,
|
|
||||||
search_attributes.to_ber_sequence
|
|
||||||
].to_ber_appsequence(3)
|
|
||||||
pkt = [next_msgid.to_ber, request].to_ber_sequence
|
|
||||||
@conn.write pkt
|
|
||||||
|
|
||||||
search_results = {}
|
|
||||||
result_code = 0
|
|
||||||
|
|
||||||
while (be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be ))
|
|
||||||
case pdu.app_tag
|
|
||||||
when 4 # search-data
|
|
||||||
search_results [pdu.search_dn] = pdu.search_attributes
|
|
||||||
when 5 # search-result
|
|
||||||
result_code = pdu.result_code
|
|
||||||
block_given? and yield( search_results )
|
|
||||||
break
|
|
||||||
else
|
|
||||||
raise LdapError.new( "invalid response-type in search: #{pdu.app_tag}" )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
result_code
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# modify
|
|
||||||
# TODO, need to support a time limit, in case the server fails to respond.
|
|
||||||
# TODO!!! We're throwing an exception here on empty DN.
|
|
||||||
# Should return a proper error instead, probaby from farther up the chain.
|
|
||||||
# TODO!!! If the user specifies a bogus opcode, we'll throw a
|
|
||||||
# confusing error here ("to_ber_enumerated is not defined on nil").
|
|
||||||
#
|
|
||||||
def modify args
|
|
||||||
modify_dn = args[:dn] or raise "Unable to modify empty DN"
|
|
||||||
modify_ops = []
|
|
||||||
a = args[:operations] and a.each {|op, attr, values|
|
|
||||||
# TODO, fix the following line, which gives a bogus error
|
|
||||||
# if the opcode is invalid.
|
|
||||||
op_1 = {:add => 0, :delete => 1, :replace => 2} [op.to_sym].to_ber_enumerated
|
|
||||||
modify_ops << [op_1, [attr.to_s.to_ber, values.to_a.map {|v| v.to_ber}.to_ber_set].to_ber_sequence].to_ber_sequence
|
|
||||||
}
|
|
||||||
|
|
||||||
request = [modify_dn.to_ber, modify_ops.to_ber_sequence].to_ber_appsequence(6)
|
|
||||||
pkt = [next_msgid.to_ber, request].to_ber_sequence
|
|
||||||
@conn.write pkt
|
|
||||||
|
|
||||||
(be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) && (pdu.app_tag == 7) or raise LdapError.new( "response missing or invalid" )
|
|
||||||
pdu.result_code
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# add
|
|
||||||
# TODO, need to support a time limit, in case the server fails to respond.
|
|
||||||
#
|
|
||||||
def add args
|
|
||||||
add_dn = args[:dn] or raise LdapError.new("Unable to add empty DN")
|
|
||||||
add_attrs = []
|
|
||||||
a = args[:attributes] and a.each {|k,v|
|
|
||||||
add_attrs << [ k.to_s.to_ber, v.to_a.map {|m| m.to_ber}.to_ber_set ].to_ber_sequence
|
|
||||||
}
|
|
||||||
|
|
||||||
request = [add_dn.to_ber, add_attrs.to_ber_sequence].to_ber_appsequence(8)
|
|
||||||
pkt = [next_msgid.to_ber, request].to_ber_sequence
|
|
||||||
@conn.write pkt
|
|
||||||
|
|
||||||
(be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) && (pdu.app_tag == 9) or raise LdapError.new( "response missing or invalid" )
|
|
||||||
pdu.result_code
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# rename
|
|
||||||
# TODO, need to support a time limit, in case the server fails to respond.
|
|
||||||
#
|
|
||||||
def rename args
|
|
||||||
old_dn = args[:olddn] or raise "Unable to rename empty DN"
|
|
||||||
new_rdn = args[:newrdn] or raise "Unable to rename to empty RDN"
|
|
||||||
delete_attrs = args[:delete_attributes] ? true : false
|
|
||||||
|
|
||||||
request = [old_dn.to_ber, new_rdn.to_ber, delete_attrs.to_ber].to_ber_appsequence(12)
|
|
||||||
pkt = [next_msgid.to_ber, request].to_ber_sequence
|
|
||||||
@conn.write pkt
|
|
||||||
|
|
||||||
(be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) && (pdu.app_tag == 13) or raise LdapError.new( "response missing or invalid" )
|
|
||||||
pdu.result_code
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end # class Connection
|
|
||||||
end # class LDAP
|
|
||||||
|
|
||||||
|
|
||||||
end # module Net
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------
|
|
||||||
|
|
||||||
if __FILE__ == $0
|
|
||||||
puts "No default action"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,185 +0,0 @@
|
||||||
# $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_application_string 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
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue