Ooops, I added my code in the top level.
Provisionally added this branch to keep from messing things up.
This commit is contained in:
commit
eb3ab200f8
10 changed files with 1357 additions and 0 deletions
281
COPYING
Normal file
281
COPYING
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
6
README
Normal file
6
README
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
README stub for Net::LDAP
|
||||||
|
|
6
RELEASE_NOTES
Normal file
6
RELEASE_NOTES
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
Release notes for Net::LDAP.
|
||||||
|
|
33
Rakefile
Normal file
33
Rakefile
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# $Id$
|
||||||
|
# Rakefile for the netldap ruby gem.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
require 'rubygems'
|
||||||
|
Gem::manage_gems
|
||||||
|
require 'rake/gempackagetask'
|
||||||
|
|
||||||
|
em_version = "0.0.1"
|
||||||
|
|
||||||
|
spec = Gem::Specification.new {|s|
|
||||||
|
s.name = "netldap"
|
||||||
|
s.version = em_version
|
||||||
|
s.author = "Francis Cianfrocca"
|
||||||
|
s.email = "garbagecat10@gmail.com"
|
||||||
|
s.homepage = "netldap@rubyforge.org"
|
||||||
|
s.summary = "Net::LDAP library"
|
||||||
|
s.files = FileList["{bin,tests,lib}/**/*"].exclude("rdoc").to_a
|
||||||
|
s.require_paths = ["lib"]
|
||||||
|
s.autorequire = "netldap"
|
||||||
|
s.test_file = "tests/testem.rb"
|
||||||
|
s.has_rdoc = true
|
||||||
|
s.extra_rdoc_files = ["README", "RELEASE_NOTES", "COPYING"]
|
||||||
|
}
|
||||||
|
|
||||||
|
Rake::GemPackageTask.new( spec ) {|pkg|
|
||||||
|
pkg.need_tar = true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
task :default => ["pkg/netldap-#{em_version}.gem"]
|
||||||
|
|
148
lib/ldappdu.rb
Normal file
148
lib/ldappdu.rb
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
# $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.
|
||||||
|
#
|
||||||
|
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. Should make this somehow a configurable parameter.
|
||||||
|
#
|
||||||
|
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
|
||||||
|
|
||||||
|
|
265
lib/netber.rb
Normal file
265
lib/netber.rb
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
# $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: garbagecat20
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
TagClasses = [:universal, :application, :context_specific, :private]
|
||||||
|
|
||||||
|
# This module is for mixing into IO and IO-like objects.
|
||||||
|
module BERParser
|
||||||
|
|
||||||
|
BuiltinSyntax = {
|
||||||
|
:universal => {
|
||||||
|
1 => :boolean,
|
||||||
|
2 => :integer,
|
||||||
|
4 => :string,
|
||||||
|
10 => :integer,
|
||||||
|
16 => :array,
|
||||||
|
17 => :array,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# read_ber
|
||||||
|
#
|
||||||
|
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 ]
|
||||||
|
constructed = (id & 0x20 != 0)
|
||||||
|
|
||||||
|
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 = (ot = BuiltinSyntax[tagclass]) && ot[tag]
|
||||||
|
objtype = objtype || (syntax && (ot = syntax[tagclass]) && ot[tag])
|
||||||
|
obj = case objtype
|
||||||
|
when :boolean
|
||||||
|
raise BerError.new( "boolean unimplemented- fix this now, dummy" )
|
||||||
|
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}, 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
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
382
lib/netldap.rb
Normal file
382
lib/netldap.rb
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
# $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 => {
|
||||||
|
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 => {
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
185
lib/netldapfilter.rb
Normal file
185
lib/netldapfilter.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_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
|
||||||
|
|
43
tests/testber.rb
Normal file
43
tests/testber.rb
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
require 'lib/netber'
|
||||||
|
require 'lib/netldap'
|
||||||
|
require 'lib/ldappdu'
|
||||||
|
require 'lib/netldapfilter'
|
||||||
|
require 'stringio'
|
||||||
|
|
||||||
|
|
||||||
|
class TestBer < Test::Unit::TestCase
|
||||||
|
|
||||||
|
def setup
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: Add some much bigger numbers
|
||||||
|
# 5000000000 is a Bignum, which hits different code.
|
||||||
|
def test_ber_integers
|
||||||
|
assert_equal( "\002\001\005", 5.to_ber )
|
||||||
|
assert_equal( "\002\002\203t", 500.to_ber )
|
||||||
|
assert_equal( "\002\003\203\206P", 50000.to_ber )
|
||||||
|
assert_equal( "\002\005\222\320\227\344\000", 5000000000.to_ber )
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ber_parsing
|
||||||
|
assert_equal( 6, "\002\001\006".read_ber( Net::LDAP::AsnSyntax ))
|
||||||
|
assert_equal( "testing", "\004\007testing".read_ber( Net::LDAP::AsnSyntax ))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def test_ber_parser_on_ldap_bind_request
|
||||||
|
s = StringIO.new "0$\002\001\001`\037\002\001\003\004\rAdministrator\200\vad_is_bogus"
|
||||||
|
assert_equal( [1, [3, "Administrator", "ad_is_bogus"]], s.read_ber( Net::LDAP::AsnSyntax ))
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
8
tests/testem.rb
Normal file
8
tests/testem.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
require 'test/unit'
|
||||||
|
require 'tests/testber'
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue