Replaced ruby-net-ldap with net-ldap 0.2.2 gem.

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@8751 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang 2012-02-02 19:30:01 +00:00
parent d02f6a8e32
commit 73f9b825f0
71 changed files with 6200 additions and 3369 deletions

View File

@ -15,7 +15,6 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'net/ldap'
require 'iconv'
class AuthSourceLdap < AuthSource

View File

@ -55,6 +55,7 @@ Rails::Initializer.run do |config|
config.action_mailer.perform_deliveries = false
config.gem 'coderay', :version => '~>1.0.0'
config.gem 'net-ldap', :version => '~>0.2.2'
# Load any local configuration that is kept out of source control
# (e.g. gems, patches).

11
vendor/gems/net-ldap-0.2.2/.autotest vendored Normal file
View File

@ -0,0 +1,11 @@
require 'rubygems'
#require 'redgreen/autotest'
require 'autotest/timestamp'
Autotest.add_hook :initialize do |autotest|
%w{.git .hg .DS_Store ._* tmp log doc}.each do |exception|
autotest.add_exception(exception)
end
end
# vim: syntax=ruby

0
vendor/gems/net-ldap-0.2.2/.gemtest vendored Normal file
View File

2
vendor/gems/net-ldap-0.2.2/.rspec vendored Normal file
View File

@ -0,0 +1,2 @@
--colour
--format documentation

View File

@ -0,0 +1,200 @@
--- !ruby/object:Gem::Specification
name: net-ldap
version: !ruby/object:Gem::Version
hash: 19
prerelease:
segments:
- 0
- 2
- 2
version: 0.2.2
platform: ruby
authors:
- Francis Cianfrocca
- Emiel van de Laar
- Rory O'Connell
- Kaspar Schiess
- Austin Ziegler
autorequire:
bindir: bin
cert_chain: []
date: 2011-03-26 00:00:00 Z
dependencies:
- !ruby/object:Gem::Dependency
name: rubyforge
prerelease: false
requirement: &id001 !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 7
segments:
- 2
- 0
- 4
version: 2.0.4
type: :development
version_requirements: *id001
- !ruby/object:Gem::Dependency
name: hoe-git
prerelease: false
requirement: &id002 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 1
segments:
- 1
version: "1"
type: :development
version_requirements: *id002
- !ruby/object:Gem::Dependency
name: hoe-gemspec
prerelease: false
requirement: &id003 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 1
segments:
- 1
version: "1"
type: :development
version_requirements: *id003
- !ruby/object:Gem::Dependency
name: metaid
prerelease: false
requirement: &id004 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 1
segments:
- 1
version: "1"
type: :development
version_requirements: *id004
- !ruby/object:Gem::Dependency
name: flexmock
prerelease: false
requirement: &id005 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 59
segments:
- 0
- 9
- 0
version: 0.9.0
type: :development
version_requirements: *id005
- !ruby/object:Gem::Dependency
name: rspec
prerelease: false
requirement: &id006 !ruby/object:Gem::Requirement
none: false
requirements:
- - ~>
- !ruby/object:Gem::Version
hash: 3
segments:
- 2
- 0
version: "2.0"
type: :development
version_requirements: *id006
- !ruby/object:Gem::Dependency
name: hoe
prerelease: false
requirement: &id007 !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 41
segments:
- 2
- 9
- 1
version: 2.9.1
type: :development
version_requirements: *id007
description: "Net::LDAP for Ruby (also called net-ldap) implements client access for the\n\
Lightweight Directory Access Protocol (LDAP), an IETF standard protocol for\n\
accessing distributed directory services. Net::LDAP is written completely in\n\
Ruby with no external dependencies. It supports most LDAP client features and a\n\
subset of server features as well.\n\n\
Net::LDAP has been tested against modern popular LDAP servers including\n\
OpenLDAP and Active Directory. The current release is mostly compliant with\n\
earlier versions of the IETF LDAP RFCs (2251\xE2\x80\x932256, 2829\xE2\x80\x932830, 3377, and 3771).\n\
Our roadmap for Net::LDAP 1.0 is to gain full <em>client</em> compliance with\n\
the most recent LDAP RFCs (4510\xE2\x80\x934519, plus portions of 4520\xE2\x80\x934532)."
email:
- blackhedd@rubyforge.org
- gemiel@gmail.com
- rory.ocon@gmail.com
- kaspar.schiess@absurd.li
- austin@rubyforge.org
executables: []
extensions: []
extra_rdoc_files:
- Manifest.txt
- Contributors.rdoc
- Hacking.rdoc
- History.rdoc
- License.rdoc
- README.rdoc
files:
- Manifest.txt
- Contributors.rdoc
- Hacking.rdoc
- History.rdoc
- License.rdoc
- README.rdoc
homepage: http://net-ldap.rubyforge.org/
licenses: []
post_install_message:
rdoc_options:
- --main
- README.rdoc
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 57
segments:
- 1
- 8
- 7
version: 1.8.7
required_rubygems_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
requirements: []
rubyforge_project: net-ldap
rubygems_version: 1.7.2
signing_key:
specification_version: 3
summary: Net::LDAP for Ruby (also called net-ldap) implements client access for the Lightweight Directory Access Protocol (LDAP), an IETF standard protocol for accessing distributed directory services
test_files: []

View File

@ -0,0 +1,21 @@
== Contributors
Net::LDAP was originally developed by:
* Francis Cianfrocca (garbagecat)
Contributions since:
* Emiel van de Laar (emiel)
* Rory O'Connell (roryo)
* Kaspar Schiess (kschiess)
* Austin Ziegler (halostatue)
* Dimitrij Denissenko (dim)
* James Hewitt (jamstah)
* Kouhei Sutou (kou)
* Lars Tobias Skjong-Børsting (larstobi)
* Rory O'Connell (roryo)
* Tony Headford (tonyheadford)
* Derek Harmel (derekharmel)
* Erik Hetzner (egh)
* nowhereman

68
vendor/gems/net-ldap-0.2.2/Hacking.rdoc vendored Normal file
View File

@ -0,0 +1,68 @@
= Hacking on Net::LDAP
We welcome your contributions to Net::LDAP. We accept most contributions, but
there are ways to increase the chance of your patch being accepted quickly.
== Licensing
Net::LDAP 0.2 and later are be licensed under an MIT-style license; any
contributions after 2010-04-20 must be under this license to be accepted.
== Formatting
* Your patches should be formatted like the rest of Net::LDAP.
* We use a text wrap of 7678 characters, especially for documentation
contents.
* Operators should have spaces around them.
* Method definitions should have parentheses around arguments (and no
parentheses if there are no arguments).
* Indentation should be kept as flat as possible; this may mean being more
explicit with constants.
We welcome your contributions to Net::LDAP. To increase the chances of your
patches being accepted, we recommend that you follow the guidelines below:
== Documentation
* Documentation: {net-ldap}[http://net-ldap.rubyforge.org/]
It is very important that, if you add new methods or objects, your code is
well-documented. The purpose of the changes should be clearly described so that
even if this is a feature we do not use, we can understand its purpose.
We also encourage documentation-only contributions that improve the
documentation of Net::LDAP.
We encourage you to provide a good summary of your as a modification to
+History.rdoc+, and if you're not yet named as a contributor, include a
modification to +Contributors.rdoc+ to add yourself.
== Tests
The Net::LDAP team uses RSpec for unit testing; all changes must have rspec
tests for any new or changed features.
Your changes should have been tested against at least one real LDAP server; the
current tests are not sufficient to find all possible bugs. It's unlikely that
they will ever be sufficient given the variations in LDAP server behaviour.
If you're introducing a new feature, it would be preferred for you to provide
us with a sample LDIF data file for importing into LDAP servers for testing.
== Development Dependencies
Net::LDAP uses several libraries during development, all of which can be
installed using RubyGems.
* *hoe*
* *hoe-git*
* *metaid*
* *rspec*
* *flexmock*
== Participation
* RubyForge: {net-ldap}[http://rubyforge.org/projects/net-ldap]
* GitHub: {ruby-ldap/ruby-net-ldap}[https://github.com/ruby-ldap/ruby-net-ldap/]
* Group: {ruby-ldap}[http://groups.google.com/group/ruby-ldap]

172
vendor/gems/net-ldap-0.2.2/History.rdoc vendored Normal file
View File

@ -0,0 +1,172 @@
=== Net::LDAP 0.2.2 / 2011-03-26
* Bug Fixes:
* Fixed the call to Net::LDAP.modify_ops from Net::LDAP#modify.
=== Net::LDAP 0.2.1 / 2011-03-23
* Bug Fixes:
* Net::LDAP.modify_ops was broken and is now fixed.
=== Net::LDAP 0.2 / 2011-03-22
* Major Enhancements:
* Net::LDAP::Filter changes:
* Filters can only be constructed using our custom constructors (eq, ge,
etc.). Cleaned up the code to reflect the private new.
* Fixed #to_ber to output a BER representation for :ne filters. Simplified
the BER construction for substring matching.
* Added Filter.join(left, right), Filter.intersect(left, right), and
Filter.negate(filter) to match Filter#&, Filter#|, and Filter#~@ to
prevent those operators from having problems with the private new.
* Added Filter.present and Filter.present? aliases for the method
previously only known as Filter.pres.
* Added Filter.escape to escape strings for use in filters, based on
rfc4515.
* Added Filter.equals, Filter.begins, Filter.ends and Filter.contains,
which automatically escape input for use in a filter string.
* Cleaned up Net::LDAP::Filter::FilterParser to handle branches better.
Fixed some of the regular expressions to be more canonically defined.
* Correctly handles single-branch branches.
* Cleaned up the string representation of Filter objects.
* Added experimental support for RFC4515 extensible matching (e.g.,
"(cn:caseExactMatch:=Fred Flintstone)"); provided by "nowhereman".
* Net::LDAP::DN class representing an automatically escaping/unescaping
distinguished name for LDAP queries.
* Minor Enhancements:
* SSL capabilities will be enabled or disabled based on whether we can load
OpenSSL successfully or not.
* Moved the core class extensions extensions from being in the Net::LDAP
hierarchy to the Net::BER hierarchy as most of the methods therein are
related to BER-encoding values. This will make extracting Net::BER from
Net::LDAP easier in the future.
* Added some unit tests for the BER core extensions.
* Paging controls are only sent where they are supported.
* Documentation Changes:
* Core class extension methods under Net::BER.
* Extensive changes to Net::BER documentation.
* Cleaned up some rdoc oddities, suppressed empty documentation sections
where possible.
* Added a document describing how to contribute to Net::LDAP most
effectively.
* Added a document recognizing contributors to Net::LDAP.
* Extended unit testing:
* Added some unit tests for the BER core extensions.
* The LDIF test data file was split for Ruby 1.9 regexp support.
* Added a cruisecontrol.rb task.
* Converted some test/unit tests to specs.
* Code clean-up:
* Made the formatting of code consistent across all files.
* Removed Net::BER::BERParser::TagClasses as it does not appear to be used.
* Replaced calls to #to_a with calls to Kernel#Array; since Ruby 1.8.3, the
default #to_a implementation has been deprecated and should be replaced
either with calls to Kernel#Array or [value].flatten(1).
* Modified #add and #modify to return a Pdu#result_code instead of a
Pdu#result. This may be changed in Net::LDAP 1.0 to return the full
Pdu#result, but if we do so, it will be that way for all LDAP calls
involving Pdu objects.
* Renamed Net::LDAP::Psw to Net::LDAP::Password with a corresponding filename
change.
* Removed the stub file lib/net/ldif.rb and class Net::LDIF.
* Project Management:
* Changed the license from Ruby + GPL to MIT with the agreement of the
original author (Francis Cianfrocca) and the named contributors. Versions
prior to 0.2.0 are still available under the Ruby + GPL license.
=== Net::LDAP 0.1.1 / 2010-03-18
* Fixing a critical problem with sockets.
=== Net::LDAP 0.1 / 2010-03-17
* Small fixes throughout, more to come.
* Ruby 1.9 support added.
* Ruby 1.8.6 and below support removed. If we can figure out a compatible way
to reintroduce this, we will.
* New maintainers, new project repository location. Please see the README.txt.
=== Net::LDAP 0.0.5 / 2009-03-xx
* 13 minor enhancements:
* Added Net::LDAP::Entry#to_ldif
* Supported rootDSE searches with a new API.
* Added [preliminary (still undocumented) support for SASL authentication.
* Supported several constructs from the server side of the LDAP protocol.
* Added a "consuming" String#read_ber! method.
* Added some support for SNMP data-handling.
* Belatedly added a patch contributed by Kouhei Sutou last October.
The patch adds start_tls support.
* Added Net::LDAP#search_subschema_entry
* Added Net::LDAP::Filter#parse_ber, which constructs Net::LDAP::Filter
objects directly from BER objects that represent search filters in
LDAP SearchRequest packets.
* Added Net::LDAP::Filter#execute, which allows arbitrary processing
based on LDAP filters.
* Changed Net::LDAP::Entry so it can be marshalled and unmarshalled.
Thanks to an anonymous feature requester who only left the name
"Jammy."
* Added support for binary values in Net::LDAP::Entry LDIF conversions
and marshalling.
* Migrated to 'hoe' as the new project droid.
* 14 bugs fixed:
* Silenced some annoying warnings in filter.rb. Thanks to "barjunk"
for pointing this out.
* Some fairly extensive performance optimizations in the BER parser.
* Fixed a bug in Net::LDAP::Entry::from_single_ldif_string noticed by
Matthias Tarasiewicz.
* Removed an erroneous LdapError value, noticed by Kouhei Sutou.
* Supported attributes containing blanks (cn=Babs Jensen) to
Filter#construct. Suggested by an anonymous Rubyforge user.
* Added missing syntactic support for Filter ANDs, NOTs and a few other
things.
* Extended support for server-reported error messages. This was provisionally
added to Net::LDAP#add, and eventually will be added to other methods.
* Fixed bug in Net::LDAP#bind. We were ignoring the passed-in auth parm.
Thanks to Kouhei Sutou for spotting it.
* Patched filter syntax to support octal \XX codes. Thanks to Kouhei Sutou
for the patch.
* Applied an additional patch from Kouhei.
* Allowed comma in filter strings, suggested by Kouhei.
* 04Sep07, Changed four error classes to inherit from StandardError rather
Exception, in order to be friendlier to irb. Suggested by Kouhei.
* Ensure connections are closed. Thanks to Kristian Meier.
* Minor bug fixes here and there.
=== Net::LDAP 0.0.4 / 2006-08-15
* 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 / 2006-07-26
* Added simple TLS encryption.
Thanks to Garett Shulman for suggestions and for helping test.
=== Net::LDAP 0.0.2 / 2006-07-12
* 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 / 2006-05-01
* 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.

29
vendor/gems/net-ldap-0.2.2/License.rdoc vendored Normal file
View File

@ -0,0 +1,29 @@
== License
This software is available under the terms of the MIT license.
Copyright 20062011 by Francis Cianfrocca and other contributors.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
=== Notice of License Change
Versions prior to 0.2 were under Ruby's dual license with the GNU GPL. With
this release (0.2), Net::LDAP is now under the MIT license.

49
vendor/gems/net-ldap-0.2.2/Manifest.txt vendored Normal file
View File

@ -0,0 +1,49 @@
.autotest
.rspec
Contributors.rdoc
Hacking.rdoc
History.rdoc
License.rdoc
Manifest.txt
README.rdoc
Rakefile
autotest/discover.rb
lib/net-ldap.rb
lib/net/ber.rb
lib/net/ber/ber_parser.rb
lib/net/ber/core_ext.rb
lib/net/ber/core_ext/array.rb
lib/net/ber/core_ext/bignum.rb
lib/net/ber/core_ext/false_class.rb
lib/net/ber/core_ext/fixnum.rb
lib/net/ber/core_ext/string.rb
lib/net/ber/core_ext/true_class.rb
lib/net/ldap.rb
lib/net/ldap/dataset.rb
lib/net/ldap/dn.rb
lib/net/ldap/entry.rb
lib/net/ldap/filter.rb
lib/net/ldap/password.rb
lib/net/ldap/pdu.rb
lib/net/snmp.rb
net-ldap.gemspec
spec/integration/ssl_ber_spec.rb
spec/spec.opts
spec/spec_helper.rb
spec/unit/ber/ber_spec.rb
spec/unit/ber/core_ext/string_spec.rb
spec/unit/ldap/dn_spec.rb
spec/unit/ldap/entry_spec.rb
spec/unit/ldap/filter_spec.rb
spec/unit/ldap_spec.rb
test/common.rb
test/test_entry.rb
test/test_filter.rb
test/test_ldap_connection.rb
test/test_ldif.rb
test/test_password.rb
test/test_rename.rb
test/test_snmp.rb
test/testdata.ldif
testserver/ldapserver.rb
testserver/testdata.ldif

52
vendor/gems/net-ldap-0.2.2/README.rdoc vendored Normal file
View File

@ -0,0 +1,52 @@
= Net::LDAP for Ruby
== Description
Net::LDAP for Ruby (also called net-ldap) implements client access for the
Lightweight Directory Access Protocol (LDAP), an IETF standard protocol for
accessing distributed directory services. Net::LDAP is written completely in
Ruby with no external dependencies. It supports most LDAP client features and a
subset of server features as well.
Net::LDAP has been tested against modern popular LDAP servers including
OpenLDAP and Active Directory. The current release is mostly compliant with
earlier versions of the IETF LDAP RFCs (22512256, 28292830, 3377, and 3771).
Our roadmap for Net::LDAP 1.0 is to gain full <em>client</em> compliance with
the most recent LDAP RFCs (45104519, plus portions of 45204532).
== Where
* {RubyForge}[http://rubyforge.org/projects/net-ldap]
* {GitHub}[https://github.com/ruby-ldap/ruby-net-ldap]
* {ruby-ldap@googlegroups.com}[http://groups.google.com/group/ruby-ldap]
* {Documentation}[http://net-ldap.rubyforge.org/]
The Net::LDAP for Ruby documentation, project description, and main downloads
can currently be found on {RubyForge}[http://rubyforge.org/projects/net-ldap].
== Synopsis
See Net::LDAP for documentation and usage samples.
== Requirements
Net::LDAP requires a Ruby 1.8.7 interpreter or better.
== Install
Net::LDAP is a pure Ruby library. It does not require any external libraries.
You can install the RubyGems version of Net::LDAP available from the usual
sources.
gem install net-ldap
Simply require either 'net-ldap' or 'net/ldap'.
For non-RubyGems installations of Net::LDAP, you can use Minero Aoki's
{setup.rb}[http://i.loveruby.net/en/projects/setup/] as the layout of
Net::LDAP is compliant. The setup installer is not included in the
Net::LDAP repository.
:include: Contributors.rdoc
:include: License.rdoc

75
vendor/gems/net-ldap-0.2.2/Rakefile vendored Normal file
View File

@ -0,0 +1,75 @@
# -*- ruby encoding: utf-8 -*-
require "rubygems"
require 'hoe'
Hoe.plugin :doofus
Hoe.plugin :git
Hoe.plugin :gemspec
Hoe.plugin :rubyforge
Hoe.spec 'net-ldap' do |spec|
spec.rubyforge_name = spec.name
spec.developer("Francis Cianfrocca", "blackhedd@rubyforge.org")
spec.developer("Emiel van de Laar", "gemiel@gmail.com")
spec.developer("Rory O'Connell", "rory.ocon@gmail.com")
spec.developer("Kaspar Schiess", "kaspar.schiess@absurd.li")
spec.developer("Austin Ziegler", "austin@rubyforge.org")
spec.remote_rdoc_dir = ''
spec.rsync_args << ' --exclude=statsvn/'
spec.url = %W(http://net-ldap.rubyforge.org/ https://github.com/ruby-ldap/ruby-net-ldap)
spec.history_file = 'History.rdoc'
spec.readme_file = 'README.rdoc'
spec.extra_rdoc_files = FileList["*.rdoc"].to_a
spec.extra_dev_deps << [ "hoe-git", "~> 1" ]
spec.extra_dev_deps << [ "hoe-gemspec", "~> 1" ]
spec.extra_dev_deps << [ "metaid", "~> 1" ]
spec.extra_dev_deps << [ "flexmock", "~> 0.9.0" ]
spec.extra_dev_deps << [ "rspec", "~> 2.0" ]
spec.clean_globs << "coverage"
spec.spec_extras[:required_ruby_version] = ">= 1.8.7"
spec.multiruby_skip << "1.8.6"
spec.multiruby_skip << "1_8_6"
spec.need_tar = true
end
# I'm not quite ready to get rid of this, but I think "rake git:manifest" is
# sufficient.
namespace :old do
desc "Build the manifest file from the current set of files."
task :build_manifest do |t|
require 'find'
paths = []
Find.find(".") do |path|
next if File.directory?(path)
next if path =~ /\.svn/
next if path =~ /\.git/
next if path =~ /\.hoerc/
next if path =~ /\.swp$/
next if path =~ %r{coverage/}
next if path =~ /~$/
paths << path.sub(%r{^\./}, '')
end
File.open("Manifest.txt", "w") do |f|
f.puts paths.sort.join("\n")
end
puts paths.sort.join("\n")
end
end
desc "Run a full set of integration and unit tests"
task :cruise => [:test, :spec]
# vim: syntax=ruby

View File

@ -0,0 +1 @@
Autotest.add_discovery { "rspec2" }

View File

@ -0,0 +1,2 @@
# -*- ruby encoding: utf-8 -*-
require 'net/ldap'

View File

@ -0,0 +1,316 @@
# -*- ruby encoding: utf-8 -*-
module Net # :nodoc:
##
# == Basic Encoding Rules (BER) Support Module
#
# Much of the text below is cribbed from Wikipedia:
# http://en.wikipedia.org/wiki/Basic_Encoding_Rules
#
# The ITU Specification is also worthwhile reading:
# http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
#
# The Basic Encoding Rules were the original rules laid out by the ASN.1
# standard for encoding abstract information into a concrete data stream.
# The rules, collectively referred to as a transfer syntax in ASN.1
# parlance, specify the exact octet sequences which are used to encode a
# given data item. The syntax defines such elements as: the
# representations for basic data types, the structure of length
# information, and the means for defining complex or compound types based
# on more primitive types. The BER syntax, along with two subsets of BER
# (the Canonical Encoding Rules and the Distinguished Encoding Rules), are
# defined by the ITU-T's X.690 standards document, which is part of the
# ASN.1 document series.
#
# == Encoding
# The BER format specifies a self-describing and self-delimiting format
# for encoding ASN.1 data structures. Each data element is encoded as a
# type identifier, a length description, the actual data elements, and
# where necessary, an end-of-content marker. This format allows a receiver
# to decode the ASN.1 information from an incomplete stream, without
# requiring any pre-knowledge of the size, content, or semantic meaning of
# the data.
#
# <Type | Length | Value [| End-of-Content]>
#
# == Protocol Data Units (PDU)
# Protocols are defined with schema represented in BER, such that a PDU
# consists of cascaded type-length-value encodings.
#
# === Type Tags
# BER type tags are represented as single octets (bytes). The lower five
# bits of the octet are tag identifier numbers and the upper three bits of
# the octet are used to distinguish the type as native to ASN.1,
# application-specific, context-specific, or private. See
# Net::BER::TAG_CLASS and Net::BER::ENCODING_TYPE for more information.
#
# If Class is set to Universal (0b00______), the value is of a type native
# to ASN.1 (e.g. INTEGER). The Application class (0b01______) is only
# valid for one specific application. Context_specific (0b10______)
# depends on the context and private (0b11_______) can be defined in
# private specifications
#
# If the primitive/constructed bit is zero (0b__0_____), it specifies that
# the value is primitive like an INTEGER. If it is one (0b__1_____), the
# value is a constructed value that contains type-length-value encoded
# types like a SET or a SEQUENCE.
#
# === Defined Universal (ASN.1 Native) Types
# There are a number of pre-defined universal (native) types.
#
# <table>
# <tr><th>Name</th><th>Primitive<br />Constructed</th><th>Number</th></tr>
# <tr><th>EOC (End-of-Content)</th><th>P</th><td>0: 0 (0x0, 0b00000000)</td></tr>
# <tr><th>BOOLEAN</th><th>P</th><td>1: 1 (0x01, 0b00000001)</td></tr>
# <tr><th>INTEGER</th><th>P</th><td>2: 2 (0x02, 0b00000010)</td></tr>
# <tr><th>BIT STRING</th><th>P</th><td>3: 3 (0x03, 0b00000011)</td></tr>
# <tr><th>BIT STRING</th><th>C</th><td>3: 35 (0x23, 0b00100011)</td></tr>
# <tr><th>OCTET STRING</th><th>P</th><td>4: 4 (0x04, 0b00000100)</td></tr>
# <tr><th>OCTET STRING</th><th>C</th><td>4: 36 (0x24, 0b00100100)</td></tr>
# <tr><th>NULL</th><th>P</th><td>5: 5 (0x05, 0b00000101)</td></tr>
# <tr><th>OBJECT IDENTIFIER</th><th>P</th><td>6: 6 (0x06, 0b00000110)</td></tr>
# <tr><th>Object Descriptor</th><th>P</th><td>7: 7 (0x07, 0b00000111)</td></tr>
# <tr><th>EXTERNAL</th><th>C</th><td>8: 40 (0x28, 0b00101000)</td></tr>
# <tr><th>REAL (float)</th><th>P</th><td>9: 9 (0x09, 0b00001001)</td></tr>
# <tr><th>ENUMERATED</th><th>P</th><td>10: 10 (0x0a, 0b00001010)</td></tr>
# <tr><th>EMBEDDED PDV</th><th>C</th><td>11: 43 (0x2b, 0b00101011)</td></tr>
# <tr><th>UTF8String</th><th>P</th><td>12: 12 (0x0c, 0b00001100)</td></tr>
# <tr><th>UTF8String</th><th>C</th><td>12: 44 (0x2c, 0b00101100)</td></tr>
# <tr><th>RELATIVE-OID</th><th>P</th><td>13: 13 (0x0d, 0b00001101)</td></tr>
# <tr><th>SEQUENCE and SEQUENCE OF</th><th>C</th><td>16: 48 (0x30, 0b00110000)</td></tr>
# <tr><th>SET and SET OF</th><th>C</th><td>17: 49 (0x31, 0b00110001)</td></tr>
# <tr><th>NumericString</th><th>P</th><td>18: 18 (0x12, 0b00010010)</td></tr>
# <tr><th>NumericString</th><th>C</th><td>18: 50 (0x32, 0b00110010)</td></tr>
# <tr><th>PrintableString</th><th>P</th><td>19: 19 (0x13, 0b00010011)</td></tr>
# <tr><th>PrintableString</th><th>C</th><td>19: 51 (0x33, 0b00110011)</td></tr>
# <tr><th>T61String</th><th>P</th><td>20: 20 (0x14, 0b00010100)</td></tr>
# <tr><th>T61String</th><th>C</th><td>20: 52 (0x34, 0b00110100)</td></tr>
# <tr><th>VideotexString</th><th>P</th><td>21: 21 (0x15, 0b00010101)</td></tr>
# <tr><th>VideotexString</th><th>C</th><td>21: 53 (0x35, 0b00110101)</td></tr>
# <tr><th>IA5String</th><th>P</th><td>22: 22 (0x16, 0b00010110)</td></tr>
# <tr><th>IA5String</th><th>C</th><td>22: 54 (0x36, 0b00110110)</td></tr>
# <tr><th>UTCTime</th><th>P</th><td>23: 23 (0x17, 0b00010111)</td></tr>
# <tr><th>UTCTime</th><th>C</th><td>23: 55 (0x37, 0b00110111)</td></tr>
# <tr><th>GeneralizedTime</th><th>P</th><td>24: 24 (0x18, 0b00011000)</td></tr>
# <tr><th>GeneralizedTime</th><th>C</th><td>24: 56 (0x38, 0b00111000)</td></tr>
# <tr><th>GraphicString</th><th>P</th><td>25: 25 (0x19, 0b00011001)</td></tr>
# <tr><th>GraphicString</th><th>C</th><td>25: 57 (0x39, 0b00111001)</td></tr>
# <tr><th>VisibleString</th><th>P</th><td>26: 26 (0x1a, 0b00011010)</td></tr>
# <tr><th>VisibleString</th><th>C</th><td>26: 58 (0x3a, 0b00111010)</td></tr>
# <tr><th>GeneralString</th><th>P</th><td>27: 27 (0x1b, 0b00011011)</td></tr>
# <tr><th>GeneralString</th><th>C</th><td>27: 59 (0x3b, 0b00111011)</td></tr>
# <tr><th>UniversalString</th><th>P</th><td>28: 28 (0x1c, 0b00011100)</td></tr>
# <tr><th>UniversalString</th><th>C</th><td>28: 60 (0x3c, 0b00111100)</td></tr>
# <tr><th>CHARACTER STRING</th><th>P</th><td>29: 29 (0x1d, 0b00011101)</td></tr>
# <tr><th>CHARACTER STRING</th><th>C</th><td>29: 61 (0x3d, 0b00111101)</td></tr>
# <tr><th>BMPString</th><th>P</th><td>30: 30 (0x1e, 0b00011110)</td></tr>
# <tr><th>BMPString</th><th>C</th><td>30: 62 (0x3e, 0b00111110)</td></tr>
# </table>
module BER
VERSION = '0.2.2'
##
# Used for BER-encoding the length and content bytes of a Fixnum integer
# values.
MAX_FIXNUM_SIZE = 0.size
##
# BER tag classes are kept in bits seven and eight of the tag type
# octet.
#
# <table>
# <tr><th>Bitmask</th><th>Definition</th></tr>
# <tr><th><tt>0b00______</tt></th><td>Universal (ASN.1 Native) Types</td></tr>
# <tr><th><tt>0b01______</tt></th><td>Application Types</td></tr>
# <tr><th><tt>0b10______</tt></th><td>Context-Specific Types</td></tr>
# <tr><th><tt>0b11______</tt></th><td>Private Types</td></tr>
# </table>
TAG_CLASS = {
:universal => 0b00000000, # 0
:application => 0b01000000, # 64
:context_specific => 0b10000000, # 128
:private => 0b11000000, # 192
}
##
# BER encoding type is kept in bit 6 of the tag type octet.
#
# <table>
# <tr><th>Bitmask</th><th>Definition</th></tr>
# <tr><th><tt>0b__0_____</tt></th><td>Primitive</td></tr>
# <tr><th><tt>0b__1_____</tt></th><td>Constructed</td></tr>
# </table>
ENCODING_TYPE = {
:primitive => 0b00000000, # 0
:constructed => 0b00100000, # 32
}
##
# Accepts a hash of hashes describing a BER syntax and converts it into
# a byte-keyed object for fast BER conversion lookup. The resulting
# "compiled" syntax is used by Net::BER::BERParser.
#
# This method should be called only by client classes of Net::BER (e.g.,
# Net::LDAP and Net::SNMP) and not by clients of those classes.
#
# The hash-based syntax uses TAG_CLASS keys that contain hashes of
# ENCODING_TYPE keys that contain tag numbers with object type markers.
#
# :<TAG_CLASS> => {
# :<ENCODING_TYPE> => {
# <number> => <object-type>
# },
# },
#
# === Permitted Object Types
# <tt>:string</tt>:: A string value, represented as BerIdentifiedString.
# <tt>:integer</tt>:: An integer value, represented with Fixnum.
# <tt>:oid</tt>:: An Object Identifier value; see X.690 section
# 8.19. Currently represented with a standard array,
# but may be better represented as a
# BerIdentifiedOID object.
# <tt>:array</tt>:: A sequence, represented as BerIdentifiedArray.
# <tt>:boolean</tt>:: A boolean value, represented as +true+ or +false+.
# <tt>:null</tt>:: A null value, represented as BerIdentifiedNull.
#
# === Example
# Net::LDAP defines its ASN.1 BER syntax something like this:
#
# class Net::LDAP
# AsnSyntax = Net::BER.compile_syntax({
# :application => {
# :primitive => {
# 2 => :null,
# },
# :constructed => {
# 0 => :array,
# # ...
# },
# },
# :context_specific => {
# :primitive => {
# 0 => :string,
# # ...
# },
# :constructed => {
# 0 => :array,
# # ...
# },
# }
# })
# end
#
# NOTE:: For readability and formatting purposes, Net::LDAP and its
# siblings actually construct their syntaxes more deliberately,
# as shown below. Since a hash is passed in the end in any case,
# the format does not matter.
#
# primitive = { 2 => :null }
# constructed = {
# 0 => :array,
# # ...
# }
# application = {
# :primitive => primitive,
# :constructed => constructed
# }
#
# primitive = {
# 0 => :string,
# # ...
# }
# constructed = {
# 0 => :array,
# # ...
# }
# context_specific = {
# :primitive => primitive,
# :constructed => constructed
# }
# AsnSyntax = Net::BER.compile_syntax(:application => application,
# :context_specific => context_specific)
def self.compile_syntax(syntax)
# TODO 20100327 AZ: Should we be allocating an array of 256 values
# that will either be +nil+ or an object type symbol, or should we
# allocate an empty Hash since unknown values return +nil+ anyway?
out = [ nil ] * 256
syntax.each do |tag_class_id, encodings|
tag_class = TAG_CLASS[tag_class_id]
encodings.each do |encoding_id, classes|
encoding = ENCODING_TYPE[encoding_id]
object_class = tag_class + encoding
classes.each do |number, object_type|
out[object_class + number] = object_type
end
end
end
out
end
end
end
class Net::BER::BerError < RuntimeError; end
##
# An Array object with a BER identifier attached.
class Net::BER::BerIdentifiedArray < Array
attr_accessor :ber_identifier
def initialize(*args)
super
end
end
##
# A BER object identifier.
class Net::BER::BerIdentifiedOid
attr_accessor :ber_identifier
def initialize(oid)
if oid.is_a?(String)
oid = oid.split(/\./).map {|s| s.to_i }
end
@value = oid
end
def to_ber
to_ber_oid
end
def to_ber_oid
@value.to_ber_oid
end
def to_s
@value.join(".")
end
def to_arr
@value.dup
end
end
##
# A String object with a BER identifier attached.
class Net::BER::BerIdentifiedString < String
attr_accessor :ber_identifier
def initialize args
super args
end
end
module Net::BER
##
# A BER null object.
class BerIdentifiedNull
attr_accessor :ber_identifier
def to_ber
"\005\000"
end
end
##
# The default BerIdentifiedNull object.
Null = Net::BER::BerIdentifiedNull.new
end
require 'net/ber/core_ext'

View File

@ -0,0 +1,168 @@
# -*- ruby encoding: utf-8 -*-
require 'stringio'
# Implements Basic Encoding Rules parsing to be mixed into types as needed.
module Net::BER::BERParser
primitive = {
1 => :boolean,
2 => :integer,
4 => :string,
5 => :null,
6 => :oid,
10 => :integer,
13 => :string # (relative OID)
}
constructed = {
16 => :array,
17 => :array
}
universal = { :primitive => primitive, :constructed => constructed }
primitive = { 10 => :integer }
context = { :primitive => primitive }
# The universal, built-in ASN.1 BER syntax.
BuiltinSyntax = Net::BER.compile_syntax(:universal => universal,
:context_specific => context)
##
# This is an extract of our BER object parsing to simplify our
# understanding of how we parse basic BER object types.
def parse_ber_object(syntax, id, data)
# Find the object type from either the provided syntax lookup table or
# the built-in syntax lookup table.
#
# This exceptionally clever bit of code is verrrry slow.
object_type = (syntax && syntax[id]) || BuiltinSyntax[id]
# == is expensive so sort this so the common cases are at the top.
if object_type == :string
s = Net::BER::BerIdentifiedString.new(data || "")
s.ber_identifier = id
s
elsif object_type == :integer
j = 0
data.each_byte { |b| j = (j << 8) + b }
j
elsif object_type == :oid
# See X.690 pgh 8.19 for an explanation of this algorithm.
# This is potentially not good enough. We may need a
# BerIdentifiedOid as a subclass of BerIdentifiedArray, to
# get the ber identifier and also a to_s method that produces
# the familiar dotted notation.
oid = data.unpack("w*")
f = oid.shift
g = if f < 40
[0, f]
elsif f < 80
[1, f - 40]
else
# f - 80 can easily be > 80. What a weird optimization.
[2, f - 80]
end
oid.unshift g.last
oid.unshift g.first
# Net::BER::BerIdentifiedOid.new(oid)
oid
elsif object_type == :array
seq = Net::BER::BerIdentifiedArray.new
seq.ber_identifier = id
sio = StringIO.new(data || "")
# 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
elsif object_type == :boolean
data != "\000"
elsif object_type == :null
n = Net::BER::BerIdentifiedNull.new
n.ber_identifier = id
n
else
raise Net::BER::BerError, "Unsupported object type: id=#{id}"
end
end
private :parse_ber_object
##
# This is an extract of how our BER object length parsing is done to
# simplify the primary call. This is defined in X.690 section 8.1.3.
#
# The BER length will either be a single byte or up to 126 bytes in
# length. There is a special case of a BER length indicating that the
# content-length is undefined and will be identified by the presence of
# two null values (0x00 0x00).
#
# <table>
# <tr>
# <th>Range</th>
# <th>Length</th>
# </tr>
# <tr>
# <th>0x00 -- 0x7f<br />0b00000000 -- 0b01111111</th>
# <td>0 - 127 bytes</td>
# </tr>
# <tr>
# <th>0x80<br />0b10000000</th>
# <td>Indeterminate (end-of-content marker required)</td>
# </tr>
# <tr>
# <th>0x81 -- 0xfe<br />0b10000001 -- 0b11111110</th>
# <td>1 - 126 bytes of length as an integer value</td>
# </tr>
# <tr>
# <th>0xff<br />0b11111111</th>
# <td>Illegal (reserved for future expansion)</td>
# </tr>
# </table>
#
#--
# This has been modified from the version that was previously inside
# #read_ber to handle both the indeterminate terminator case and the
# invalid BER length case. Because the "lengthlength" value was not used
# inside of #read_ber, we no longer return it.
def read_ber_length
n = getbyte
if n <= 0x7f
n
elsif n == 0x80
-1
elsif n == 0xff
raise Net::BER::BerError, "Invalid BER length 0xFF detected."
else
v = 0
read(n & 0x7f).each_byte do |b|
v = (v << 8) + b
end
v
end
end
private :read_ber_length
##
# Reads a BER object from the including object. Requires that #getbyte is
# implemented on the including object and that it returns a Fixnum value.
# Also requires #read(bytes) to work.
#
# This does not work with non-blocking I/O.
def read_ber(syntax = nil)
# 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.
id = getbyte or return nil # don't trash this value, we'll use it later
content_length = read_ber_length
if -1 == content_length
raise Net::BER::BerError, "Indeterminite BER content length not implemented."
else
data = read(content_length)
end
parse_ber_object(syntax, id, data)
end
end

View File

@ -0,0 +1,62 @@
# -*- ruby encoding: utf-8 -*-
require 'net/ber/ber_parser'
# :stopdoc:
class IO
include Net::BER::BERParser
end
class StringIO
include Net::BER::BERParser
end
if defined? ::OpenSSL
class OpenSSL::SSL::SSLSocket
include Net::BER::BERParser
end
end
# :startdoc:
module Net::BER::Extensions # :nodoc:
end
require 'net/ber/core_ext/string'
# :stopdoc:
class String
include Net::BER::BERParser
include Net::BER::Extensions::String
end
require 'net/ber/core_ext/array'
# :stopdoc:
class Array
include Net::BER::Extensions::Array
end
# :startdoc:
require 'net/ber/core_ext/bignum'
# :stopdoc:
class Bignum
include Net::BER::Extensions::Bignum
end
# :startdoc:
require 'net/ber/core_ext/fixnum'
# :stopdoc:
class Fixnum
include Net::BER::Extensions::Fixnum
end
# :startdoc:
require 'net/ber/core_ext/true_class'
# :stopdoc:
class TrueClass
include Net::BER::Extensions::TrueClass
end
# :startdoc:
require 'net/ber/core_ext/false_class'
# :stopdoc:
class FalseClass
include Net::BER::Extensions::FalseClass
end
# :startdoc:

View File

@ -0,0 +1,82 @@
# -*- ruby encoding: utf-8 -*-
##
# BER extensions to the Array class.
module Net::BER::Extensions::Array
##
# Converts an Array to a BER sequence. All values in the Array are
# expected to be in BER format prior to calling this method.
def to_ber(id = 0)
# The universal sequence tag 0x30 is composed of the base tag value
# (0x10) and the constructed flag (0x20).
to_ber_seq_internal(0x30 + id)
end
alias_method :to_ber_sequence, :to_ber
##
# Converts an Array to a BER set. All values in the Array are expected to
# be in BER format prior to calling this method.
def to_ber_set(id = 0)
# The universal set tag 0x31 is composed of the base tag value (0x11)
# and the constructed flag (0x20).
to_ber_seq_internal(0x31 + id)
end
##
# Converts an Array to an application-specific sequence, assigned a tag
# value that is meaningful to the particular protocol being used. All
# values in the Array are expected to be in BER format pr prior to calling
# this method.
#--
# Implementor's note 20100320(AZ): RFC 4511 (the LDAPv3 protocol) as well
# as earlier RFCs 1777 and 2559 seem to indicate that LDAP only has
# application constructed sequences (0x60). However, ldapsearch sends some
# context-specific constructed sequences (0xA0); other clients may do the
# same. This behaviour appears to violate the RFCs. In real-world
# practice, we may need to change calls of #to_ber_appsequence to
# #to_ber_contextspecific for full LDAP server compatibility.
#
# This note probably belongs elsewhere.
#++
def to_ber_appsequence(id = 0)
# The application sequence tag always starts from the application flag
# (0x40) and the constructed flag (0x20).
to_ber_seq_internal(0x60 + id)
end
##
# Converts an Array to a context-specific sequence, assigned a tag value
# that is meaningful to the particular context of the particular protocol
# being used. All values in the Array are expected to be in BER format
# prior to calling this method.
def to_ber_contextspecific(id = 0)
# The application sequence tag always starts from the context flag
# (0x80) and the constructed flag (0x20).
to_ber_seq_internal(0xa0 + id)
end
##
# The internal sequence packing routine. All values in the Array are
# expected to be in BER format prior to calling this method.
def to_ber_seq_internal(code)
s = self.join
[code].pack('C') + s.length.to_ber_length_encoding + s
end
private :to_ber_seq_internal
##
# SNMP Object Identifiers (OID) are special arrays
#--
# 20100320 AZ: I do not think that this method should be in BER, since
# this appears to be SNMP-specific. This should probably be subsumed by a
# proper SNMP OID object.
#++
def to_ber_oid
ary = self.dup
first = ary.shift
raise Net::BER::BerError, "Invalid OID" unless [0, 1, 2].include?(first)
first = first * 40 + ary.shift
ary.unshift first
oid = ary.pack("w*")
[6, oid.length].pack("CC") + oid
end
end

View File

@ -0,0 +1,22 @@
# -*- ruby encoding: utf-8 -*-
##
# BER extensions to the Bignum class.
module Net::BER::Extensions::Bignum
##
# Converts a Bignum to an uncompressed BER integer.
def to_ber
result = []
# NOTE: Array#pack's 'w' is a BER _compressed_ integer. We need
# uncompressed BER integers, so we're not using that. See also:
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/228864
n = self
while n > 0
b = n & 0xff
result << b
n = n >> 8
end
"\002" + ([result.size] + result.reverse).pack('C*')
end
end

View File

@ -0,0 +1,10 @@
# -*- ruby encoding: utf-8 -*-
##
# BER extensions to +false+.
module Net::BER::Extensions::FalseClass
##
# Converts +false+ to the BER wireline representation of +false+.
def to_ber
"\001\001\000"
end
end

View File

@ -0,0 +1,66 @@
# -*- ruby encoding: utf-8 -*-
##
# Ber extensions to the Fixnum class.
module Net::BER::Extensions::Fixnum
##
# Converts the fixnum to BER format.
def to_ber
"\002#{to_ber_internal}"
end
##
# Converts the fixnum to BER enumerated format.
def to_ber_enumerated
"\012#{to_ber_internal}"
end
##
# Converts the fixnum to BER length encodining format.
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
##
# Generate a BER-encoding for an application-defined INTEGER. Examples of
# such integers are SNMP's Counter, Gauge, and TimeTick types.
def to_ber_application(tag)
[0x40 + tag].pack("C") + to_ber_internal
end
##
# Used to BER-encode the length and content bytes of a Fixnum. Callers
# must prepend the tag byte for the contained value.
def to_ber_internal
# CAUTION: Bit twiddling ahead. You might want to shield your eyes or
# something.
# Looks for the first byte in the fixnum that is not all zeroes. It does
# this by masking one byte after another, checking the result for bits
# that are left on.
size = Net::BER::MAX_FIXNUM_SIZE
while size > 1
break if (self & (0xff << (size - 1) * 8)) > 0
size -= 1
end
# Store the size of the fixnum in the result
result = [size]
# Appends bytes to result, starting with higher orders first. Extraction
# of bytes is done by right shifting the original fixnum by an amount
# and then masking that with 0xff.
while size > 0
# right shift size - 1 bytes, mask with 0xff
result << ((self >> ((size - 1) * 8)) & 0xff)
size -= 1
end
result.pack('C*')
end
private :to_ber_internal
end

View File

@ -0,0 +1,48 @@
# -*- ruby encoding: utf-8 -*-
require 'stringio'
##
# BER extensions to the String class.
module Net::BER::Extensions::String
##
# Converts a string to a BER string. Universal octet-strings are tagged
# with 0x04, but other values are possible depending on the context, so we
# let the caller give us one.
#
# User code should call either #to_ber_application_string or
# #to_ber_contextspecific.
def to_ber(code = 0x04)
[code].pack('C') + length.to_ber_length_encoding + self
end
##
# Creates an application-specific BER string encoded value with the
# provided syntax code value.
def to_ber_application_string(code)
to_ber(0x40 + code)
end
##
# Creates a context-specific BER string encoded value with the provided
# syntax code value.
def to_ber_contextspecific(code)
to_ber(0x80 + code)
end
##
# Nondestructively reads a BER object from this string.
def read_ber(syntax = nil)
StringIO.new(self).read_ber(syntax)
end
##
# Destructively reads a BER object from the string.
def read_ber!(syntax = nil)
io = StringIO.new(self)
result = io.read_ber(syntax)
self.slice!(0...io.pos)
return result
end
end

View File

@ -0,0 +1,12 @@
# -*- ruby encoding: utf-8 -*-
##
# BER extensions to +true+.
module Net::BER::Extensions::TrueClass
##
# Converts +true+ to the BER wireline representation of +true+.
def to_ber
# 20100319 AZ: Note that this may not be the completely correct value,
# per some test documentation. We need to determine the truth of this.
"\001\001\001"
end
end

1549
vendor/gems/net-ldap-0.2.2/lib/net/ldap.rb vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,154 @@
# -*- ruby encoding: utf-8 -*-
##
# An LDAP Dataset. Used primarily as an intermediate format for converting
# to and from LDIF strings and Net::LDAP::Entry objects.
class Net::LDAP::Dataset < Hash
##
# Dataset object comments.
attr_reader :comments
def initialize(*args, &block) # :nodoc:
super
@comments = []
end
##
# Outputs an LDAP Dataset as an array of strings representing LDIF
# entries.
def to_ldif
ary = []
ary += @comments unless @comments.empty?
keys.sort.each do |dn|
ary << "dn: #{dn}"
attributes = self[dn].keys.map { |attr| attr.to_s }.sort
attributes.each do |attr|
self[dn][attr.to_sym].each do |value|
if attr == "userpassword" or value_is_binary?(value)
value = [value].pack("m").chomp.gsub(/\n/m, "\n ")
ary << "#{attr}:: #{value}"
else
ary << "#{attr}: #{value}"
end
end
end
ary << ""
end
block_given? and ary.each { |line| yield line}
ary
end
##
# Outputs an LDAP Dataset as an LDIF string.
def to_ldif_string
to_ldif.join("\n")
end
##
# Convert the parsed LDIF objects to Net::LDAP::Entry objects.
def to_entries
ary = []
keys.each do |dn|
entry = Net::LDAP::Entry.new(dn)
self[dn].each do |attr, value|
entry[attr] = value
end
ary << entry
end
ary
end
##
# This is an internal convenience method to determine if a value requires
# base64-encoding before conversion to LDIF output. The standard approach
# in most LDAP tools is to check whether the value is a password, or if
# the first or last bytes are non-printable. Microsoft Active Directory,
# on the other hand, sometimes sends values that are binary in the middle.
#
# In the worst cases, this could be a nasty performance killer, which is
# why we handle the simplest cases first. Ideally, we would also test the
# first/last byte, but it's a bit harder to do this in a way that's
# compatible with both 1.8.6 and 1.8.7.
def value_is_binary?(value) # :nodoc:
value = value.to_s
return true if value[0] == ?: or value[0] == ?<
value.each_byte { |byte| return true if (byte < 32) || (byte > 126) }
false
end
private :value_is_binary?
class << self
class ChompedIO # :nodoc:
def initialize(io)
@io = io
end
def gets
s = @io.gets
s.chomp if s
end
end
##
# Creates a Dataset object from an Entry object. Used mostly to assist
# with the conversion of
def from_entry(entry)
dataset = Net::LDAP::Dataset.new
hash = { }
entry.each_attribute do |attribute, value|
next if attribute == :dn
hash[attribute] = value
end
dataset[entry.dn] = hash
dataset
end
##
# Reads an object that returns data line-wise (using #gets) and parses
# LDIF data into a Dataset object.
def read_ldif(io)
ds = Net::LDAP::Dataset.new
io = ChompedIO.new(io)
line = io.gets
dn = nil
while line
new_line = io.gets
if new_line =~ /^[\s]+/
line << " " << $'
else
nextline = new_line
if line =~ /^#/
ds.comments << line
yield :comment, line if block_given?
elsif line =~ /^dn:[\s]*/i
dn = $'
ds[dn] = Hash.new { |k,v| k[v] = [] }
yield :dn, dn if block_given?
elsif line.empty?
dn = nil
yield :end, nil if block_given?
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.to_sym] << attrvalue
yield :attr, [$1.downcase.to_sym, attrvalue] if block_given?
end
line = nextline
end
end
ds
end
end
end
require 'net/ldap/entry' unless defined? Net::LDAP::Entry

View File

@ -0,0 +1,225 @@
# -*- ruby encoding: utf-8 -*-
##
# Objects of this class represent an LDAP DN ("Distinguished Name"). A DN
# ("Distinguished Name") is a unique identifier for an entry within an LDAP
# directory. It is made up of a number of other attributes strung together,
# to identify the entry in the tree.
#
# Each attribute that makes up a DN needs to have its value escaped so that
# the DN is valid. This class helps take care of that.
#
# A fully escaped DN needs to be unescaped when analysing its contents. This
# class also helps take care of that.
class Net::LDAP::DN
##
# Initialize a DN, escaping as required. Pass in attributes in name/value
# pairs. If there is a left over argument, it will be appended to the dn
# without escaping (useful for a base string).
#
# Most uses of this class will be to escape a DN, rather than to parse it,
# so storing the dn as an escaped String and parsing parts as required
# with a state machine seems sensible.
def initialize(*args)
buffer = StringIO.new
args.each_index do |index|
buffer << "=" if index % 2 == 1
buffer << "," if index % 2 == 0 && index != 0
if index < args.length - 1 || index % 2 == 1
buffer << Net::LDAP::DN.escape(args[index])
else
buffer << args[index]
end
end
@dn = buffer.string
end
##
# Parse a DN into key value pairs using ASN from
# http://tools.ietf.org/html/rfc2253 section 3.
def each_pair
state = :key
key = StringIO.new
value = StringIO.new
hex_buffer = ""
@dn.each_char do |char|
case state
when :key then
case char
when 'a'..'z', 'A'..'Z' then
state = :key_normal
key << char
when '0'..'9' then
state = :key_oid
key << char
when ' ' then state = :key
else raise "DN badly formed"
end
when :key_normal then
case char
when '=' then state = :value
when 'a'..'z', 'A'..'Z', '0'..'9', '-', ' ' then key << char
else raise "DN badly formed"
end
when :key_oid then
case char
when '=' then state = :value
when '0'..'9', '.', ' ' then key << char
else raise "DN badly formed"
end
when :value then
case char
when '\\' then state = :value_normal_escape
when '"' then state = :value_quoted
when ' ' then state = :value
when '#' then
state = :value_hexstring
value << char
when ',' then
state = :key
yield key.string.strip, value.string.rstrip
key = StringIO.new
value = StringIO.new;
else
state = :value_normal
value << char
end
when :value_normal then
case char
when '\\' then state = :value_normal_escape
when ',' then
state = :key
yield key.string.strip, value.string.rstrip
key = StringIO.new
value = StringIO.new;
else value << char
end
when :value_normal_escape then
case char
when '0'..'9', 'a'..'f', 'A'..'F' then
state = :value_normal_escape_hex
hex_buffer = char
else state = :value_normal; value << char
end
when :value_normal_escape_hex then
case char
when '0'..'9', 'a'..'f', 'A'..'F' then
state = :value_normal
value << "#{hex_buffer}#{char}".to_i(16).chr
else raise "DN badly formed"
end
when :value_quoted then
case char
when '\\' then state = :value_quoted_escape
when '"' then state = :value_end
else value << char
end
when :value_quoted_escape then
case char
when '0'..'9', 'a'..'f', 'A'..'F' then
state = :value_quoted_escape_hex
hex_buffer = char
else
state = :value_quoted;
value << char
end
when :value_quoted_escape_hex then
case char
when '0'..'9', 'a'..'f', 'A'..'F' then
state = :value_quoted
value << "#{hex_buffer}#{char}".to_i(16).chr
else raise "DN badly formed"
end
when :value_hexstring then
case char
when '0'..'9', 'a'..'f', 'A'..'F' then
state = :value_hexstring_hex
value << char
when ' ' then state = :value_end
when ',' then
state = :key
yield key.string.strip, value.string.rstrip
key = StringIO.new
value = StringIO.new;
else raise "DN badly formed"
end
when :value_hexstring_hex then
case char
when '0'..'9', 'a'..'f', 'A'..'F' then
state = :value_hexstring
value << char
else raise "DN badly formed"
end
when :value_end then
case char
when ' ' then state = :value_end
when ',' then
state = :key
yield key.string.strip, value.string.rstrip
key = StringIO.new
value = StringIO.new;
else raise "DN badly formed"
end
else raise "Fell out of state machine"
end
end
# Last pair
if [:value, :value_normal, :value_hexstring, :value_end].include? state
yield key.string.strip, value.string.rstrip
else
raise "DN badly formed"
end
end
##
# Returns the DN as an array in the form expected by the constructor.
def to_a
a = []
self.each_pair { |key, value| a << key << value }
a
end
##
# Return the DN as an escaped string.
def to_s
@dn
end
# http://tools.ietf.org/html/rfc2253 section 2.4 lists these exceptions
# for dn values. All of the following must be escaped in any normal string
# using a single backslash ('\') as escape.
ESCAPES = {
',' => ',',
'+' => '+',
'"' => '"',
'\\' => '\\',
'<' => '<',
'>' => '>',
';' => ';',
}
# Compiled character class regexp using the keys from the above hash, and
# checking for a space or # at the start, or space at the end, of the
# string.
ESCAPE_RE = Regexp.new("(^ |^#| $|[" +
ESCAPES.keys.map { |e| Regexp.escape(e) }.join +
"])")
##
# Escape a string for use in a DN value
def self.escape(string)
string.gsub(ESCAPE_RE) { |char| "\\" + ESCAPES[char] }
end
##
# Proxy all other requests to the string object, because a DN is mainly
# used within the library as a string
def method_missing(method, *args, &block)
@dn.send(method, *args, &block)
end
end

View File

@ -0,0 +1,185 @@
# -*- ruby encoding: utf-8 -*-
##
# 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 Net::LDAP::Entry
##
# This constructor is not generally called by user code.
def initialize(dn = nil) #:nodoc:
@myhash = {}
@myhash[:dn] = [dn]
end
##
# Use the LDIF format for Marshal serialization.
def _dump(depth) #:nodoc:
to_ldif
end
##
# Use the LDIF format for Marshal serialization.
def self._load(entry) #:nodoc:
from_single_ldif_string(entry)
end
class << self
##
# Converts a single LDIF entry string into an Entry object. Useful for
# Marshal serialization. If a string with multiple LDIF entries is
# provided, an exception will be raised.
def from_single_ldif_string(ldif)
ds = Net::LDAP::Dataset.read_ldif(::StringIO.new(ldif))
return nil if ds.empty?
raise Net::LDAP::LdapError, "Too many LDIF entries" unless ds.size == 1
entry = ds.to_entries.first
return nil if entry.dn.nil?
entry
end
##
# Canonicalizes an LDAP attribute name as a \Symbol. The name is
# lowercased and, if present, a trailing equals sign is removed.
def attribute_name(name)
name = name.to_s.downcase
name = name[0..-2] if name[-1] == ?=
name.to_sym
end
end
##
# Sets or replaces the array of values for the provided attribute. The
# attribute name is canonicalized prior to assignment.
#
# When an attribute is set using this, that attribute is now made
# accessible through methods as well.
#
# entry = Net::LDAP::Entry.new("dc=com")
# entry.foo # => NoMethodError
# entry["foo"] = 12345 # => [12345]
# entry.foo # => [12345]
def []=(name, value)
@myhash[self.class.attribute_name(name)] = Kernel::Array(value)
end
##
# Reads the array of values for the provided attribute. The attribute name
# is canonicalized prior to reading. Returns an empty array if the
# attribute does not exist.
def [](name)
name = self.class.attribute_name(name)
@myhash[name] || []
end
##
# Returns the first distinguished name (dn) of the Entry as a \String.
def dn
self[:dn].first.to_s
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 # :yields: attribute-name, data-values-array
if block_given?
attribute_names.each {|a|
attr_name,values = a,self[a]
yield attr_name, values
}
end
end
alias_method :each_attribute, :each
##
# Converts the Entry to an LDIF-formatted String
def to_ldif
Net::LDAP::Dataset.from_entry(self).to_ldif_string
end
def respond_to?(sym) #:nodoc:
return true if valid_attribute?(self.class.attribute_name(sym))
return super
end
def method_missing(sym, *args, &block) #:nodoc:
name = self.class.attribute_name(sym)
if valid_attribute?(name )
if setter?(sym) && args.size == 1
value = args.first
value = Array(value)
self[name]= value
return value
elsif args.empty?
return self[name]
end
end
super
end
# Given a valid attribute symbol, returns true.
def valid_attribute?(attr_name)
attribute_names.include?(attr_name)
end
private :valid_attribute?
# Returns true if the symbol ends with an equal sign.
def setter?(sym)
sym.to_s[-1] == ?=
end
private :setter?
end # class Entry
require 'net/ldap/dataset' unless defined? Net::LDAP::Dataset

View File

@ -0,0 +1,759 @@
# -*- ruby encoding: utf-8 -*-
##
# Class Net::LDAP::Filter is used to constrain LDAP searches. An object of
# this class is passed to Net::LDAP#search in the parameter :filter.
#
# Net::LDAP::Filter supports the complete set of search filters available in
# LDAP, including conjunction, disjunction and negation (AND, OR, and NOT).
# This class supplants the (infamous) RFC 2254 standard notation for
# specifying LDAP search filters.
#--
# NOTE: This wording needs to change as we will be supporting LDAPv3 search
# filter strings (RFC 4515).
#++
#
# Here's how to code the familiar "objectclass is present" filter:
# f = Net::LDAP::Filter.present("objectclass")
#
# The object returned by this code can be passed directly to the
# <tt>:filter</tt> parameter of Net::LDAP#search.
#
# See the individual class and instance methods below for more examples.
class Net::LDAP::Filter
##
# Known filter types.
FilterTypes = [ :ne, :eq, :ge, :le, :and, :or, :not, :ex ]
def initialize(op, left, right) #:nodoc:
unless FilterTypes.include?(op)
raise Net::LDAP::LdapError, "Invalid or unsupported operator #{op.inspect} in LDAP Filter."
end
@op = op
@left = left
@right = right
end
class << self
# We don't want filters created except using our custom constructors.
private :new
##
# Creates a Filter object indicating that the value of a particular
# attribute must either be present or match a particular string.
#
# Specifying that an attribute is 'present' means only directory entries
# which contain a value for the particular attribute will be selected by
# the filter. This is useful in case of optional attributes such as
# <tt>mail.</tt> Presence is indicated by giving the value "*" in the
# second parameter to #eq. This example selects only entries that have
# one or more values for <tt>sAMAccountName:</tt>
#
# f = Net::LDAP::Filter.eq("sAMAccountName", "*")
#
# To match a particular range of values, pass a string as the second
# parameter to #eq. The string may contain one or more "*" characters as
# wildcards: these match zero or more occurrences of any character. Full
# regular-expressions are <i>not</i> supported due to limitations in the
# underlying LDAP protocol. This example selects any entry with a
# <tt>mail</tt> value containing the substring "anderson":
#
# f = Net::LDAP::Filter.eq("mail", "*anderson*")
#
# This filter does not perform any escaping
def eq(attribute, value)
new(:eq, attribute, value)
end
##
# Creates a Filter object indicating extensible comparison. This Filter
# object is currently considered EXPERIMENTAL.
#
# sample_attributes = ['cn:fr', 'cn:fr.eq',
# 'cn:1.3.6.1.4.1.42.2.27.9.4.49.1.3', 'cn:dn:fr', 'cn:dn:fr.eq']
# attr = sample_attributes.first # Pick an extensible attribute
# value = 'roberts'
#
# filter = "#{attr}:=#{value}" # Basic String Filter
# filter = Net::LDAP::Filter.ex(attr, value) # Net::LDAP::Filter
#
# # Perform a search with the Extensible Match Filter
# Net::LDAP.search(:filter => filter)
#--
# The LDIF required to support the above examples on the OpenDS LDAP
# server:
#
# version: 1
#
# dn: dc=example,dc=com
# objectClass: domain
# objectClass: top
# dc: example
#
# dn: ou=People,dc=example,dc=com
# objectClass: organizationalUnit
# objectClass: top
# ou: People
#
# dn: uid=1,ou=People,dc=example,dc=com
# objectClass: person
# objectClass: organizationalPerson
# objectClass: inetOrgPerson
# objectClass: top
# cn:: csO0YsOpcnRz
# sn:: YsO0YiByw7Riw6lydHM=
# givenName:: YsO0Yg==
# uid: 1
#
# =Refs:
# * http://www.ietf.org/rfc/rfc2251.txt
# * http://www.novell.com/documentation/edir88/edir88/?page=/documentation/edir88/edir88/data/agazepd.html
# * https://docs.opends.org/2.0/page/SearchingUsingInternationalCollationRules
#++
def ex(attribute, value)
new(:ex, attribute, value)
end
##
# Creates a Filter object indicating that a particular attribute value
# is either not present or does not match a particular string; see
# Filter::eq for more information.
#
# This filter does not perform any escaping
def ne(attribute, value)
new(:ne, attribute, value)
end
##
# Creates a Filter object indicating that the value of a particular
# attribute must match a particular string. The attribute value is
# escaped, so the "*" character is interpreted literally.
def equals(attribute, value)
new(:eq, attribute, escape(value))
end
##
# Creates a Filter object indicating that the value of a particular
# attribute must begin with a particular string. The attribute value is
# escaped, so the "*" character is interpreted literally.
def begins(attribute, value)
new(:eq, attribute, escape(value) + "*")
end
##
# Creates a Filter object indicating that the value of a particular
# attribute must end with a particular string. The attribute value is
# escaped, so the "*" character is interpreted literally.
def ends(attribute, value)
new(:eq, attribute, "*" + escape(value))
end
##
# Creates a Filter object indicating that the value of a particular
# attribute must contain a particular string. The attribute value is
# escaped, so the "*" character is interpreted literally.
def contains(attribute, value)
new(:eq, attribute, "*" + escape(value) + "*")
end
##
# Creates a Filter object indicating that a particular attribute value
# is greater than or equal to the specified value.
def ge(attribute, value)
new(:ge, attribute, value)
end
##
# Creates a Filter object indicating that a particular attribute value
# is less than or equal to the specified value.
def le(attribute, value)
new(:le, attribute, value)
end
##
# Joins two or more filters so that all conditions must be true. Calling
# <tt>Filter.join(left, right)</tt> is the same as <tt>left &
# right</tt>.
#
# # Selects only entries that have an <tt>objectclass</tt> attribute.
# x = Net::LDAP::Filter.present("objectclass")
# # Selects only entries that have a <tt>mail</tt> attribute that begins
# # with "George".
# y = Net::LDAP::Filter.eq("mail", "George*")
# # Selects only entries that meet both conditions above.
# z = Net::LDAP::Filter.join(x, y)
def join(left, right)
new(:and, left, right)
end
##
# Creates a disjoint comparison between two or more filters. Selects
# entries where either the left or right side are true. Calling
# <tt>Filter.intersect(left, right)</tt> is the same as <tt>left |
# right</tt>.
#
# # Selects only entries that have an <tt>objectclass</tt> attribute.
# x = Net::LDAP::Filter.present("objectclass")
# # Selects only entries that have a <tt>mail</tt> attribute that begins
# # with "George".
# y = Net::LDAP::Filter.eq("mail", "George*")
# # Selects only entries that meet either condition above.
# z = x | y
def intersect(left, right)
new(:or, left, right)
end
##
# Negates a filter. Calling <tt>Fitler.negate(filter)</tt> i s the same
# as <tt>~filter</tt>.
#
# # Selects only entries that do not have an <tt>objectclass</tt>
# # attribute.
# x = ~Net::LDAP::Filter.present("objectclass")
def negate(filter)
new(:not, filter, nil)
end
##
# This is a synonym for #eq(attribute, "*"). Also known as #present and
# #pres.
def present?(attribute)
eq(attribute, "*")
end
alias_method :present, :present?
alias_method :pres, :present?
# http://tools.ietf.org/html/rfc4515 lists these exceptions from UTF1
# charset for filters. All of the following must be escaped in any normal
# string using a single backslash ('\') as escape.
#
ESCAPES = {
"\0" => '00', # NUL = %x00 ; null character
'*' => '2A', # ASTERISK = %x2A ; asterisk ("*")
'(' => '28', # LPARENS = %x28 ; left parenthesis ("(")
')' => '29', # RPARENS = %x29 ; right parenthesis (")")
'\\' => '5C', # ESC = %x5C ; esc (or backslash) ("\")
}
# Compiled character class regexp using the keys from the above hash.
ESCAPE_RE = Regexp.new(
"[" +
ESCAPES.keys.map { |e| Regexp.escape(e) }.join +
"]")
##
# Escape a string for use in an LDAP filter
def escape(string)
string.gsub(ESCAPE_RE) { |char| "\\" + ESCAPES[char] }
end
##
# Converts an LDAP search filter in BER format to an Net::LDAP::Filter
# object. The incoming BER object most likely came to us by parsing an
# LDAP searchRequest PDU. See also the comments under #to_ber, including
# the grammar snippet from the RFC.
#--
# We're hardcoding the BER constants from the RFC. These should be
# broken out insto constants.
def parse_ber(ber)
case ber.ber_identifier
when 0xa0 # context-specific constructed 0, "and"
ber.map { |b| parse_ber(b) }.inject { |memo, obj| memo & obj }
when 0xa1 # context-specific constructed 1, "or"
ber.map { |b| parse_ber(b) }.inject { |memo, obj| memo | obj }
when 0xa2 # context-specific constructed 2, "not"
~parse_ber(ber.first)
when 0xa3 # context-specific constructed 3, "equalityMatch"
if ber.last == "*"
else
eq(ber.first, ber.last)
end
when 0xa4 # context-specific constructed 4, "substring"
str = ""
final = false
ber.last.each { |b|
case b.ber_identifier
when 0x80 # context-specific primitive 0, SubstringFilter "initial"
raise Net::LDAP::LdapError, "Unrecognized substring filter; bad initial value." if str.length > 0
str += b
when 0x81 # context-specific primitive 0, SubstringFilter "any"
str += "*#{b}"
when 0x82 # context-specific primitive 0, SubstringFilter "final"
str += "*#{b}"
final = true
end
}
str += "*" unless final
eq(ber.first.to_s, str)
when 0xa5 # context-specific constructed 5, "greaterOrEqual"
ge(ber.first.to_s, ber.last.to_s)
when 0xa6 # context-specific constructed 6, "lessOrEqual"
le(ber.first.to_s, ber.last.to_s)
when 0x87 # context-specific primitive 7, "present"
# call to_s to get rid of the BER-identifiedness of the incoming string.
present?(ber.to_s)
when 0xa9 # context-specific constructed 9, "extensible comparison"
raise Net::LDAP::LdapError, "Invalid extensible search filter, should be at least two elements" if ber.size<2
# Reassembles the extensible filter parts
# (["sn", "2.4.6.8.10", "Barbara Jones", '1'])
type = value = dn = rule = nil
ber.each do |element|
case element.ber_identifier
when 0x81 then rule=element
when 0x82 then type=element
when 0x83 then value=element
when 0x84 then dn='dn'
end
end
attribute = ''
attribute << type if type
attribute << ":#{dn}" if dn
attribute << ":#{rule}" if rule
ex(attribute, value)
else
raise Net::LDAP::LdapError, "Invalid BER tag-value (#{ber.ber_identifier}) in search filter."
end
end
##
# Converts an LDAP filter-string (in the prefix syntax specified in RFC-2254)
# to a Net::LDAP::Filter.
def construct(ldap_filter_string)
FilterParser.parse(ldap_filter_string)
end
alias_method :from_rfc2254, :construct
alias_method :from_rfc4515, :construct
##
# Convert an RFC-1777 LDAP/BER "Filter" object to a Net::LDAP::Filter
# object.
#--
# TODO, we're hardcoding the RFC-1777 BER-encodings of the various
# filter types. Could pull them out into a constant.
#++
def parse_ldap_filter(obj)
case obj.ber_identifier
when 0x87 # present. context-specific primitive 7.
eq(obj.to_s, "*")
when 0xa3 # equalityMatch. context-specific constructed 3.
eq(obj[0], obj[1])
else
raise Net::LDAP::LdapError, "Unknown LDAP search-filter type: #{obj.ber_identifier}"
end
end
end
##
# Joins two or more filters so that all conditions must be true.
#
# # Selects only entries that have an <tt>objectclass</tt> attribute.
# x = Net::LDAP::Filter.present("objectclass")
# # Selects only entries that have a <tt>mail</tt> attribute that begins
# # with "George".
# y = Net::LDAP::Filter.eq("mail", "George*")
# # Selects only entries that meet both conditions above.
# z = x & y
def &(filter)
self.class.join(self, filter)
end
##
# Creates a disjoint comparison between two or more filters. Selects
# entries where either the left or right side are true.
#
# # Selects only entries that have an <tt>objectclass</tt> attribute.
# x = Net::LDAP::Filter.present("objectclass")
# # Selects only entries that have a <tt>mail</tt> attribute that begins
# # with "George".
# y = Net::LDAP::Filter.eq("mail", "George*")
# # Selects only entries that meet either condition above.
# z = x | y
def |(filter)
self.class.intersect(self, filter)
end
##
# Negates a filter.
#
# # Selects only entries that do not have an <tt>objectclass</tt>
# # attribute.
# x = ~Net::LDAP::Filter.present("objectclass")
def ~@
self.class.negate(self)
end
##
# Equality operator for filters, useful primarily for constructing unit tests.
def ==(filter)
# 20100320 AZ: We need to come up with a better way of doing this. This
# is just nasty.
str = "[@op,@left,@right]"
self.instance_eval(str) == filter.instance_eval(str)
end
def to_raw_rfc2254
case @op
when :ne
"!(#{@left}=#{@right})"
when :eq
"#{@left}=#{@right}"
when :ex
"#{@left}:=#{@right}"
when :ge
"#{@left}>=#{@right}"
when :le
"#{@left}<=#{@right}"
when :and
"&(#{@left.to_raw_rfc2254})(#{@right.to_raw_rfc2254})"
when :or
"|(#{@left.to_raw_rfc2254})(#{@right.to_raw_rfc2254})"
when :not
"!(#{@left.to_raw_rfc2254})"
end
end
##
# Converts the Filter object to an RFC 2254-compatible text format.
def to_rfc2254
"(#{to_raw_rfc2254})"
end
def to_s
to_rfc2254
end
##
# Converts the filter to BER format.
#--
# 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,
# extensibleMatch [9] MatchingRuleAssertion
# }
#
# SubstringFilter ::=
# SEQUENCE {
# type AttributeType,
# SEQUENCE OF CHOICE {
# initial [0] LDAPString,
# any [1] LDAPString,
# final [2] LDAPString
# }
# }
#
# MatchingRuleAssertion ::=
# SEQUENCE {
# matchingRule [1] MatchingRuleId OPTIONAL,
# type [2] AttributeDescription OPTIONAL,
# matchValue [3] AssertionValue,
# dnAttributes [4] BOOLEAN DEFAULT FALSE
# }
#
# Matching Rule Suffixes
# Less than [.1] or .[lt]
# Less than or equal to [.2] or [.lte]
# Equality [.3] or [.eq] (default)
# Greater than or equal to [.4] or [.gte]
# Greater than [.5] or [.gt]
# Substring [.6] or [.sub]
#
#++
def to_ber
case @op
when :eq
if @right == "*" # presence test
@left.to_s.to_ber_contextspecific(7)
elsif @right =~ /[*]/ # substring
# Parsing substrings is a little tricky. We use String#split 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, so we use a limit parameter value of
# -1: "If negative, there is no limit to the number of fields
# returned, and trailing null fields are not suppressed."
#
# 20100320 AZ: This is much simpler than the previous verison. Also,
# unnecessary regex escaping has been removed.
ary = @right.split(/[*]+/, -1)
if ary.first.empty?
first = nil
ary.shift
else
first = ary.shift.to_ber_contextspecific(0)
end
if ary.last.empty?
last = nil
ary.pop
else
last = ary.pop.to_ber_contextspecific(2)
end
seq = ary.map { |e| e.to_ber_contextspecific(1) }
seq.unshift first if first
seq.push last if last
[@left.to_s.to_ber, seq.to_ber].to_ber_contextspecific(4)
else # equality
[@left.to_s.to_ber, unescape(@right).to_ber].to_ber_contextspecific(3)
end
when :ex
seq = []
unless @left =~ /^([-;\w]*)(:dn)?(:(\w+|[.\w]+))?$/
raise Net::LDAP::LdapError, "Bad attribute #{@left}"
end
type, dn, rule = $1, $2, $4
seq << rule.to_ber_contextspecific(1) unless rule.to_s.empty? # matchingRule
seq << type.to_ber_contextspecific(2) unless type.to_s.empty? # type
seq << unescape(@right).to_ber_contextspecific(3) # matchingValue
seq << "1".to_ber_contextspecific(4) unless dn.to_s.empty? # dnAttributes
seq.to_ber_contextspecific(9)
when :ge
[@left.to_s.to_ber, unescape(@right).to_ber].to_ber_contextspecific(5)
when :le
[@left.to_s.to_ber, unescape(@right).to_ber].to_ber_contextspecific(6)
when :ne
[self.class.eq(@left, @right).to_ber].to_ber_contextspecific(2)
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)
end
end
##
# Perform filter operations against a user-supplied block. This is useful
# when implementing an LDAP directory server. The caller's block will be
# called with two arguments: first, a symbol denoting the "operation" of
# the filter; and second, an array consisting of arguments to the
# operation. The user-supplied block (which is MANDATORY) should perform
# some desired application-defined processing, and may return a
# locally-meaningful object that will appear as a parameter in the :and,
# :or and :not operations detailed below.
#
# A typical object to return from the user-supplied block is an array of
# Net::LDAP::Filter objects.
#
# These are the possible values that may be passed to the user-supplied
# block:
# * :equalityMatch (the arguments will be an attribute name and a value
# to be matched);
# * :substrings (two arguments: an attribute name and a value containing
# one or more "*" characters);
# * :present (one argument: an attribute name);
# * :greaterOrEqual (two arguments: an attribute name and a value to be
# compared against);
# * :lessOrEqual (two arguments: an attribute name and a value to be
# compared against);
# * :and (two or more arguments, each of which is an object returned
# from a recursive call to #execute, with the same block;
# * :or (two or more arguments, each of which is an object returned from
# a recursive call to #execute, with the same block; and
# * :not (one argument, which is an object returned from a recursive
# call to #execute with the the same block.
def execute(&block)
case @op
when :eq
if @right == "*"
yield :present, @left
elsif @right.index '*'
yield :substrings, @left, @right
else
yield :equalityMatch, @left, @right
end
when :ge
yield :greaterOrEqual, @left, @right
when :le
yield :lessOrEqual, @left, @right
when :or, :and
yield @op, (@left.execute(&block)), (@right.execute(&block))
when :not
yield @op, (@left.execute(&block))
end || []
end
##
# 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) #:nodoc:
if @op == operator
[@left.coalesce(operator), @right.coalesce(operator)]
else
[self]
end
end
##
#--
# We got a hash of attribute values.
# Do we match the attributes?
# Return T/F, and call match recursively as necessary.
#++
def match(entry)
case @op
when :eq
if @right == "*"
l = entry[@left] and l.length > 0
else
l = entry[@left] and l = Array(l) and l.index(@right)
end
else
raise Net::LDAP::LdapError, "Unknown filter type in match: #{@op}"
end
end
##
# Converts escaped characters (e.g., "\\28") to unescaped characters
# ("(").
def unescape(right)
right.gsub(/\\([a-fA-F\d]{2})/) { [$1.hex].pack("U") }
end
private :unescape
##
# Parses RFC 2254-style string representations of LDAP filters into Filter
# object hierarchies.
class FilterParser #:nodoc:
##
# The constructed filter.
attr_reader :filter
class << self
private :new
##
# Construct a filter tree from the provided string and return it.
def parse(ldap_filter_string)
new(ldap_filter_string).filter
end
end
def initialize(str)
require 'strscan' # Don't load strscan until we need it.
@filter = parse(StringScanner.new(str))
raise Net::LDAP::LdapError, "Invalid filter syntax." unless @filter
end
##
# Parse the string contained in the StringScanner provided. Parsing
# tries to parse a standalone expression first. If that fails, it tries
# to parse a parenthesized expression.
def parse(scanner)
parse_filter_branch(scanner) or parse_paren_expression(scanner)
end
private :parse
##
# Join ("&") and intersect ("|") operations are presented in branches.
# That is, the expression <tt>(&(test1)(test2)</tt> has two branches:
# test1 and test2. Each of these is parsed separately and then pushed
# into a branch array for filter merging using the parent operation.
#
# This method parses the branch text out into an array of filter
# objects.
def parse_branches(scanner)
branches = []
while branch = parse_paren_expression(scanner)
branches << branch
end
branches
end
private :parse_branches
##
# Join ("&") and intersect ("|") operations are presented in branches.
# That is, the expression <tt>(&(test1)(test2)</tt> has two branches:
# test1 and test2. Each of these is parsed separately and then pushed
# into a branch array for filter merging using the parent operation.
#
# This method calls #parse_branches to generate the branch list and then
# merges them into a single Filter tree by calling the provided
# operation.
def merge_branches(op, scanner)
filter = nil
branches = parse_branches(scanner)
if branches.size >= 1
filter = branches.shift
while not branches.empty?
filter = filter.__send__(op, branches.shift)
end
end
filter
end
private :merge_branches
def parse_paren_expression(scanner)
if scanner.scan(/\s*\(\s*/)
expr = if scanner.scan(/\s*\&\s*/)
merge_branches(:&, scanner)
elsif scanner.scan(/\s*\|\s*/)
merge_branches(:|, scanner)
elsif scanner.scan(/\s*\!\s*/)
br = parse_paren_expression(scanner)
~br if br
else
parse_filter_branch(scanner)
end
if expr and scanner.scan(/\s*\)\s*/)
expr
end
end
end
private :parse_paren_expression
##
# This parses a given expression inside of parentheses.
def parse_filter_branch(scanner)
scanner.scan(/\s*/)
if token = scanner.scan(/[-\w:.]*[\w]/)
scanner.scan(/\s*/)
if op = scanner.scan(/<=|>=|!=|:=|=/)
scanner.scan(/\s*/)
if value = scanner.scan(/(?:[-\w*.+@=,#\$%&!'\s]|\\[a-fA-F\d]{2})+/)
# 20100313 AZ: Assumes that "(uid=george*)" is the same as
# "(uid=george* )". The standard doesn't specify, but I can find
# no examples that suggest otherwise.
value.strip!
case op
when "="
Net::LDAP::Filter.eq(token, value)
when "!="
Net::LDAP::Filter.ne(token, value)
when "<="
Net::LDAP::Filter.le(token, value)
when ">="
Net::LDAP::Filter.ge(token, value)
when ":="
Net::LDAP::Filter.ex(token, value)
end
end
end
end
end
private :parse_filter_branch
end # class Net::LDAP::FilterParser
end # class Net::LDAP::Filter

View File

@ -0,0 +1,31 @@
# -*- ruby encoding: utf-8 -*-
require 'digest/sha1'
require 'digest/md5'
class Net::LDAP::Password
class << self
# Generate a password-hash suitable for inclusion in an LDAP attribute.
# Pass a hash type (currently supported: :md5 and :sha) and a plaintext
# password. This function will return a hashed representation.
#
#--
# STUB: This is here to fulfill the requirements of an RFC, which
# one?
#
# TODO, gotta do salted-sha and (maybe)salted-md5. Should we provide
# sha1 as a synonym for sha1? I vote no because then should you also
# provide ssha1 for symmetry?
def generate(type, str)
digest, digest_name = case type
when :md5
[Digest::MD5.new, 'MD5']
when :sha
[Digest::SHA1.new, 'SHA']
else
raise Net::LDAP::LdapError, "Unsupported password-hash type (#{type})"
end
digest << str.to_s
return "{#{digest_name}}#{[digest.digest].pack('m').chomp }"
end
end
end

View File

@ -0,0 +1,256 @@
# -*- ruby encoding: utf-8 -*-
require 'ostruct'
##
# Defines the Protocol Data Unit (PDU) for LDAP. An LDAP PDU always looks
# like a BER SEQUENCE with at least two elements: an INTEGER message ID
# number and an application-specific SEQUENCE. Some LDAPv3 packets also
# include an optional third element, a sequence of "controls" (see RFC 2251
# section 4.1.12 for more information).
#
# 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.
#
# Currently, we only support controls on SearchResult.
class Net::LDAP::PDU
class Error < RuntimeError; end
##
# This message packet is a bind request.
BindRequest = 0
BindResult = 1
UnbindRequest = 2
SearchRequest = 3
SearchReturnedData = 4
SearchResult = 5
ModifyResponse = 7
AddResponse = 9
DeleteResponse = 11
ModifyRDNResponse = 13
SearchResultReferral = 19
ExtendedRequest = 23
ExtendedResponse = 24
##
# The LDAP packet message ID.
attr_reader :message_id
alias_method :msg_id, :message_id
##
# The application protocol format tag.
attr_reader :app_tag
attr_reader :search_entry
attr_reader :search_referrals
attr_reader :search_parameters
attr_reader :bind_parameters
##
# Returns RFC-2251 Controls if any.
attr_reader :ldap_controls
alias_method :result_controls, :ldap_controls
# Messy. Does this functionality belong somewhere else?
def initialize(ber_object)
begin
@message_id = ber_object[0].to_i
# Grab the bottom five bits of the identifier so we know which type of
# PDU this is.
#
# This is safe enough in LDAP-land, but it is recommended that other
# approaches be taken for other protocols in the case that there's an
# app-specific tag that has both primitive and constructed forms.
@app_tag = ber_object[1].ber_identifier & 0x1f
@ldap_controls = []
rescue Exception => ex
raise Net::LDAP::PDU::Error, "LDAP PDU Format Error: #{ex.message}"
end
case @app_tag
when BindResult
parse_bind_response(ber_object[1])
when SearchReturnedData
parse_search_return(ber_object[1])
when SearchResultReferral
parse_search_referral(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 DeleteResponse
parse_ldap_result(ber_object[1])
when ModifyRDNResponse
parse_ldap_result(ber_object[1])
when SearchRequest
parse_ldap_search_request(ber_object[1])
when BindRequest
parse_bind_request(ber_object[1])
when UnbindRequest
parse_unbind_request(ber_object[1])
when ExtendedResponse
parse_ldap_result(ber_object[1])
else
raise LdapPduError.new("unknown pdu-type: #{@app_tag}")
end
parse_controls(ber_object[2]) if ber_object[2]
end
##
# Returns a hash which (usually) defines the members :resultCode,
# :errorMessage, and :matchedDN. These values come directly from an LDAP
# response packet returned by the remote peer. Also see #result_code.
def result
@ldap_result || {}
end
##
# 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
##
# Return serverSaslCreds, which are only present in BindResponse packets.
#--
# Messy. Does this functionality belong somewhere else? We ought to
# refactor the accessors of this class before they get any kludgier.
def result_server_sasl_creds
@ldap_result && @ldap_result[:serverSaslCreds]
end
def parse_ldap_result(sequence)
sequence.length >= 3 or raise Net::LDAP::PDU::Error, "Invalid LDAP result length."
@ldap_result = {
:resultCode => sequence[0],
:matchedDN => sequence[1],
:errorMessage => sequence[2]
}
end
private :parse_ldap_result
##
# A Bind Response may have an additional field, ID [7], serverSaslCreds,
# per RFC 2251 pgh 4.2.3.
def parse_bind_response(sequence)
sequence.length >= 3 or raise Net::LDAP::PDU::Error, "Invalid LDAP Bind Response length."
parse_ldap_result(sequence)
@ldap_result[:serverSaslCreds] = sequence[3] if sequence.length >= 4
@ldap_result
end
private :parse_bind_response
# 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 Net::LDAP::PDU::Error, "Invalid Search Response length."
@search_entry = Net::LDAP::Entry.new(sequence[0])
sequence[1].each { |seq| @search_entry[seq[0]] = seq[1] }
end
private :parse_search_return
##
# A search referral is a sequence of one or more LDAP URIs. Any number of
# search-referral replies can be returned by the server, interspersed with
# normal replies in any order.
#--
# Until I can think of a better way to do this, we'll return the referrals
# as an array. It'll be up to higher-level handlers to expose something
# reasonable to the client.
def parse_search_referral(uris)
@search_referrals = uris
end
private :parse_search_referral
##
# Per RFC 2251, an LDAP "control" is a sequence of tuples, each consisting
# of an OID, a boolean criticality flag defaulting FALSE, and an OPTIONAL
# Octet String. If only two fields are given, the second one may be either
# criticality or data, since criticality has a default value. Someday we
# may want to come back here and add support for some of more-widely used
# controls. RFC-2696 is a good example.
def parse_controls(sequence)
@ldap_controls = sequence.map do |control|
o = OpenStruct.new
o.oid, o.criticality, o.value = control[0], control[1], control[2]
if o.criticality and o.criticality.is_a?(String)
o.value = o.criticality
o.criticality = false
end
o
end
end
private :parse_controls
# (provisional, must document)
def parse_ldap_search_request(sequence)
s = OpenStruct.new
s.base_object, s.scope, s.deref_aliases, s.size_limit, s.time_limit,
s.types_only, s.filter, s.attributes = sequence
@search_parameters = s
end
private :parse_ldap_search_request
# (provisional, must document)
def parse_bind_request sequence
s = OpenStruct.new
s.version, s.name, s.authentication = sequence
@bind_parameters = s
end
private :parse_bind_request
# (provisional, must document)
# UnbindRequest has no content so this is a no-op.
def parse_unbind_request(sequence)
nil
end
private :parse_unbind_request
end
module Net
##
# Handle renamed constants Net::LdapPdu (Net::LDAP::PDU) and
# Net::LdapPduError (Net::LDAP::PDU::Error).
def self.const_missing(name) #:nodoc:
case name.to_s
when "LdapPdu"
warn "Net::#{name} has been deprecated. Use Net::LDAP::PDU instead."
Net::LDAP::PDU
when "LdapPduError"
warn "Net::#{name} has been deprecated. Use Net::LDAP::PDU::Error instead."
Net::LDAP::PDU::Error
when 'LDAP'
else
super
end
end
end # module Net

View File

@ -0,0 +1,268 @@
# -*- ruby encoding: utf-8 -*-
# :stopdoc:
module Net
class SNMP
VERSION = '0.2.2'
AsnSyntax = Net::BER.compile_syntax({
:application => {
:primitive => {
1 => :integer, # Counter32, (RFC2578 sec 2)
2 => :integer, # Gauge32 or Unsigned32, (RFC2578 sec 2)
3 => :integer # TimeTicks32, (RFC2578 sec 2)
},
:constructed => {
}
},
:context_specific => {
:primitive => {
},
:constructed => {
0 => :array, # GetRequest PDU (RFC1157 pgh 4.1.2)
1 => :array, # GetNextRequest PDU (RFC1157 pgh 4.1.3)
2 => :array # GetResponse PDU (RFC1157 pgh 4.1.4)
}
}
})
# SNMP 32-bit counter.
# Defined in RFC1155 (Structure of Mangement Information), section 6.
# A 32-bit counter is an ASN.1 application [1] implicit unsigned integer
# with a range from 0 to 2^^32 - 1.
class Counter32
def initialize value
@value = value
end
def to_ber
@value.to_ber_application(1)
end
end
# SNMP 32-bit gauge.
# Defined in RFC1155 (Structure of Mangement Information), section 6.
# A 32-bit counter is an ASN.1 application [2] implicit unsigned integer.
# This is also indistinguishable from Unsigned32. (Need to alias them.)
class Gauge32
def initialize value
@value = value
end
def to_ber
@value.to_ber_application(2)
end
end
# SNMP 32-bit timer-ticks.
# Defined in RFC1155 (Structure of Mangement Information), section 6.
# A 32-bit counter is an ASN.1 application [3] implicit unsigned integer.
class TimeTicks32
def initialize value
@value = value
end
def to_ber
@value.to_ber_application(3)
end
end
end
class SnmpPdu
class Error < StandardError; end
PduTypes = [
:get_request,
:get_next_request,
:get_response,
:set_request,
:trap
]
ErrorStatusCodes = { # Per RFC1157, pgh 4.1.1
0 => "noError",
1 => "tooBig",
2 => "noSuchName",
3 => "badValue",
4 => "readOnly",
5 => "genErr"
}
class << self
def parse ber_object
n = new
n.send :parse, ber_object
n
end
end
attr_reader :version, :community, :pdu_type, :variables, :error_status
attr_accessor :request_id, :error_index
def initialize args={}
@version = args[:version] || 0
@community = args[:community] || "public"
@pdu_type = args[:pdu_type] # leave nil unless specified; there's no reasonable default value.
@error_status = args[:error_status] || 0
@error_index = args[:error_index] || 0
@variables = args[:variables] || []
end
#--
def parse ber_object
begin
parse_ber_object ber_object
rescue Error
# Pass through any SnmpPdu::Error instances
raise $!
rescue
# Wrap any basic parsing error so it becomes a PDU-format error
raise Error.new( "snmp-pdu format error" )
end
end
private :parse
def parse_ber_object ber_object
send :version=, ber_object[0].to_i
send :community=, ber_object[1].to_s
data = ber_object[2]
case (app_tag = data.ber_identifier & 31)
when 0
send :pdu_type=, :get_request
parse_get_request data
when 1
send :pdu_type=, :get_next_request
# This PDU is identical to get-request except for the type.
parse_get_request data
when 2
send :pdu_type=, :get_response
# This PDU is identical to get-request except for the type,
# the error_status and error_index values are meaningful,
# and the fact that the variable bindings will be non-null.
parse_get_response data
else
raise Error.new( "unknown snmp-pdu type: #{app_tag}" )
end
end
private :parse_ber_object
#--
# Defined in RFC1157, pgh 4.1.2.
def parse_get_request data
send :request_id=, data[0].to_i
# data[1] is error_status, always zero.
# data[2] is error_index, always zero.
send :error_status=, 0
send :error_index=, 0
data[3].each {|n,v|
# A variable-binding, of which there may be several,
# consists of an OID and a BER null.
# We're ignoring the null, we might want to verify it instead.
unless v.is_a?(Net::BER::BerIdentifiedNull)
raise Error.new(" invalid variable-binding in get-request" )
end
add_variable_binding n, nil
}
end
private :parse_get_request
#--
# Defined in RFC1157, pgh 4.1.4
def parse_get_response data
send :request_id=, data[0].to_i
send :error_status=, data[1].to_i
send :error_index=, data[2].to_i
data[3].each {|n,v|
# A variable-binding, of which there may be several,
# consists of an OID and a BER null.
# We're ignoring the null, we might want to verify it instead.
add_variable_binding n, v
}
end
private :parse_get_response
def version= ver
unless [0,2].include?(ver)
raise Error.new("unknown snmp-version: #{ver}")
end
@version = ver
end
def pdu_type= t
unless PduTypes.include?(t)
raise Error.new("unknown pdu-type: #{t}")
end
@pdu_type = t
end
def error_status= es
unless ErrorStatusCodes.has_key?(es)
raise Error.new("unknown error-status: #{es}")
end
@error_status = es
end
def community= c
@community = c.to_s
end
#--
# Syntactic sugar
def add_variable_binding name, value=nil
@variables ||= []
@variables << [name, value]
end
def to_ber_string
[
version.to_ber,
community.to_ber,
pdu_to_ber_string
].to_ber_sequence
end
#--
# Helper method that returns a PDU payload in BER form,
# depending on the PDU type.
def pdu_to_ber_string
case pdu_type
when :get_request
[
request_id.to_ber,
error_status.to_ber,
error_index.to_ber,
[
@variables.map {|n,v|
[n.to_ber_oid, Net::BER::BerIdentifiedNull.new.to_ber].to_ber_sequence
}
].to_ber_sequence
].to_ber_contextspecific(0)
when :get_next_request
[
request_id.to_ber,
error_status.to_ber,
error_index.to_ber,
[
@variables.map {|n,v|
[n.to_ber_oid, Net::BER::BerIdentifiedNull.new.to_ber].to_ber_sequence
}
].to_ber_sequence
].to_ber_contextspecific(1)
when :get_response
[
request_id.to_ber,
error_status.to_ber,
error_index.to_ber,
[
@variables.map {|n,v|
[n.to_ber_oid, v.to_ber].to_ber_sequence
}
].to_ber_sequence
].to_ber_contextspecific(2)
else
raise Error.new( "unknown pdu-type: #{pdu_type}" )
end
end
private :pdu_to_ber_string
end
end
# :startdoc:

View File

@ -0,0 +1,59 @@
# -*- encoding: utf-8 -*-
Gem::Specification.new do |s|
s.name = %q{net-ldap}
s.version = "0.2.20110317223538"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Francis Cianfrocca", "Emiel van de Laar", "Rory O'Connell", "Kaspar Schiess", "Austin Ziegler"]
s.date = %q{2011-03-17}
s.description = %q{Net::LDAP for Ruby (also called net-ldap) implements client access for the
Lightweight Directory Access Protocol (LDAP), an IETF standard protocol for
accessing distributed directory services. Net::LDAP is written completely in
Ruby with no external dependencies. It supports most LDAP client features and a
subset of server features as well.
Net::LDAP has been tested against modern popular LDAP servers including
OpenLDAP and Active Directory. The current release is mostly compliant with
earlier versions of the IETF LDAP RFCs (22512256, 28292830, 3377, and 3771).
Our roadmap for Net::LDAP 1.0 is to gain full <em>client</em> compliance with
the most recent LDAP RFCs (45104519, plutions of 45204532).}
s.email = ["blackhedd@rubyforge.org", "gemiel@gmail.com", "rory.ocon@gmail.com", "kaspar.schiess@absurd.li", "austin@rubyforge.org"]
s.extra_rdoc_files = ["Manifest.txt", "Contributors.rdoc", "Hacking.rdoc", "History.rdoc", "License.rdoc", "README.rdoc"]
s.files = [".autotest", ".rspec", "Contributors.rdoc", "Hacking.rdoc", "History.rdoc", "License.rdoc", "Manifest.txt", "README.rdoc", "Rakefile", "autotest/discover.rb", "lib/net-ldap.rb", "lib/net/ber.rb", "lib/net/ber/ber_parser.rb", "lib/net/ber/core_ext.rb", "lib/net/ber/core_ext/array.rb", "lib/net/ber/core_ext/bignum.rb", "lib/net/ber/core_ext/false_class.rb", "lib/net/ber/core_ext/fixnum.rb", "lib/net/ber/core_ext/string.rb", "lib/net/ber/core_ext/true_class.rb", "lib/net/ldap.rb", "lib/net/ldap/dataset.rb", "lib/net/ldap/dn.rb", "lib/net/ldap/entry.rb", "lib/net/ldap/filter.rb", "lib/net/ldap/password.rb", "lib/net/ldap/pdu.rb", "lib/net/snmp.rb", "net-ldap.gemspec", "spec/integration/ssl_ber_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "spec/unit/ber/ber_spec.rb", "spec/unit/ber/core_ext/string_spec.rb", "spec/unit/ldap/dn_spec.rb", "spec/unit/ldap/entry_spec.rb", "spec/unit/ldap/filter_spec.rb", "spec/unit/ldap_spec.rb", "test/common.rb", "test/test_entry.rb", "test/test_filter.rb", "test/test_ldap_connection.rb", "test/test_ldif.rb", "test/test_password.rb", "test/test_rename.rb", "test/test_snmp.rb", "test/testdata.ldif", "testserver/ldapserver.rb", "testserver/testdata.ldif", ".gemtest"]
s.homepage = %q{http://net-ldap.rubyforge.org/}
s.rdoc_options = ["--main", "README.rdoc"]
s.require_paths = ["lib"]
s.required_ruby_version = Gem::Requirement.new(">= 1.8.7")
s.rubyforge_project = %q{net-ldap}
s.rubygems_version = %q{1.5.2}
s.summary = %q{Net::LDAP for Ruby (also called net-ldap) implements client access for the Lightweight Directory Access Protocol (LDAP), an IETF standard protocol for accessing distributed directory services}
s.test_files = ["test/test_entry.rb", "test/test_filter.rb", "test/test_ldap_connection.rb", "test/test_ldif.rb", "test/test_password.rb", "test/test_rename.rb", "test/test_snmp.rb"]
if s.respond_to? :specification_version then
s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_development_dependency(%q<hoe-git>, ["~> 1"])
s.add_development_dependency(%q<hoe-gemspec>, ["~> 1"])
s.add_development_dependency(%q<metaid>, ["~> 1"])
s.add_development_dependency(%q<flexmock>, ["~> 0.9.0"])
s.add_development_dependency(%q<rspec>, ["~> 2.0"])
s.add_development_dependency(%q<hoe>, [">= 2.9.1"])
else
s.add_dependency(%q<hoe-git>, ["~> 1"])
s.add_dependency(%q<hoe-gemspec>, ["~> 1"])
s.add_dependency(%q<metaid>, ["~> 1"])
s.add_dependency(%q<flexmock>, ["~> 0.9.0"])
s.add_dependency(%q<rspec>, ["~> 2.0"])
s.add_dependency(%q<hoe>, [">= 2.9.1"])
end
else
s.add_dependency(%q<hoe-git>, ["~> 1"])
s.add_dependency(%q<hoe-gemspec>, ["~> 1"])
s.add_dependency(%q<metaid>, ["~> 1"])
s.add_dependency(%q<flexmock>, ["~> 0.9.0"])
s.add_dependency(%q<rspec>, ["~> 2.0"])
s.add_dependency(%q<hoe>, [">= 2.9.1"])
end
end

View File

@ -0,0 +1,36 @@
require 'spec_helper'
require 'net/ldap'
describe "BER serialisation (SSL)" do
# Transmits str to #to and reads it back from #from.
#
def transmit(str)
to.write(str)
to.close
from.read
end
attr_reader :to, :from
before(:each) do
@from, @to = IO.pipe
# The production code operates on sockets, which do need #connect called
# on them to work. Pipes are more robust for this test, so we'll skip
# the #connect call since it fails.
flexmock(OpenSSL::SSL::SSLSocket).
new_instances.should_receive(:connect => nil)
@to = Net::LDAP::Connection.wrap_with_ssl(to)
@from = Net::LDAP::Connection.wrap_with_ssl(from)
end
it "should transmit strings" do
transmit('foo').should == 'foo'
end
it "should correctly transmit numbers" do
to.write 1234.to_ber
from.read_ber.should == 1234
end
end

View File

@ -0,0 +1,2 @@
--format specdoc
--colour

View File

@ -0,0 +1,5 @@
require 'net/ldap'
RSpec.configure do |config|
config.mock_with :flexmock
end

View File

@ -0,0 +1,94 @@
require 'spec_helper'
require 'net/ber'
require 'net/ldap'
describe "BER encoding of" do
RSpec::Matchers.define :properly_encode_and_decode do
match do |given|
given.to_ber.read_ber.should == given
end
end
context "arrays" do
it "should properly encode/decode []" do
[].should properly_encode_and_decode
end
it "should properly encode/decode [1,2,3]" do
ary = [1,2,3]
encoded_ary = ary.map { |el| el.to_ber }.to_ber
encoded_ary.read_ber.should == ary
end
end
context "booleans" do
it "should encode true" do
true.to_ber.should == "\x01\x01\x01"
end
it "should encode false" do
false.to_ber.should == "\x01\x01\x00"
end
end
context "numbers" do
# Sample based
{
0 => "\x02\x01\x00",
1 => "\x02\x01\x01",
127 => "\x02\x01\x7F",
128 => "\x02\x01\x80",
255 => "\x02\x01\xFF",
256 => "\x02\x02\x01\x00",
65535 => "\x02\x02\xFF\xFF",
65536 => "\x02\x03\x01\x00\x00",
16_777_215 => "\x02\x03\xFF\xFF\xFF",
0x01000000 => "\x02\x04\x01\x00\x00\x00",
0x3FFFFFFF => "\x02\x04\x3F\xFF\xFF\xFF",
0x4FFFFFFF => "\x02\x04\x4F\xFF\xFF\xFF",
# Some odd samples...
5 => "\002\001\005",
500 => "\002\002\001\364",
50_000 => "\x02\x02\xC3P",
5_000_000_000 => "\002\005\001*\005\362\000"
}.each do |number, expected_encoding|
it "should encode #{number} as #{expected_encoding.inspect}" do
number.to_ber.should == expected_encoding
end
end
# Round-trip encoding: This is mostly to be sure to cover Bignums well.
context "when decoding with #read_ber" do
it "should correctly handle powers of two" do
100.times do |p|
n = 2 << p
n.should properly_encode_and_decode
end
end
it "should correctly handle powers of ten" do
100.times do |p|
n = 5 * 10**p
n.should properly_encode_and_decode
end
end
end
end
end
describe "BER decoding of" do
context "numbers" do
it "should decode #{"\002\001\006".inspect} (6)" do
"\002\001\006".read_ber(Net::LDAP::AsnSyntax).should == 6
end
it "should decode #{"\004\007testing".inspect} ('testing')" do
"\004\007testing".read_ber(Net::LDAP::AsnSyntax).should == 'testing'
end
it "should decode an ldap bind request" do
"0$\002\001\001`\037\002\001\003\004\rAdministrator\200\vad_is_bogus".
read_ber(Net::LDAP::AsnSyntax).should ==
[1, [3, "Administrator", "ad_is_bogus"]]
end
end
end

View File

@ -0,0 +1,51 @@
require 'spec_helper'
require 'metaid'
describe String, "when extended with BER core extensions" do
describe "<- #read_ber! (consuming read_ber method)" do
context "when passed an ldap bind request and some extra data" do
attr_reader :str, :result
before(:each) do
@str = "0$\002\001\001`\037\002\001\003\004\rAdministrator\200\vad_is_bogus UNCONSUMED"
@result = str.read_ber!(Net::LDAP::AsnSyntax)
end
it "should correctly parse the ber message" do
result.should == [1, [3, "Administrator", "ad_is_bogus"]]
end
it "should leave unconsumed part of message in place" do
str.should == " UNCONSUMED"
end
context "if an exception occurs during #read_ber" do
attr_reader :initial_value
before(:each) do
stub_exception_class = Class.new(StandardError)
@initial_value = "0$\002\001\001`\037\002\001\003\004\rAdministrator\200\vad_is_bogus"
@str = initial_value.dup
# Defines a string
io = StringIO.new(initial_value)
io.meta_def :read_ber do |syntax|
read
raise stub_exception_class
end
flexmock(StringIO).should_receive(:new).and_return(io)
begin
str.read_ber!(Net::LDAP::AsnSyntax)
rescue stub_exception_class
# EMPTY ON PURPOSE
else
raise "The stub code should raise an exception!"
end
end
it "should not modify string" do
str.should == initial_value
end
end
end
end
end

View File

@ -0,0 +1,80 @@
require 'spec_helper'
require 'net/ldap/dn'
describe Net::LDAP::DN do
describe "<- .construct" do
attr_reader :dn
before(:each) do
@dn = Net::LDAP::DN.new('cn', ',+"\\<>;', 'ou=company')
end
it "should construct a Net::LDAP::DN" do
dn.should be_an_instance_of(Net::LDAP::DN)
end
it "should escape all the required characters" do
dn.to_s.should == 'cn=\\,\\+\\"\\\\\\<\\>\\;,ou=company'
end
end
describe "<- .to_a" do
context "parsing" do
{
'cn=James, ou=Company\\,\\20LLC' => ['cn','James','ou','Company, LLC'],
'cn = \ James , ou = "Comp\28ny" ' => ['cn',' James','ou','Comp(ny'],
'1.23.4= #A3B4D5 ,ou=Company' => ['1.23.4','#A3B4D5','ou','Company'],
}.each do |key, value|
context "(#{key})" do
attr_reader :dn
before(:each) do
@dn = Net::LDAP::DN.new(key)
end
it "should decode into a Net::LDAP::DN" do
dn.should be_an_instance_of(Net::LDAP::DN)
end
it "should return the correct array" do
dn.to_a.should == value
end
end
end
end
context "parsing bad input" do
[
'cn=James,',
'cn=#aa aa',
'cn="James',
'cn=J\ames',
'cn=\\',
'1.2.d=Value',
'd1.2=Value',
].each do |value|
context "(#{value})" do
attr_reader :dn
before(:each) do
@dn = Net::LDAP::DN.new(value)
end
it "should decode into a Net::LDAP::DN" do
dn.should be_an_instance_of(Net::LDAP::DN)
end
it "should raise an error on parsing" do
lambda { dn.to_a }.should raise_error
end
end
end
end
end
describe "<- .escape(str)" do
it "should escape ,, +, \", \\, <, >, and ;" do
Net::LDAP::DN.escape(',+"\\<>;').should == '\\,\\+\\"\\\\\\<\\>\\;'
end
end
end

View File

@ -0,0 +1,51 @@
require 'spec_helper'
describe Net::LDAP::Entry do
attr_reader :entry
before(:each) do
@entry = Net::LDAP::Entry.from_single_ldif_string(
%Q{dn: something
foo: foo
barAttribute: bar
}
)
end
describe "entry access" do
it "should always respond to #dn" do
entry.should respond_to(:dn)
end
context "<- #foo" do
it "should respond_to?" do
entry.should respond_to(:foo)
end
it "should return 'foo'" do
entry.foo.should == ['foo']
end
end
context "<- #Foo" do
it "should respond_to?" do
entry.should respond_to(:Foo)
end
it "should return 'foo'" do
entry.foo.should == ['foo']
end
end
context "<- #foo=" do
it "should respond_to?" do
entry.should respond_to(:foo=)
end
it "should set 'foo'" do
entry.foo= 'bar'
entry.foo.should == ['bar']
end
end
context "<- #fOo=" do
it "should return 'foo'" do
entry.fOo= 'bar'
entry.fOo.should == ['bar']
end
end
end
end

View File

@ -0,0 +1,84 @@
require 'spec_helper'
describe Net::LDAP::Filter do
describe "<- .ex(attr, value)" do
context "('foo', 'bar')" do
attr_reader :filter
before(:each) do
@filter = Net::LDAP::Filter.ex('foo', 'bar')
end
it "should convert to 'foo:=bar'" do
filter.to_s.should == '(foo:=bar)'
end
it "should survive roundtrip via to_s/from_rfc2254" do
Net::LDAP::Filter.from_rfc2254(filter.to_s).should == filter
end
it "should survive roundtrip conversion to/from ber" do
ber = filter.to_ber
Net::LDAP::Filter.parse_ber(ber.read_ber(Net::LDAP::AsnSyntax)).should ==
filter
end
end
context "various legal inputs" do
[
'(o:dn:=Ace Industry)',
'(:dn:2.4.8.10:=Dino)',
'(cn:dn:1.2.3.4.5:=John Smith)',
'(sn:dn:2.4.6.8.10:=Barbara Jones)',
'(&(sn:dn:2.4.6.8.10:=Barbara Jones))'
].each do |filter_str|
context "from_rfc2254(#{filter_str.inspect})" do
attr_reader :filter
before(:each) do
@filter = Net::LDAP::Filter.from_rfc2254(filter_str)
end
it "should decode into a Net::LDAP::Filter" do
filter.should be_an_instance_of(Net::LDAP::Filter)
end
it "should survive roundtrip conversion to/from ber" do
ber = filter.to_ber
Net::LDAP::Filter.parse_ber(ber.read_ber(Net::LDAP::AsnSyntax)).should ==
filter
end
end
end
end
end
describe "<- .construct" do
it "should accept apostrophes in filters (regression)" do
Net::LDAP::Filter.construct("uid=O'Keefe").to_rfc2254.should == "(uid=O'Keefe)"
end
end
describe "convenience filter constructors" do
def eq(attribute, value)
described_class.eq(attribute, value)
end
describe "<- .equals(attr, val)" do
it "should delegate to .eq with escaping" do
described_class.equals('dn', 'f*oo').should == eq('dn', 'f\2Aoo')
end
end
describe "<- .begins(attr, val)" do
it "should delegate to .eq with escaping" do
described_class.begins('dn', 'f*oo').should == eq('dn', 'f\2Aoo*')
end
end
describe "<- .ends(attr, val)" do
it "should delegate to .eq with escaping" do
described_class.ends('dn', 'f*oo').should == eq('dn', '*f\2Aoo')
end
end
describe "<- .contains(attr, val)" do
it "should delegate to .eq with escaping" do
described_class.contains('dn', 'f*oo').should == eq('dn', '*f\2Aoo*')
end
end
end
describe "<- .escape(str)" do
it "should escape nul, *, (, ) and \\" do
Net::LDAP::Filter.escape("\0*()\\").should == "\\00\\2A\\28\\29\\5C"
end
end
end

View File

@ -0,0 +1,48 @@
require 'spec_helper'
describe Net::LDAP::Connection do
describe "initialize" do
context "when host is not responding" do
before(:each) do
flexmock(TCPSocket).
should_receive(:new).and_raise(Errno::ECONNREFUSED)
end
it "should raise LdapError" do
lambda {
Net::LDAP::Connection.new(
:server => 'test.mocked.com',
:port => 636)
}.should raise_error(Net::LDAP::LdapError)
end
end
context "when host is blocking the port" do
before(:each) do
flexmock(TCPSocket).
should_receive(:new).and_raise(SocketError)
end
it "should raise LdapError" do
lambda {
Net::LDAP::Connection.new(
:server => 'test.mocked.com',
:port => 636)
}.should raise_error(Net::LDAP::LdapError)
end
end
context "on other exceptions" do
before(:each) do
flexmock(TCPSocket).
should_receive(:new).and_raise(NameError)
end
it "should rethrow the exception" do
lambda {
Net::LDAP::Connection.new(
:server => 'test.mocked.com',
:port => 636)
}.should raise_error(NameError)
end
end
end
end

View File

@ -0,0 +1,3 @@
# Add 'lib' to load path.
require 'test/unit'
require 'net/ldap'

View File

@ -0,0 +1,59 @@
require 'common'
=begin
class TestEntry < Test::Unit::TestCase
Commented out until I can make it a spec.
context "An instance of Entry" do
setup do
@entry = Net::LDAP::Entry.new 'cn=Barbara,o=corp'
end
should "be initialized with the DN" do
assert_equal 'cn=Barbara,o=corp', @entry.dn
end
should 'return an empty array when accessing a nonexistent attribute (index lookup)' do
assert_equal [], @entry['sn']
end
should 'return an empty array when accessing a nonexistent attribute (method call)' do
assert_equal [], @entry.sn
end
should 'create an attribute on assignment (index lookup)' do
@entry['sn'] = 'Jensen'
assert_equal ['Jensen'], @entry['sn']
end
should 'create an attribute on assignment (method call)' do
@entry.sn = 'Jensen'
assert_equal ['Jensen'], @entry.sn
end
should 'have attributes accessible by index lookup' do
@entry['sn'] = 'Jensen'
assert_equal ['Jensen'], @entry['sn']
end
should 'have attributes accessible using a Symbol as the index' do
@entry[:sn] = 'Jensen'
assert_equal ['Jensen'], @entry[:sn]
end
should 'have attributes accessible by method call' do
@entry['sn'] = 'Jensen'
assert_equal ['Jensen'], @entry.sn
end
should 'ignore case of attribute names' do
@entry['sn'] = 'Jensen'
assert_equal ['Jensen'], @entry.sn
assert_equal ['Jensen'], @entry.Sn
assert_equal ['Jensen'], @entry.SN
assert_equal ['Jensen'], @entry['sn']
assert_equal ['Jensen'], @entry['Sn']
assert_equal ['Jensen'], @entry['SN']
end
end
end
=end

View File

@ -0,0 +1,122 @@
require 'common'
class TestFilter < Test::Unit::TestCase
Filter = Net::LDAP::Filter
def test_bug_7534_rfc2254
assert_equal("(cn=Tim Wizard)",
Filter.from_rfc2254("(cn=Tim Wizard)").to_rfc2254)
end
def test_invalid_filter_string
assert_raises(Net::LDAP::LdapError) { Filter.from_rfc2254("") }
end
def test_invalid_filter
assert_raises(Net::LDAP::LdapError) {
# This test exists to prove that our constructor blocks unknown filter
# types. All filters must be constructed using helpers.
Filter.__send__(:new, :xx, nil, nil)
}
end
def test_to_s
assert_equal("(uid=george *)", Filter.eq("uid", "george *").to_s)
end
def test_convenience_filters
assert_equal("(uid=\\2A)", Filter.equals("uid", "*").to_s)
assert_equal("(uid=\\28*)", Filter.begins("uid", "(").to_s)
assert_equal("(uid=*\\29)", Filter.ends("uid", ")").to_s)
assert_equal("(uid=*\\5C*)", Filter.contains("uid", "\\").to_s)
end
def test_c2
assert_equal("(uid=george *)",
Filter.from_rfc2254("uid=george *").to_rfc2254)
assert_equal("(uid:=george *)",
Filter.from_rfc2254("uid:=george *").to_rfc2254)
assert_equal("(uid=george*)",
Filter.from_rfc2254(" ( uid = george* ) ").to_rfc2254)
assert_equal("(!(uid=george*))",
Filter.from_rfc2254("uid!=george*").to_rfc2254)
assert_equal("(uid<=george*)",
Filter.from_rfc2254("uid <= george*").to_rfc2254)
assert_equal("(uid>=george*)",
Filter.from_rfc2254("uid>=george*").to_rfc2254)
assert_equal("(&(uid=george*)(mail=*))",
Filter.from_rfc2254("(& (uid=george* ) (mail=*))").to_rfc2254)
assert_equal("(|(uid=george*)(mail=*))",
Filter.from_rfc2254("(| (uid=george* ) (mail=*))").to_rfc2254)
assert_equal("(!(mail=*))",
Filter.from_rfc2254("(! (mail=*))").to_rfc2254)
end
def test_filter_with_single_clause
assert_equal("(cn=name)", Net::LDAP::Filter.construct("(&(cn=name))").to_s)
end
def test_filters_from_ber
[
Net::LDAP::Filter.eq("objectclass", "*"),
Net::LDAP::Filter.pres("objectclass"),
Net::LDAP::Filter.eq("objectclass", "ou"),
Net::LDAP::Filter.ge("uid", "500"),
Net::LDAP::Filter.le("uid", "500"),
(~ Net::LDAP::Filter.pres("objectclass")),
(Net::LDAP::Filter.pres("objectclass") & Net::LDAP::Filter.pres("ou")),
(Net::LDAP::Filter.pres("objectclass") & Net::LDAP::Filter.pres("ou") & Net::LDAP::Filter.pres("sn")),
(Net::LDAP::Filter.pres("objectclass") | Net::LDAP::Filter.pres("ou") | Net::LDAP::Filter.pres("sn")),
Net::LDAP::Filter.eq("objectclass", "*aaa"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*ccc"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb*ccc"),
Net::LDAP::Filter.eq("objectclass", "abc*def*1111*22*g"),
Net::LDAP::Filter.eq("objectclass", "*aaa*"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*"),
Net::LDAP::Filter.eq("objectclass", "*aaa*bbb*ccc*"),
Net::LDAP::Filter.eq("objectclass", "aaa*"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb*"),
Net::LDAP::Filter.eq("objectclass", "aaa*bbb*ccc*"),
].each do |ber|
f = Net::LDAP::Filter.parse_ber(ber.to_ber.read_ber(Net::LDAP::AsnSyntax))
assert(f == ber)
assert_equal(f.to_ber, ber.to_ber)
end
end
def test_ber_from_rfc2254_filter
[
Net::LDAP::Filter.construct("objectclass=*"),
Net::LDAP::Filter.construct("objectclass=ou"),
Net::LDAP::Filter.construct("uid >= 500"),
Net::LDAP::Filter.construct("uid <= 500"),
Net::LDAP::Filter.construct("(!(uid=*))"),
Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*))"),
Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*)(sn=*))"),
Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*))"),
Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*)(sn=*))"),
Net::LDAP::Filter.construct("objectclass=*aaa"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb"),
Net::LDAP::Filter.construct("objectclass=*aaa bbb"),
Net::LDAP::Filter.construct("objectclass=*aaa bbb"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc"),
Net::LDAP::Filter.construct("objectclass=abc*def*1111*22*g"),
Net::LDAP::Filter.construct("objectclass=*aaa*"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb*"),
Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc*"),
Net::LDAP::Filter.construct("objectclass=aaa*"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb*"),
Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc*"),
].each do |ber|
f = Net::LDAP::Filter.parse_ber(ber.to_ber.read_ber(Net::LDAP::AsnSyntax))
assert(f == ber)
assert_equal(f.to_ber, ber.to_ber)
end
end
end

View File

@ -0,0 +1,24 @@
require 'common'
class TestLDAP < Test::Unit::TestCase
def test_modify_ops_delete
args = { :operations => [ [ :delete, "mail" ] ] }
result = Net::LDAP::Connection.modify_ops(args[:operations])
expected = [ "0\r\n\x01\x010\b\x04\x04mail1\x00" ]
assert_equal(expected, result)
end
def test_modify_ops_add
args = { :operations => [ [ :add, "mail", "testuser@example.com" ] ] }
result = Net::LDAP::Connection.modify_ops(args[:operations])
expected = [ "0#\n\x01\x000\x1E\x04\x04mail1\x16\x04\x14testuser@example.com" ]
assert_equal(expected, result)
end
def test_modify_ops_replace
args = { :operations =>[ [ :replace, "mail", "testuser@example.com" ] ] }
result = Net::LDAP::Connection.modify_ops(args[:operations])
expected = [ "0#\n\x01\x020\x1E\x04\x04mail1\x16\x04\x14testuser@example.com" ]
assert_equal(expected, result)
end
end

View File

@ -0,0 +1,67 @@
# $Id: testldif.rb 61 2006-04-18 20:55:55Z blackhedd $
require 'common'
require 'digest/sha1'
require 'base64'
class TestLdif < Test::Unit::TestCase
TestLdifFilename = "#{File.dirname(__FILE__)}/testdata.ldif"
def test_empty_ldif
ds = Net::LDAP::Dataset.read_ldif(StringIO.new)
assert_equal(true, ds.empty?)
end
def test_ldif_with_comments
str = ["# Hello from LDIF-land", "# This is an unterminated comment"]
io = StringIO.new(str[0] + "\r\n" + str[1])
ds = Net::LDAP::Dataset::read_ldif(io)
assert_equal(str, ds.comments)
end
def test_ldif_with_password
psw = "goldbricks"
hashed_psw = "{SHA}" + Base64::encode64(Digest::SHA1.digest(psw)).chomp
ldif_encoded = Base64::encode64(hashed_psw).chomp
ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: Goldbrick\r\nuserPassword:: #{ldif_encoded}\r\n\r\n"))
recovered_psw = ds["Goldbrick"][:userpassword].shift
assert_equal(hashed_psw, recovered_psw)
end
def test_ldif_with_continuation_lines
ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
assert_equal(true, ds.has_key?("abcdefg hijklmn"))
end
# TODO, INADEQUATE. We need some more tests
# to verify the content.
def test_ldif
File.open(TestLdifFilename, "r") {|f|
ds = Net::LDAP::Dataset::read_ldif(f)
assert_equal(13, ds.length)
}
end
# Must test folded lines and base64-encoded lines as well as normal ones.
def test_to_ldif
data = File.open(TestLdifFilename, "rb") { |f| f.read }
io = StringIO.new(data)
# added .lines to turn to array because 1.9 doesn't have
# .grep on basic strings
entries = data.lines.grep(/^dn:\s*/) { $'.chomp }
dn_entries = entries.dup
ds = Net::LDAP::Dataset::read_ldif(io) { |type, value|
case type
when :dn
assert_equal(dn_entries.first, value)
dn_entries.shift
end
}
assert_equal(entries.size, ds.size)
assert_equal(entries.sort, ds.to_ldif.grep(/^dn:\s*/) { $'.chomp })
end
end

View File

@ -0,0 +1,17 @@
# $Id: testpsw.rb 72 2006-04-24 21:58:14Z blackhedd $
require 'common'
class TestPassword < Test::Unit::TestCase
def test_psw
assert_equal(
"{MD5}xq8jwrcfibi0sZdZYNkSng==",
Net::LDAP::Password.generate( :md5, "cashflow" ))
assert_equal(
"{SHA}YE4eGkN4BvwNN1f5R7CZz0kFn14=",
Net::LDAP::Password.generate( :sha, "cashflow" ))
end
end

View File

@ -0,0 +1,77 @@
require 'common'
# Commented out since it assumes you have a live LDAP server somewhere. This
# will be migrated to the integration specs, as soon as they are ready.
=begin
class TestRename < Test::Unit::TestCase
HOST= '10.10.10.71'
PORT = 389
BASE = "o=test"
AUTH = { :method => :simple, :username => "cn=testadmin,#{BASE}", :password => 'password' }
BASIC_USER = "cn=jsmith,ou=sales,#{BASE}"
RENAMED_USER = "cn=jbrown,ou=sales,#{BASE}"
MOVED_USER = "cn=jsmith,ou=marketing,#{BASE}"
RENAMED_MOVED_USER = "cn=jjones,ou=marketing,#{BASE}"
def setup
# create the entries we're going to manipulate
Net::LDAP::open(:host => HOST, :port => PORT, :auth => AUTH) do |ldap|
if ldap.add(:dn => "ou=sales,#{BASE}", :attributes => { :ou => "sales", :objectclass => "organizationalUnit" })
puts "Add failed: #{ldap.get_operation_result.message} - code: #{ldap.get_operation_result.code}"
end
ldap.add(:dn => "ou=marketing,#{BASE}", :attributes => { :ou => "marketing", :objectclass => "organizationalUnit" })
ldap.add(:dn => BASIC_USER, :attributes => { :cn => "jsmith", :objectclass => "inetOrgPerson", :sn => "Smith" })
end
end
def test_rename_entry
dn = nil
Net::LDAP::open(:host => HOST, :port => PORT, :auth => AUTH) do |ldap|
ldap.rename(:olddn => BASIC_USER, :newrdn => "cn=jbrown")
ldap.search(:base => RENAMED_USER) do |entry|
dn = entry.dn
end
end
assert_equal(RENAMED_USER, dn)
end
def test_move_entry
dn = nil
Net::LDAP::open(:host => HOST, :port => PORT, :auth => AUTH) do |ldap|
ldap.rename(:olddn => BASIC_USER, :newrdn => "cn=jsmith", :new_superior => "ou=marketing,#{BASE}")
ldap.search(:base => MOVED_USER) do |entry|
dn = entry.dn
end
end
assert_equal(MOVED_USER, dn)
end
def test_move_and_rename_entry
dn = nil
Net::LDAP::open(:host => HOST, :port => PORT, :auth => AUTH) do |ldap|
ldap.rename(:olddn => BASIC_USER, :newrdn => "cn=jjones", :new_superior => "ou=marketing,#{BASE}")
ldap.search(:base => RENAMED_MOVED_USER) do |entry|
dn = entry.dn
end
end
assert_equal(RENAMED_MOVED_USER, dn)
end
def teardown
# delete the entries
# note: this doesn't always completely clear up on eDirectory as objects get locked while
# the rename/move is being completed on the server and this prevents the delete from happening
Net::LDAP::open(:host => HOST, :port => PORT, :auth => AUTH) do |ldap|
ldap.delete(:dn => BASIC_USER)
ldap.delete(:dn => RENAMED_USER)
ldap.delete(:dn => MOVED_USER)
ldap.delete(:dn => RENAMED_MOVED_USER)
ldap.delete(:dn => "ou=sales,#{BASE}")
ldap.delete(:dn => "ou=marketing,#{BASE}")
end
end
end
=end

View File

@ -0,0 +1,114 @@
# $Id: testsnmp.rb 231 2006-12-21 15:09:29Z blackhedd $
require 'common'
require 'net/snmp'
class TestSnmp < Test::Unit::TestCase
SnmpGetRequest = "0'\002\001\000\004\006public\240\032\002\002?*\002\001\000\002\001\0000\0160\f\006\b+\006\001\002\001\001\001\000\005\000"
SnmpGetResponse = "0+\002\001\000\004\006public\242\036\002\002'\017\002\001\000\002\001\0000\0220\020\006\b+\006\001\002\001\001\001\000\004\004test"
SnmpGetRequestXXX = "0'\002\001\000\004\006xxxxxx\240\032\002\002?*\002\001\000\002\001\0000\0160\f\006\b+\006\001\002\001\001\001\000\005\000"
def test_invalid_packet
data = "xxxx"
assert_raise(Net::BER::BerError) {
ary = data.read_ber(Net::SNMP::AsnSyntax)
}
end
# The method String#read_ber! added by Net::BER consumes a well-formed BER
# object from the head of a string. If it doesn't find a complete,
# well-formed BER object, it returns nil and leaves the string unchanged.
# If it finds an object, it returns the object and removes it from the
# head of the string. This is good for handling partially-received data
# streams, such as from network connections.
def _test_consume_string
data = "xxx"
assert_equal(nil, data.read_ber!)
assert_equal("xxx", data)
data = SnmpGetRequest + "!!!"
ary = data.read_ber!(Net::SNMP::AsnSyntax)
assert_equal("!!!", data)
assert ary.is_a?(Array)
assert ary.is_a?(Net::BER::BerIdentifiedArray)
end
def test_weird_packet
assert_raise(Net::SnmpPdu::Error) {
Net::SnmpPdu.parse("aaaaaaaaaaaaaa")
}
end
def test_get_request
data = SnmpGetRequest.dup
pkt = data.read_ber(Net::SNMP::AsnSyntax)
assert pkt.is_a?(Net::BER::BerIdentifiedArray)
assert_equal(48, pkt.ber_identifier) # Constructed [0], signifies GetRequest
pdu = Net::SnmpPdu.parse(pkt)
assert_equal(:get_request, pdu.pdu_type)
assert_equal(16170, pdu.request_id) # whatever was in the test data. 16170 is not magic.
assert_equal([[[1, 3, 6, 1, 2, 1, 1, 1, 0], nil]], pdu.variables)
assert_equal(pdu.to_ber_string, SnmpGetRequest)
end
def test_empty_pdu
pdu = Net::SnmpPdu.new
assert_raise(Net::SnmpPdu::Error) { pdu.to_ber_string }
end
def test_malformations
pdu = Net::SnmpPdu.new
pdu.version = 0
pdu.version = 2
assert_raise(Net::SnmpPdu::Error) { pdu.version = 100 }
pdu.pdu_type = :get_request
pdu.pdu_type = :get_next_request
pdu.pdu_type = :get_response
pdu.pdu_type = :set_request
pdu.pdu_type = :trap
assert_raise(Net::SnmpPdu::Error) { pdu.pdu_type = :something_else }
end
def test_make_response
pdu = Net::SnmpPdu.new
pdu.version = 0
pdu.community = "public"
pdu.pdu_type = :get_response
pdu.request_id = 9999
pdu.error_status = 0
pdu.error_index = 0
pdu.add_variable_binding [1, 3, 6, 1, 2, 1, 1, 1, 0], "test"
assert_equal(SnmpGetResponse, pdu.to_ber_string)
end
def test_make_bad_response
pdu = Net::SnmpPdu.new
assert_raise(Net::SnmpPdu::Error) {pdu.to_ber_string}
pdu.pdu_type = :get_response
pdu.request_id = 999
pdu.to_ber_string
# Not specifying variables doesn't create an error. (Maybe it should?)
end
def test_snmp_integers
c32 = Net::SNMP::Counter32.new(100)
assert_equal("A\001d", c32.to_ber)
g32 = Net::SNMP::Gauge32.new(100)
assert_equal("B\001d", g32.to_ber)
t32 = Net::SNMP::TimeTicks32.new(100)
assert_equal("C\001d", t32.to_ber)
end
def test_community
data = SnmpGetRequestXXX.dup
ary = data.read_ber(Net::SNMP::AsnSyntax)
pdu = Net::SnmpPdu.parse(ary)
assert_equal("xxxxxx", pdu.community)
end
end

View File

@ -0,0 +1,210 @@
# $Id$
#
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
# Gmail account: garbagecat10.
#
# This is an LDAP server intended for unit testing of Net::LDAP.
# It implements as much of the protocol as we have the stomach
# to implement but serves static data. Use ldapsearch to test
# this server!
#
# To make this easier to write, we use the Ruby/EventMachine
# reactor library.
#
#------------------------------------------------
module LdapServer
LdapServerAsnSyntax = {
:application => {
:constructed => {
0 => :array, # LDAP BindRequest
3 => :array # LDAP SearchRequest
},
:primitive => {
2 => :string, # ldapsearch sends this to unbind
}
},
:context_specific => {
:primitive => {
0 => :string, # simple auth (password)
7 => :string # present filter
},
:constructed => {
3 => :array # equality filter
},
}
}
def post_init
$logger.info "Accepted LDAP connection"
@authenticated = false
end
def receive_data data
@data ||= ""; @data << data
while pdu = @data.read_ber!(LdapServerAsnSyntax)
begin
handle_ldap_pdu pdu
rescue
$logger.error "closing connection due to error #{$!}"
close_connection
end
end
end
def handle_ldap_pdu pdu
tag_id = pdu[1].ber_identifier
case tag_id
when 0x60
handle_bind_request pdu
when 0x63
handle_search_request pdu
when 0x42
# bizarre thing, it's a null object (primitive application-2)
# sent by ldapsearch to request an unbind (or a kiss-off, not sure which)
close_connection_after_writing
else
$logger.error "received unknown packet-type #{tag_id}"
close_connection_after_writing
end
end
def handle_bind_request pdu
# TODO, return a proper LDAP error instead of blowing up on version error
if pdu[1][0] != 3
send_ldap_response 1, pdu[0].to_i, 2, "", "We only support version 3"
elsif pdu[1][1] != "cn=bigshot,dc=bayshorenetworks,dc=com"
send_ldap_response 1, pdu[0].to_i, 48, "", "Who are you?"
elsif pdu[1][2].ber_identifier != 0x80
send_ldap_response 1, pdu[0].to_i, 7, "", "Keep it simple, man"
elsif pdu[1][2] != "opensesame"
send_ldap_response 1, pdu[0].to_i, 49, "", "Make my day"
else
@authenticated = true
send_ldap_response 1, pdu[0].to_i, 0, pdu[1][1], "I'll take it"
end
end
#--
# Search Response ::=
# CHOICE {
# entry [APPLICATION 4] SEQUENCE {
# objectName LDAPDN,
# attributes SEQUENCE OF SEQUENCE {
# AttributeType,
# SET OF AttributeValue
# }
# },
# resultCode [APPLICATION 5] LDAPResult
# }
def handle_search_request pdu
unless @authenticated
# NOTE, early exit.
send_ldap_response 5, pdu[0].to_i, 50, "", "Who did you say you were?"
return
end
treebase = pdu[1][0]
if treebase != "dc=bayshorenetworks,dc=com"
send_ldap_response 5, pdu[0].to_i, 32, "", "unknown treebase"
return
end
msgid = pdu[0].to_i.to_ber
# pdu[1][7] is the list of requested attributes.
# If it's an empty array, that means that *all* attributes were requested.
requested_attrs = if pdu[1][7].length > 0
pdu[1][7].map {|a| a.downcase}
else
:all
end
filters = pdu[1][6]
if filters.length == 0
# NOTE, early exit.
send_ldap_response 5, pdu[0].to_i, 53, "", "No filter specified"
end
# TODO, what if this returns nil?
filter = Net::LDAP::Filter.parse_ldap_filter( filters )
$ldif.each {|dn, entry|
if filter.match( entry )
attrs = []
entry.each {|k, v|
if requested_attrs == :all or requested_attrs.include?(k.downcase)
attrvals = v.map {|v1| v1.to_ber}.to_ber_set
attrs << [k.to_ber, attrvals].to_ber_sequence
end
}
appseq = [dn.to_ber, attrs.to_ber_sequence].to_ber_appsequence(4)
pkt = [msgid.to_ber, appseq].to_ber_sequence
send_data pkt
end
}
send_ldap_response 5, pdu[0].to_i, 0, "", "Was that what you wanted?"
end
def send_ldap_response pkt_tag, msgid, code, dn, text
send_data( [msgid.to_ber, [code.to_ber, dn.to_ber, text.to_ber].to_ber_appsequence(pkt_tag) ].to_ber )
end
end
#------------------------------------------------
# Rather bogus, a global method, which reads a HARDCODED filename
# parses out LDIF data. It will be used to serve LDAP queries out of this server.
#
def load_test_data
ary = File.readlines( "./testdata.ldif" )
hash = {}
while line = ary.shift and line.chomp!
if line =~ /^dn:[\s]*/i
dn = $'
hash[dn] = {}
while attr = ary.shift and attr.chomp! and attr =~ /^([\w]+)[\s]*:[\s]*/
hash[dn][$1.downcase] ||= []
hash[dn][$1.downcase] << $'
end
end
end
hash
end
#------------------------------------------------
if __FILE__ == $0
require 'rubygems'
require 'eventmachine'
require 'logger'
$logger = Logger.new $stderr
$logger.info "adding ../lib to loadpath, to pick up dev version of Net::LDAP."
$:.unshift "../lib"
$ldif = load_test_data
require 'net/ldap'
EventMachine.run {
$logger.info "starting LDAP server on 127.0.0.1 port 3890"
EventMachine.start_server "127.0.0.1", 3890, LdapServer
EventMachine.add_periodic_timer 60, proc {$logger.info "heartbeat"}
}
end

View File

@ -0,0 +1,101 @@
# $Id$
#
# This is test-data for an LDAP server in LDIF format.
#
dn: dc=bayshorenetworks,dc=com
objectClass: dcObject
objectClass: organization
o: Bayshore Networks LLC
dc: bayshorenetworks
dn: cn=Manager,dc=bayshorenetworks,dc=com
objectClass: organizationalrole
cn: Manager
dn: ou=people,dc=bayshorenetworks,dc=com
objectClass: organizationalunit
ou: people
dn: ou=privileges,dc=bayshorenetworks,dc=com
objectClass: organizationalunit
ou: privileges
dn: ou=roles,dc=bayshorenetworks,dc=com
objectClass: organizationalunit
ou: roles
dn: ou=office,dc=bayshorenetworks,dc=com
objectClass: organizationalunit
ou: office
dn: mail=nogoodnik@steamheat.net,ou=people,dc=bayshorenetworks,dc=com
cn: Bob Fosse
mail: nogoodnik@steamheat.net
sn: Fosse
ou: people
objectClass: top
objectClass: inetorgperson
objectClass: authorizedperson
hasAccessRole: uniqueIdentifier=engineer,ou=roles
hasAccessRole: uniqueIdentifier=ldapadmin,ou=roles
hasAccessRole: uniqueIdentifier=ldapsuperadmin,ou=roles
hasAccessRole: uniqueIdentifier=ogilvy_elephant_user,ou=roles
hasAccessRole: uniqueIdentifier=ogilvy_eagle_user,ou=roles
hasAccessRole: uniqueIdentifier=greenplug_user,ou=roles
hasAccessRole: uniqueIdentifier=brandplace_logging_user,ou=roles
hasAccessRole: uniqueIdentifier=brandplace_report_user,ou=roles
hasAccessRole: uniqueIdentifier=workorder_user,ou=roles
hasAccessRole: uniqueIdentifier=bayshore_eagle_user,ou=roles
hasAccessRole: uniqueIdentifier=bayshore_eagle_superuser,ou=roles
hasAccessRole: uniqueIdentifier=kledaras_user,ou=roles
dn: mail=elephant@steamheat.net,ou=people,dc=bayshorenetworks,dc=com
cn: Gwen Verdon
mail: elephant@steamheat.net
sn: Verdon
ou: people
objectClass: top
objectClass: inetorgperson
objectClass: authorizedperson
hasAccessRole: uniqueIdentifier=brandplace_report_user,ou=roles
hasAccessRole: uniqueIdentifier=engineer,ou=roles
hasAccessRole: uniqueIdentifier=ogilvy_elephant_user,ou=roles
hasAccessRole: uniqueIdentifier=ldapsuperadmin,ou=roles
hasAccessRole: uniqueIdentifier=ldapadmin,ou=roles
dn: uniqueIdentifier=engineering,ou=privileges,dc=bayshorenetworks,dc=com
uniqueIdentifier: engineering
ou: privileges
objectClass: accessPrivilege
dn: uniqueIdentifier=engineer,ou=roles,dc=bayshorenetworks,dc=com
uniqueIdentifier: engineer
ou: roles
objectClass: accessRole
hasAccessPrivilege: uniqueIdentifier=engineering,ou=privileges
dn: uniqueIdentifier=ldapadmin,ou=roles,dc=bayshorenetworks,dc=com
uniqueIdentifier: ldapadmin
ou: roles
objectClass: accessRole
dn: uniqueIdentifier=ldapsuperadmin,ou=roles,dc=bayshorenetworks,dc=com
uniqueIdentifier: ldapsuperadmin
ou: roles
objectClass: accessRole
dn: mail=catperson@steamheat.net,ou=people,dc=bayshorenetworks,dc=com
cn: Sid Sorokin
mail: catperson@steamheat.net
sn: Sorokin
ou: people
objectClass: top
objectClass: inetorgperson
objectClass: authorizedperson
hasAccessRole: uniqueIdentifier=engineer,ou=roles
hasAccessRole: uniqueIdentifier=ogilvy_elephant_user,ou=roles
hasAccessRole: uniqueIdentifier=ldapsuperadmin,ou=roles
hasAccessRole: uniqueIdentifier=ogilvy_eagle_user,ou=roles
hasAccessRole: uniqueIdentifier=greenplug_user,ou=roles
hasAccessRole: uniqueIdentifier=workorder_user,ou=roles

View File

@ -1,272 +0,0 @@
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.

View File

@ -1,58 +0,0 @@
= 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

View File

@ -1,55 +0,0 @@
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.

View File

@ -1,32 +0,0 @@
= 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

View File

@ -1,294 +0,0 @@
# $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

File diff suppressed because it is too large Load Diff

View File

@ -1,108 +0,0 @@
# $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

View File

@ -1,165 +0,0 @@
# $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[name] = value
else
raise NoMethodError.new( "undefined method '#{s}'" )
end
end
def write
end
end # class Entry
end # class LDAP
end # module Net

View File

@ -1,387 +0,0 @@
# $Id: filter.rb 151 2006-08-15 08:34:53Z 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 Net::LDAP::Filter is used to constrain
# LDAP searches. An object of this class is
# passed to Net::LDAP#search in the parameter :filter.
#
# Net::LDAP::Filter supports the complete set of search filters
# available in LDAP, including conjunction, disjunction and negation
# (AND, OR, and NOT). This class supplants the (infamous) RFC-2254
# standard notation for specifying LDAP search filters.
#
# Here's how to code the familiar "objectclass is present" filter:
# f = Net::LDAP::Filter.pres( "objectclass" )
# The object returned by this code can be passed directly to
# the <tt>:filter</tt> parameter of Net::LDAP#search.
#
# See the individual class and instance methods below for more examples.
#
class Filter
def initialize op, a, b
@op = op
@left = a
@right = b
end
# #eq creates a filter object indicating that the value of
# a paticular attribute must be either <i>present</i> or must
# match a particular string.
#
# To specify that an attribute is "present" means that only
# directory entries which contain a value for the particular
# attribute will be selected by the filter. This is useful
# in case of optional attributes such as <tt>mail.</tt>
# Presence is indicated by giving the value "*" in the second
# parameter to #eq. This example selects only entries that have
# one or more values for <tt>sAMAccountName:</tt>
# f = Net::LDAP::Filter.eq( "sAMAccountName", "*" )
#
# To match a particular range of values, pass a string as the
# second parameter to #eq. The string may contain one or more
# "*" characters as wildcards: these match zero or more occurrences
# of any character. Full regular-expressions are <i>not</i> supported
# due to limitations in the underlying LDAP protocol.
# This example selects any entry with a <tt>mail</tt> value containing
# the substring "anderson":
# f = Net::LDAP::Filter.eq( "mail", "*anderson*" )
#--
# Removed gt and lt. They ain't in the standard!
#
def Filter::eq attribute, value; Filter.new :eq, attribute, value; end
def Filter::ne attribute, value; Filter.new :ne, attribute, value; end
#def Filter::gt attribute, value; Filter.new :gt, attribute, value; end
#def Filter::lt attribute, value; Filter.new :lt, attribute, value; end
def Filter::ge attribute, value; Filter.new :ge, attribute, value; end
def Filter::le attribute, value; Filter.new :le, attribute, value; end
# #pres( attribute ) is a synonym for #eq( attribute, "*" )
#
def Filter::pres attribute; Filter.eq attribute, "*"; end
# operator & ("AND") is used to conjoin two or more filters.
# This expression will select only entries that have an <tt>objectclass</tt>
# attribute AND have a <tt>mail</tt> attribute that begins with "George":
# f = Net::LDAP::Filter.pres( "objectclass" ) & Net::LDAP::Filter.eq( "mail", "George*" )
#
def & filter; Filter.new :and, self, filter; end
# operator | ("OR") is used to disjoin two or more filters.
# This expression will select entries that have either an <tt>objectclass</tt>
# attribute OR a <tt>mail</tt> attribute that begins with "George":
# f = Net::LDAP::Filter.pres( "objectclass" ) | Net::LDAP::Filter.eq( "mail", "George*" )
#
def | filter; Filter.new :or, self, filter; end
#
# operator ~ ("NOT") is used to negate a filter.
# This expression will select only entries that <i>do not</i> have an <tt>objectclass</tt>
# attribute:
# f = ~ Net::LDAP::Filter.pres( "objectclass" )
#
#--
# This operator can't be !, evidently. Try it.
# Removed GT and LT. They're not in the RFC.
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_s.to_ber_contextspecific 7
elsif @right =~ /[\*]/ #substring
ary = @right.split( /[\*]+/ )
final_star = @right =~ /[\*]$/
initial_star = ary.first == "" and ary.shift
seq = []
unless initial_star
seq << ary.shift.to_ber_contextspecific(0)
end
n_any_strings = ary.length - (final_star ? 0 : 1)
#p n_any_strings
n_any_strings.times {
seq << ary.shift.to_ber_contextspecific(1)
}
unless final_star
seq << ary.shift.to_ber_contextspecific(2)
end
[@left.to_s.to_ber, seq.to_ber].to_ber_contextspecific 4
else #equality
[@left.to_s.to_ber, @right.to_ber].to_ber_contextspecific 3
end
when :ge
[@left.to_s.to_ber, @right.to_ber].to_ber_contextspecific 5
when :le
[@left.to_s.to_ber, @right.to_ber].to_ber_contextspecific 6
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
#--
# We get a Ruby object which comes from parsing an RFC-1777 "Filter"
# object. Convert it to a Net::LDAP::Filter.
# TODO, we're hardcoding the RFC-1777 BER-encodings of the various
# filter types. Could pull them out into a constant.
#
def Filter::parse_ldap_filter obj
case obj.ber_identifier
when 0x87 # present. context-specific primitive 7.
Filter.eq( obj.to_s, "*" )
when 0xa3 # equalityMatch. context-specific constructed 3.
Filter.eq( obj[0], obj[1] )
else
raise LdapError.new( "unknown ldap search-filter type: #{obj.ber_identifier}" )
end
end
#--
# We got a hash of attribute values.
# Do we match the attributes?
# Return T/F, and call match recursively as necessary.
def match entry
case @op
when :eq
if @right == "*"
l = entry[@left] and l.length > 0
else
l = entry[@left] and l = l.to_a and l.index(@right)
end
else
raise LdapError.new( "unknown filter type in match: #{@op}" )
end
end
# Converts an LDAP filter-string (in the prefix syntax specified in RFC-2254)
# to a Net::LDAP::Filter.
def self.construct ldap_filter_string
FilterParser.new(ldap_filter_string).filter
end
# Synonym for #construct.
# to a Net::LDAP::Filter.
def self.from_rfc2254 ldap_filter_string
construct ldap_filter_string
end
end # class Net::LDAP::Filter
class FilterParser #:nodoc:
attr_reader :filter
def initialize str
require 'strscan'
@filter = parse( StringScanner.new( str )) or raise Net::LDAP::LdapError.new( "invalid filter syntax" )
end
def parse scanner
parse_filter_branch(scanner) or parse_paren_expression(scanner)
end
def parse_paren_expression scanner
if scanner.scan(/\s*\(\s*/)
b = if scanner.scan(/\s*\&\s*/)
a = nil
branches = []
while br = parse_paren_expression(scanner)
branches << br
end
if branches.length >= 2
a = branches.shift
while branches.length > 0
a = a & branches.shift
end
a
end
elsif scanner.scan(/\s*\|\s*/)
# TODO: DRY!
a = nil
branches = []
while br = parse_paren_expression(scanner)
branches << br
end
if branches.length >= 2
a = branches.shift
while branches.length > 0
a = a | branches.shift
end
a
end
elsif scanner.scan(/\s*\!\s*/)
br = parse_paren_expression(scanner)
if br
~ br
end
else
parse_filter_branch( scanner )
end
if b and scanner.scan( /\s*\)\s*/ )
b
end
end
end
# Added a greatly-augmented filter contributed by Andre Nathan
# for detecting special characters in values. (15Aug06)
def parse_filter_branch scanner
scanner.scan(/\s*/)
if token = scanner.scan( /[\w\-_]+/ )
scanner.scan(/\s*/)
if op = scanner.scan( /\=|\<\=|\<|\>\=|\>|\!\=/ )
scanner.scan(/\s*/)
#if value = scanner.scan( /[\w\*\.]+/ ) (ORG)
if value = scanner.scan( /[\w\*\.\+\-@=#\$%&!]+/ )
case op
when "="
Filter.eq( token, value )
when "!="
Filter.ne( token, value )
when "<"
Filter.lt( token, value )
when "<="
Filter.le( token, value )
when ">"
Filter.gt( token, value )
when ">="
Filter.ge( token, value )
end
end
end
end
end
end # class Net::LDAP::FilterParser
end # class Net::LDAP
end # module Net

View File

@ -1,205 +0,0 @@
# $Id: pdu.rb 126 2006-05-31 15:55:16Z blackhedd $
#
# 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
DeleteResponse = 11
ModifyRDNResponse = 13
SearchResultReferral = 19
attr_reader :msg_id, :app_tag
attr_reader :search_dn, :search_attributes, :search_entry
attr_reader :search_referrals
#
# initialize
# An LDAP PDU always looks like a BerSequence with
# at least two elements: an integer (message-id number), and
# an application-specific sequence.
# Some LDAPv3 packets also include an optional
# third element, which is a sequence of "controls"
# (See RFC 2251, section 4.1.12).
# 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.
#
# Added a controls-processor to SearchResult.
# Didn't add it everywhere because it just _feels_
# like it will need to be refactored.
#
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 SearchResultReferral
parse_search_referral ber_object[1]
when SearchResult
parse_ldap_result ber_object[1]
parse_controls(ber_object[2]) if ber_object[2]
when ModifyResponse
parse_ldap_result ber_object[1]
when AddResponse
parse_ldap_result ber_object[1]
when DeleteResponse
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
# Return RFC-2251 Controls if any.
# Messy. Does this functionality belong somewhere else?
def result_controls
@ldap_controls || []
end
#
# 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
private :parse_ldap_result
#
# 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.
# ALTERNATE IMPLEMENTATION: In addition to @search_dn and @search_attributes,
# we also return @search_entry, which is an LDAP::Entry object.
# If that works out well, then we'll remove the first two.
#
# Provisionally removed obsolete search_attributes and search_dn, 04May06.
#
def parse_search_return sequence
sequence.length >= 2 or raise LdapPduError
@search_entry = LDAP::Entry.new( sequence[0] )
#@search_dn = sequence[0]
#@search_attributes = {}
sequence[1].each {|seq|
@search_entry[seq[0]] = seq[1]
#@search_attributes[seq[0].downcase.intern] = seq[1]
}
end
#
# A search referral is a sequence of one or more LDAP URIs.
# Any number of search-referral replies can be returned by the server, interspersed
# with normal replies in any order.
# Until I can think of a better way to do this, we'll return the referrals as an array.
# It'll be up to higher-level handlers to expose something reasonable to the client.
def parse_search_referral uris
@search_referrals = uris
end
# Per RFC 2251, an LDAP "control" is a sequence of tuples, each consisting
# of an OID, a boolean criticality flag defaulting FALSE, and an OPTIONAL
# Octet String. If only two fields are given, the second one may be
# either criticality or data, since criticality has a default value.
# Someday we may want to come back here and add support for some of
# more-widely used controls. RFC-2696 is a good example.
#
def parse_controls sequence
@ldap_controls = sequence.map do |control|
o = OpenStruct.new
o.oid,o.criticality,o.value = control[0],control[1],control[2]
if o.criticality and o.criticality.is_a?(String)
o.value = o.criticality
o.criticality = false
end
o
end
end
private :parse_controls
end
end # module Net

View File

@ -1,64 +0,0 @@
# $Id: psw.rb 73 2006-04-24 21:59:35Z 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 Password
class << self
# Generate a password-hash suitable for inclusion in an LDAP attribute.
# Pass a hash type (currently supported: :md5 and :sha) and a plaintext
# password. This function will return a hashed representation.
# STUB: This is here to fulfill the requirements of an RFC, which one?
# TODO, gotta do salted-sha and (maybe) salted-md5.
# Should we provide sha1 as a synonym for sha1? I vote no because then
# should you also provide ssha1 for symmetry?
def generate( type, str )
case type
when :md5
require 'md5'
"{MD5}#{ [MD5.new( str.to_s ).digest].pack("m").chomp }"
when :sha
require 'sha1'
"{SHA}#{ [SHA1.new( str.to_s ).digest].pack("m").chomp }"
# when ssha
else
raise Net::LDAP::LdapError.new( "unsupported password-hash type (#{type})" )
end
end
end
end
end # class LDAP
end # module Net

View File

@ -1,39 +0,0 @@
# $Id: ldif.rb 78 2006-04-26 02:57:34Z blackhedd $
#
# Net::LDIF 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
#
#
# THIS FILE IS A STUB.
module Net
class LDIF
end # class LDIF
end # module Net

View File

@ -1,42 +0,0 @@
# $Id: testber.rb 57 2006-04-18 00:18:48Z blackhedd $
#
#
$:.unshift "lib"
require 'net/ldap'
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

View File

@ -1,12 +0,0 @@
# $Id: testem.rb 121 2006-05-15 18:36:24Z blackhedd $
#
#
require 'test/unit'
require 'tests/testber'
require 'tests/testldif'
require 'tests/testldap'
require 'tests/testpsw'
require 'tests/testfilter'

View File

@ -1,37 +0,0 @@
# $Id: testfilter.rb 122 2006-05-15 20:03:56Z blackhedd $
#
#
require 'test/unit'
$:.unshift "lib"
require 'net/ldap'
class TestFilter < Test::Unit::TestCase
def setup
end
def teardown
end
def test_rfc_2254
p Net::LDAP::Filter.from_rfc2254( " ( uid=george* ) " )
p Net::LDAP::Filter.from_rfc2254( "uid!=george*" )
p Net::LDAP::Filter.from_rfc2254( "uid<george*" )
p Net::LDAP::Filter.from_rfc2254( "uid <= george*" )
p Net::LDAP::Filter.from_rfc2254( "uid>george*" )
p Net::LDAP::Filter.from_rfc2254( "uid>=george*" )
p Net::LDAP::Filter.from_rfc2254( "uid!=george*" )
p Net::LDAP::Filter.from_rfc2254( "(& (uid!=george* ) (mail=*))" )
p Net::LDAP::Filter.from_rfc2254( "(| (uid!=george* ) (mail=*))" )
p Net::LDAP::Filter.from_rfc2254( "(! (mail=*))" )
end
end

View File

@ -1,190 +0,0 @@
# $Id: testldap.rb 65 2006-04-23 01:17:49Z blackhedd $
#
#
$:.unshift "lib"
require 'test/unit'
require 'net/ldap'
require 'stringio'
class TestLdapClient < Test::Unit::TestCase
# TODO: these tests crash and burn if the associated
# LDAP testserver isn't up and running.
# We rely on being able to read a file with test data
# in LDIF format.
# TODO, WARNING: for the moment, this data is in a file
# whose name and location are HARDCODED into the
# instance method load_test_data.
def setup
@host = "127.0.0.1"
@port = 3890
@auth = {
:method => :simple,
:username => "cn=bigshot,dc=bayshorenetworks,dc=com",
:password => "opensesame"
}
@ldif = load_test_data
end
# Get some test data which will be used to validate
# the responses from the test LDAP server we will
# connect to.
# TODO, Bogus: we are HARDCODING the location of the file for now.
#
def load_test_data
ary = File.readlines( "tests/testdata.ldif" )
hash = {}
while line = ary.shift and line.chomp!
if line =~ /^dn:[\s]*/i
dn = $'
hash[dn] = {}
while attr = ary.shift and attr.chomp! and attr =~ /^([\w]+)[\s]*:[\s]*/
hash[dn][$1.downcase.intern] ||= []
hash[dn][$1.downcase.intern] << $'
end
end
end
hash
end
# Binding tests.
# Need tests for all kinds of network failures and incorrect auth.
# TODO: Implement a class-level timeout for operations like bind.
# Search has a timeout defined at the protocol level, other ops do not.
# TODO, use constants for the LDAP result codes, rather than hardcoding them.
def test_bind
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
assert_equal( true, ldap.bind )
assert_equal( 0, ldap.get_operation_result.code )
assert_equal( "Success", ldap.get_operation_result.message )
bad_username = @auth.merge( {:username => "cn=badguy,dc=imposters,dc=com"} )
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => bad_username
assert_equal( false, ldap.bind )
assert_equal( 48, ldap.get_operation_result.code )
assert_equal( "Inappropriate Authentication", ldap.get_operation_result.message )
bad_password = @auth.merge( {:password => "cornhusk"} )
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => bad_password
assert_equal( false, ldap.bind )
assert_equal( 49, ldap.get_operation_result.code )
assert_equal( "Invalid Credentials", ldap.get_operation_result.message )
end
def test_search
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
search = {:base => "dc=smalldomain,dc=com"}
assert_equal( false, ldap.search( search ))
assert_equal( 32, ldap.get_operation_result.code )
search = {:base => "dc=bayshorenetworks,dc=com"}
assert_equal( true, ldap.search( search ))
assert_equal( 0, ldap.get_operation_result.code )
ldap.search( search ) {|res|
assert_equal( res, @ldif )
}
end
# This is a helper routine for test_search_attributes.
def internal_test_search_attributes attrs_to_search
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
assert( ldap.bind )
search = {
:base => "dc=bayshorenetworks,dc=com",
:attributes => attrs_to_search
}
ldif = @ldif
ldif.each {|dn,entry|
entry.delete_if {|attr,value|
! attrs_to_search.include?(attr)
}
}
assert_equal( true, ldap.search( search ))
ldap.search( search ) {|res|
res_keys = res.keys.sort
ldif_keys = ldif.keys.sort
assert( res_keys, ldif_keys )
res.keys.each {|rk|
assert( res[rk], ldif[rk] )
}
}
end
def test_search_attributes
internal_test_search_attributes [:mail]
internal_test_search_attributes [:cn]
internal_test_search_attributes [:ou]
internal_test_search_attributes [:hasaccessprivilege]
internal_test_search_attributes ["mail"]
internal_test_search_attributes ["cn"]
internal_test_search_attributes ["ou"]
internal_test_search_attributes ["hasaccessrole"]
internal_test_search_attributes [:mail, :cn, :ou, :hasaccessrole]
internal_test_search_attributes [:mail, "cn", :ou, "hasaccessrole"]
end
def test_search_filters
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
search = {
:base => "dc=bayshorenetworks,dc=com",
:filter => Net::LDAP::Filter.eq( "sn", "Fosse" )
}
ldap.search( search ) {|res|
p res
}
end
def test_open
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
ldap.open {|ldap|
10.times {
rc = ldap.search( :base => "dc=bayshorenetworks,dc=com" )
assert_equal( true, rc )
}
}
end
def test_ldap_open
Net::LDAP.open( :host => @host, :port => @port, :auth => @auth ) {|ldap|
10.times {
rc = ldap.search( :base => "dc=bayshorenetworks,dc=com" )
assert_equal( true, rc )
}
}
end
end

View File

@ -1,69 +0,0 @@
# $Id: testldif.rb 61 2006-04-18 20:55:55Z blackhedd $
#
#
$:.unshift "lib"
require 'test/unit'
require 'net/ldap'
require 'net/ldif'
require 'sha1'
require 'base64'
class TestLdif < Test::Unit::TestCase
TestLdifFilename = "tests/testdata.ldif"
def test_empty_ldif
ds = Net::LDAP::Dataset::read_ldif( StringIO.new )
assert_equal( true, ds.empty? )
end
def test_ldif_with_comments
str = ["# Hello from LDIF-land", "# This is an unterminated comment"]
io = StringIO.new( str[0] + "\r\n" + str[1] )
ds = Net::LDAP::Dataset::read_ldif( io )
assert_equal( str, ds.comments )
end
def test_ldif_with_password
psw = "goldbricks"
hashed_psw = "{SHA}" + Base64::encode64( SHA1.new(psw).digest ).chomp
ldif_encoded = Base64::encode64( hashed_psw ).chomp
ds = Net::LDAP::Dataset::read_ldif( StringIO.new( "dn: Goldbrick\r\nuserPassword:: #{ldif_encoded}\r\n\r\n" ))
recovered_psw = ds["Goldbrick"][:userpassword].shift
assert_equal( hashed_psw, recovered_psw )
end
def test_ldif_with_continuation_lines
ds = Net::LDAP::Dataset::read_ldif( StringIO.new( "dn: abcdefg\r\n hijklmn\r\n\r\n" ))
assert_equal( true, ds.has_key?( "abcdefg hijklmn" ))
end
# TODO, INADEQUATE. We need some more tests
# to verify the content.
def test_ldif
File.open( TestLdifFilename, "r" ) {|f|
ds = Net::LDAP::Dataset::read_ldif( f )
assert_equal( 13, ds.length )
}
end
# TODO, need some tests.
# Must test folded lines and base64-encoded lines as well as normal ones.
def test_to_ldif
File.open( TestLdifFilename, "r" ) {|f|
ds = Net::LDAP::Dataset::read_ldif( f )
ds.to_ldif
assert_equal( true, false ) # REMOVE WHEN WE HAVE SOME TESTS HERE.
}
end
end

View File

@ -1,28 +0,0 @@
# $Id: testpsw.rb 72 2006-04-24 21:58:14Z blackhedd $
#
#
$:.unshift "lib"
require 'net/ldap'
require 'stringio'
class TestPassword < Test::Unit::TestCase
def setup
end
def test_psw
assert_equal( "{MD5}xq8jwrcfibi0sZdZYNkSng==", Net::LDAP::Password.generate( :md5, "cashflow" ))
assert_equal( "{SHA}YE4eGkN4BvwNN1f5R7CZz0kFn14=", Net::LDAP::Password.generate( :sha, "cashflow" ))
end
end