Commit 166d65b2 authored by Felix Schäfer's avatar Felix Schäfer

[#258] Unvendor net-ldap

parent 1322fbba
......@@ -18,6 +18,10 @@ group :test do
platforms :mri_19, :mingw_19 do gem 'ruby-debug19', :require => 'ruby-debug' end
end
group :ldap do
gem "net-ldap", '~> 0.2.2'
end
group :openid do
gem "ruby-openid", '~> 2.1.4', :require => 'openid'
end
......
This diff is collapsed.
= Net::LDAP Changelog
== Net::LDAP 0.0.4: August 15, 2006
* Undeprecated Net::LDAP#modify. Thanks to Justin Forder for
providing the rationale for this.
* Added a much-expanded set of special characters to the parser
for RFC-2254 filters. Thanks to Andre Nathan.
* Changed Net::LDAP#search so you can pass it a filter in string form.
The conversion to a Net::LDAP::Filter now happens automatically.
* Implemented Net::LDAP#bind_as (preliminary and subject to change).
Thanks for Simon Claret for valuable suggestions and for helping test.
* Fixed bug in Net::LDAP#open that was preventing #open from being
called more than one on a given Net::LDAP object.
== Net::LDAP 0.0.3: July 26, 2006
* Added simple TLS encryption.
Thanks to Garett Shulman for suggestions and for helping test.
== Net::LDAP 0.0.2: July 12, 2006
* Fixed malformation in distro tarball and gem.
* Improved documentation.
* Supported "paged search control."
* Added a range of API improvements.
* Thanks to Andre Nathan, andre@digirati.com.br, for valuable
suggestions.
* Added support for LE and GE search filters.
* Added support for Search referrals.
* Fixed a regression with openldap 2.2.x and higher caused
by the introduction of RFC-2696 controls. Thanks to Andre
Nathan for reporting the problem.
* Added support for RFC-2254 filter syntax.
== Net::LDAP 0.0.1: May 1, 2006
* Initial release.
* Client functionality is near-complete, although the APIs
are not guaranteed and may change depending on feedback
from the community.
* We're internally working on a Ruby-based implementation
of a full-featured, production-quality LDAP server,
which will leverage the underlying LDAP and BER functionality
in Net::LDAP.
* Please tell us if you would be interested in seeing a public
release of the LDAP server.
* Grateful acknowledgement to Austin Ziegler, who reviewed
this code and provided the release framework, including
minitar.
#--
# Net::LDAP for Ruby.
# http://rubyforge.org/projects/net-ldap/
# Copyright (C) 2006 by Francis Cianfrocca
#
# Available under the same terms as Ruby. See LICENCE in the main
# distribution for full licensing information.
#
# $Id: ChangeLog,v 1.17.2.4 2005/09/09 12:36:42 austin Exp $
#++
# vim: sts=2 sw=2 ts=4 et ai tw=77
Net::LDAP is copyrighted free software by Francis Cianfrocca
<garbagecat10@gmail.com>. You can redistribute it and/or modify it under either
the terms of the GPL (see the file COPYING), or the conditions below:
1. You may make and give away verbatim copies of the source form of the
software without restriction, provided that you duplicate all of the
original copyright notices and associated disclaimers.
2. You may modify your copy of the software in any way, provided that you do
at least ONE of the following:
a) place your modifications in the Public Domain or otherwise make them
Freely Available, such as by posting said modifications to Usenet or
an equivalent medium, or by allowing the author to include your
modifications in the software.
b) use the modified software only within your corporation or
organization.
c) rename any non-standard executables so the names do not conflict with
standard executables, which must also be provided.
d) make other distribution arrangements with the author.
3. You may distribute the software in object code or executable form,
provided that you do at least ONE of the following:
a) distribute the executables and library files of the software, together
with instructions (in the manual page or equivalent) on where to get
the original distribution.
b) accompany the distribution with the machine-readable source of the
software.
c) give non-standard executables non-standard names, with instructions on
where to get the original software distribution.
d) make other distribution arrangements with the author.
4. You may modify and include the part of the software into any other
software (possibly commercial). But some files in the distribution are
not written by the author, so that they are not under this terms.
They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
files under the ./missing directory. See each file for the copying
condition.
5. The scripts and library files supplied as input to or produced as output
from the software do not automatically fall under the copyright of the
software, but belong to whomever generated them, and may be sold
commercially, and may be aggregated with this software.
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
= Net::LDAP for Ruby
Net::LDAP is an LDAP support library written in pure Ruby. It supports all
LDAP client features, and a subset of server features as well.
Homepage:: http://rubyforge.org/projects/net-ldap/
Copyright:: (C) 2006 by Francis Cianfrocca
Original developer: Francis Cianfrocca
Contributions by Austin Ziegler gratefully acknowledged.
== LICENCE NOTES
Please read the file LICENCE for licensing restrictions on this library. In
the simplest terms, this library is available under the same terms as Ruby
itself.
== Requirements
Net::LDAP requires Ruby 1.8.2 or better.
== Documentation
See Net::LDAP for documentation and usage samples.
#--
# Net::LDAP for Ruby.
# http://rubyforge.org/projects/net-ldap/
# Copyright (C) 2006 by Francis Cianfrocca
#
# Available under the same terms as Ruby. See LICENCE in the main
# distribution for full licensing information.
#
# $Id: README 141 2006-07-12 10:37:37Z blackhedd $
#++
# vim: sts=2 sw=2 ts=4 et ai tw=77
#-- encoding: UTF-8
# $Id: ber.rb 142 2006-07-26 12:20:33Z blackhedd $
#
# 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
return nil if (StringIO == self.class) and eof?
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 || "" )
# Interpret the subobject, but note how the loop
# is built: nil ends the loop, but false (a valid
# BER value) does not!
while (e = sio.read_ber(syntax)) != nil
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
begin
require 'openssl'
class OpenSSL::SSL::SSLSocket
include Net::BER::BERParser
end
rescue LoadError
# Ignore LoadError.
# DON'T ignore NameError, which means the SSLSocket class
# is somehow unavailable on this implementation of Ruby's openssl.
# This may be WRONG, however, because we don't yet know how Ruby's
# openssl behaves on machines with no OpenSSL library. I suppose
# it's possible they do not fail to require 'openssl' but do not
# create the classes. So this code is provisional.
# Also, you might think that OpenSSL::SSL::SSLSocket inherits from
# IO so we'd pick it up above. But you'd be wrong.
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.
# The preferred way to do this in user code is via to_ber_application_sring
# and to_ber_contextspecific.
#
def to_ber code = 4
[code].pack('C') + length.to_ber_length_encoding + self
end
#
# to_ber_application_string
#
def to_ber_application_string code
to_ber( 0x40 + 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
This diff is collapsed.
#-- encoding: UTF-8
# $Id: dataset.rb 78 2006-04-26 02:57:34Z blackhedd $
#
#
#----------------------------------------------------------------------------
#
# 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 Dataset < Hash
attr_reader :comments
def Dataset::read_ldif io
ds = Dataset.new
line = io.gets && chomp
dn = nil
while line
io.gets and chomp
if $_ =~ /^[\s]+/
line << " " << $'
else
nextline = $_
if line =~ /^\#/
ds.comments << line
elsif line =~ /^dn:[\s]*/i
dn = $'
ds[dn] = Hash.new {|k,v| k[v] = []}
elsif line.length == 0
dn = nil
elsif line =~ /^([^:]+):([\:]?)[\s]*/
# $1 is the attribute name
# $2 is a colon iff the attr-value is base-64 encoded
# $' is the attr-value
# Avoid the Base64 class because not all Ruby versions have it.
attrvalue = ($2 == ":") ? $'.unpack('m').shift : $'
ds[dn][$1.downcase.intern] << attrvalue
end
line = nextline
end
end
ds
end
def initialize
@comments = []
end
def to_ldif
ary = []
ary += (@comments || [])
keys.sort.each {|dn|
ary << "dn: #{dn}"
self[dn].keys.map {|sym| sym.to_s}.sort.each {|attr|
self[dn][attr.intern].each {|val|
ary << "#{attr}: #{val}"
}
}
ary << ""
}
block_given? and ary.each {|line| yield line}
ary
end
end # Dataset
end # LDAP
end # Net
#-- encoding: UTF-8
# $Id: entry.rb 123 2006-05-18 03:52:38Z blackhedd $
#
# LDAP Entry (search-result) 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 LDAP
# Objects of this class represent individual entries in an LDAP
# directory. User code generally does not instantiate this class.
# Net::LDAP#search provides objects of this class to user code,
# either as block parameters or as return values.
#
# In LDAP-land, an "entry" is a collection of attributes that are
# uniquely and globally identified by a DN ("Distinguished Name").
# Attributes are identified by short, descriptive words or phrases.
# Although a directory is
# free to implement any attribute name, most of them follow rigorous
# standards so that the range of commonly-encountered attribute
# names is not large.
#
# An attribute name is case-insensitive. Most directories also
# restrict the range of characters allowed in attribute names.
# To simplify handling attribute names, Net::LDAP::Entry
# internally converts them to a standard format. Therefore, the
# methods which take attribute names can take Strings or Symbols,
# and work correctly regardless of case or capitalization.
#
# An attribute consists of zero or more data items called
# <i>values.</i> An entry is the combination of a unique DN, a set of attribute
# names, and a (possibly-empty) array of values for each attribute.
#
# Class Net::LDAP::Entry provides convenience methods for dealing
# with LDAP entries.
# In addition to the methods documented below, you may access individual
# attributes of an entry simply by giving the attribute name as
# the name of a method call. For example:
# ldap.search( ... ) do |entry|
# puts "Common name: #{entry.cn}"
# puts "Email addresses:"
# entry.mail.each {|ma| puts ma}
# end
# If you use this technique to access an attribute that is not present
# in a particular Entry object, a NoMethodError exception will be raised.
#
#--
# Ugly problem to fix someday: We key off the internal hash with
# a canonical form of the attribute name: convert to a string,
# downcase, then take the symbol. Unfortunately we do this in
# at least three places. Should do it in ONE place.
class Entry
# This constructor is not generally called by user code.
def initialize dn = nil # :nodoc:
@myhash = Hash.new {|k,v| k[v] = [] }
@myhash[:dn] = [dn]
end
def []= name, value # :nodoc:
sym = name.to_s.downcase.intern
@myhash[sym] = value
end
#--
# We have to deal with this one as we do with []=
# because this one and not the other one gets called
# in formulations like entry["CN"] << cn.
#
def [] name # :nodoc:
name = name.to_s.downcase.intern unless name.is_a?(Symbol)
@myhash[name]
end
# Returns the dn of the Entry as a String.
def dn
self[:dn][0]
end
# Returns an array of the attribute names present in the Entry.
def attribute_names
@myhash.keys
end
# Accesses each of the attributes present in the Entry.
# Calls a user-supplied block with each attribute in turn,
# passing two arguments to the block: a Symbol giving
# the name of the attribute, and a (possibly empty)
# Array of data values.
#
def each
if block_given?
attribute_names.each {|a|
attr_name,values = a,self[a]
yield attr_name, values
}
end
end
alias_method :each_attribute, :each
#--
# Convenience method to convert unknown method names
# to attribute references. Of course the method name
# comes to us as a symbol, so let's save a little time
# and not bother with the to_s.downcase two-step.
# Of course that means that a method name like mAIL
# won't work, but we shouldn't be encouraging that
# kind of bad behavior in the first place.
# Maybe we should thow something if the caller sends
# arguments or a block...
#
def method_missing *args, &block # :nodoc:
s = args[0].to_s.downcase.intern
if attribute_names.include?(s)
self[s]
elsif s.to_s[-1] == 61 and s.to_s.length > 1
value = args[1] or raise RuntimeError.new( "unable to set value" )
value = [value] unless value.is_a?(Array)
name = s.to_s[0..-2].intern
self<