ad-ldap 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in ad-ldap.gemspec
4
+ gemspec
5
+
6
+ gem 'rake', '~>0.9.2'
@@ -0,0 +1,128 @@
1
+ # AD::LDAP
2
+
3
+ A small wrapper to Net::LDAP to provide some extended functionality and utility.
4
+
5
+ ## Description
6
+
7
+ AD::LDAP is a small wrapper to the Net::LDAP library. Net::LDAP provides a nice low-level interface for interacting with an LDAP server. AD::LDAP simply wraps that interface and provides some extended functionality through:
8
+
9
+ * Built-in logging of any communication with the LDAP server
10
+ * Easier searching
11
+
12
+ ## Installation
13
+
14
+ gem install ad-ldap
15
+
16
+ ## Usage
17
+
18
+ First, you need to configure the gem:
19
+
20
+ ```ruby
21
+ AD::LDAP.configure do |config|
22
+ config.host = "127.0.0.1"
23
+ config.port = 389
24
+ config.base = "DC=mydomain, DC=com"
25
+ config.encrytion = :simple_tls
26
+ config.logger = Rails.logger
27
+ end
28
+ ```
29
+
30
+ Then you can start running LDAP commands like you would with Net::LDAP.
31
+
32
+ ```ruby
33
+ AD::LDAP.search({
34
+ :base => "DC=Users, DC=mydomain, DC=com",
35
+ :filter => "(name=collin)"
36
+ })
37
+ ```
38
+
39
+ Most of the commands have the same syntax as they do in net-ldap:
40
+
41
+ ```ruby
42
+ AD::LDAP.add({
43
+ :dn => "DN=collin, DC=Users, DC=mydomain, DC=com",
44
+ :attributes => { :givenname => "Collin", :lastname => "Redding" }
45
+ })
46
+ ```
47
+
48
+ Some are slightly different though, the following:
49
+
50
+ ```ruby
51
+ AD::LDAP.delete("DN=collin, DC=Users, DC=mydomain, DC=com")
52
+ ```
53
+
54
+ is equivalent to:
55
+
56
+ ```ruby
57
+ ldap = Net::LDAP.new
58
+ ldap.delete({ :dn => "DN=collin, DC=Users, DC=mydomain, DC=com })`
59
+ ```
60
+
61
+ The biggest feature of AD::LDAP is some of the conventions when using the search method. If I don't provide a filter and have extra keys not supported by net-ldap's search, they are converted to filters automatically:
62
+
63
+ ```ruby
64
+ AD::LDAP.search({ :name__eq => "collin" })
65
+ ```
66
+
67
+ which can be simplified even further:
68
+
69
+ ```ruby
70
+ AD::LDAP.search({ :name => "collin" })
71
+ ```
72
+
73
+ Multiple filters are joined together (Net::LDAP::Filter.join) by default:
74
+
75
+ ```ruby
76
+ AD::LDAP.search({ :name => "collin", :objectclass => "user" })
77
+ ```
78
+
79
+ AD::LDAP won't get in your wa if you need to do something complex:
80
+
81
+ ```ruby
82
+ name_filter = Net::LDAP::Filter.eq("name", "collin*")
83
+ class_filter = Net::LDAP::Filter.eq("objectclass", "user")
84
+ filters = name_filter | class_filter
85
+ AD::LDAP.search({ :filter => filters, :size => 1 })
86
+ ```
87
+
88
+ Finally, because the LDAP names for most fields are not very ruby-ish (are all one word) it's sometimes convenient to setup mappings from a more ruby friendly name to a LDAP name:
89
+
90
+ ```ruby
91
+ AD::LDAP.configure do |config|
92
+ # ...
93
+ config.mapppings = {
94
+ "login" => "samaccountname"
95
+ }
96
+ end
97
+ ```
98
+
99
+ with the above config you can then search with the mapping:
100
+
101
+ ```ruby
102
+ AD::LDAP.search({ :login => "jcredding" })
103
+ ```
104
+
105
+ ## License
106
+
107
+ Copyright (c) 2011 Collin Redding and Team Insight
108
+
109
+ Permission is hereby granted, free of charge, to any person
110
+ obtaining a copy of this software and associated documentation
111
+ files (the "Software"), to deal in the Software without
112
+ restriction, including without limitation the rights to use,
113
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
114
+ copies of the Software, and to permit persons to whom the
115
+ Software is furnished to do so, subject to the following
116
+ conditions:
117
+
118
+ The above copyright notice and this permission notice shall be
119
+ included in all copies or substantial portions of the Software.
120
+
121
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
122
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
123
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
124
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
125
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
126
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
127
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
128
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ include Rake::DSL
2
+
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ require 'assert/rake_tasks'
7
+ Assert::RakeTasks.for :test
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "ad-ldap/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "ad-ldap"
7
+ s.version = AD::LDAP::VERSION
8
+ s.authors = ["Collin Redding"]
9
+ s.homepage = "http://github.com/teaminsight/ad-ldap"
10
+ s.summary = %q{A small wrapper to Net::LDAP to provide some extended functionality and utility.}
11
+ s.description = %q{A small wrapper to Net::LDAP to provide some extended functionality and utility.}
12
+
13
+ s.rubyforge_project = "ad-ldap"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_runtime_dependency "net-ldap", "~>0.2.2"
21
+
22
+ s.add_development_dependency "assert" #TODO: lock the version of assert
23
+ s.add_development_dependency "mocha", "=0.9.12"
24
+ end
@@ -0,0 +1,97 @@
1
+ require 'benchmark'
2
+ require 'ostruct'
3
+
4
+ require 'ad-ldap/adapter'
5
+ require 'ad-ldap/logger'
6
+ require 'ad-ldap/search_args'
7
+ require 'ad-ldap/version'
8
+
9
+ module AD
10
+ module LDAP
11
+ class << self
12
+
13
+ def configure
14
+ yield self.config
15
+ end
16
+
17
+ def config
18
+ @config ||= OpenStruct.new({
19
+ :run_commands => false,
20
+ :search_size_supported => true,
21
+ :mappings => {}
22
+ })
23
+ end
24
+
25
+ def adapter
26
+ @adapter ||= AD::LDAP::Adapter.new(self.config)
27
+ end
28
+
29
+ def logger
30
+ @logger ||= AD::LDAP::Logger.new(self.config)
31
+ end
32
+
33
+ def add(args = {})
34
+ self.run(:add, args)
35
+ end
36
+
37
+ def delete(dn)
38
+ self.run(:delete, { :dn => dn })
39
+ end
40
+
41
+ def replace_attribute(dn, field, value)
42
+ self.run(:replace_attribute, dn, field, value)
43
+ end
44
+
45
+ def delete_attribute(dn, field)
46
+ self.run(:delete_attribute, dn, field)
47
+ end
48
+
49
+ def search(args = {})
50
+ self.run_search(args)
51
+ end
52
+
53
+ def bind_as(args = {})
54
+ if !args[:filter]
55
+ password = args.delete(:password)
56
+ search_args = AD::LDAP::SearchArgs.new(args)
57
+ args = {
58
+ :filter => search_args[:filter],
59
+ :password => password
60
+ }
61
+ end
62
+ !!self.run(:bind_as, args)
63
+ end
64
+
65
+ protected
66
+
67
+ def run_search(args)
68
+ search_args = AD::LDAP::SearchArgs.new(args)
69
+ if !self.config.search_size_supported
70
+ size = search_args.delete(:size)
71
+ end
72
+ results = (self.run(:search, search_args) || [])
73
+ if !self.config.search_size_supported && size && results.kind_of?(Array)
74
+ results[0..(size.to_i - 1)]
75
+ else
76
+ results
77
+ end
78
+ end
79
+
80
+ # Inspired by https://github.com/tpett/perry logger
81
+ def run(method, *args)
82
+ result, time = [ nil, -1 ]
83
+ if self.config.run_commands
84
+ time = (Benchmark.measure do
85
+ result = self.adapter.send(method, *args)
86
+ end).real
87
+ end
88
+ self.logger.out(method, args, result, time)
89
+ result
90
+ rescue Exception => exception
91
+ self.logger.out(method, args, result, time)
92
+ raise(exception)
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,69 @@
1
+ require 'net/ldap'
2
+
3
+ require 'ad-ldap/response'
4
+
5
+ module AD
6
+ module LDAP
7
+
8
+ class Adapter < ::Net::LDAP
9
+ attr_accessor :config
10
+
11
+ def initialize(config)
12
+ self.config = config
13
+ super({
14
+ :host => self.config.host,
15
+ :port => self.config.port,
16
+ :base => self.config.base,
17
+ :encryption => self.config.encryption
18
+ })
19
+ if self.config.auth
20
+ self.auth(self.config.auth.username, self.config.auth.password)
21
+ end
22
+ end
23
+
24
+ # don't raise when an open connection is already open, just yield it
25
+ def open
26
+ if @open_connection
27
+ yield @open_connection
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ [ :add, :delete, :modify ].each do |method|
34
+ define_method(method) do |args|
35
+ result = super
36
+ self.check_operation
37
+ result
38
+ end
39
+ end
40
+ [ :add_attribute, :replace_attribute ].each do |method|
41
+ define_method(method) do |dn, attribute, value|
42
+ result = super
43
+ self.check_operation
44
+ result
45
+ end
46
+ end
47
+ def delete_attribute(dn, attribute)
48
+ result = super
49
+ self.check_operation
50
+ result
51
+ end
52
+
53
+ def search(args = {})
54
+ results = super(args.dup)
55
+ self.check_operation
56
+ results
57
+ end
58
+
59
+ protected
60
+
61
+ def check_operation
62
+ check = self.get_operation_result
63
+ AD::LDAP::Response.new(check.code, check.message).handle!
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,6 @@
1
+ module AD::LDAP
2
+
3
+ class Error < StandardError
4
+ end
5
+
6
+ end
@@ -0,0 +1,59 @@
1
+ module AD
2
+ module LDAP
3
+
4
+ # Inspired by https://github.com/tpett/perry logger
5
+ class Logger
6
+ attr_accessor :logger, :silent
7
+
8
+ def initialize(config)
9
+ self.logger = config.logger
10
+ self.silent = !!config.silent
11
+ end
12
+
13
+ def out(method, args, result, time)
14
+ color = "4;32;1"
15
+ name = "%s (%.1fms)" % [ "LDAP", time ]
16
+ message = self.generate_message(method, args)
17
+ output = " \e[#{color}]#{name} #{message}\e[0m"
18
+ if self.logger
19
+ self.logger.debug(output)
20
+ elsif !self.silent
21
+ puts output
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def generate_message(method, args)
28
+ case(method.to_sym)
29
+ when :replace_attribute
30
+ dn, field, value = args
31
+ "#{method}(#{dn.inspect}, #{field.inspect}, #{self.filter_value(value, field).inspect})"
32
+ when :delete_attribute
33
+ dn, field = args
34
+ "#{method}(#{dn.inspect}, #{field.inspect})"
35
+ else
36
+ "#{method}(#{self.filter_args(args.first)})"
37
+ end
38
+ end
39
+
40
+ FILTERED = [ /password/, /unicodePwd/ ]
41
+ def filter_args(args = {})
42
+ (args.inject({}) do |filtered, (key, value)|
43
+ filtered[key] = self.filter_value(value, key)
44
+ filtered
45
+ end).inspect
46
+ end
47
+ def filter_value(value, key)
48
+ case(key.to_s)
49
+ when *FILTERED
50
+ "[FILTERED]"
51
+ else
52
+ value
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,28 @@
1
+ require 'ad-ldap/exceptions'
2
+
3
+ module AD
4
+ module LDAP
5
+
6
+ # descriptions: http://wiki.service-now.com/index.php?title=LDAP_Error_Codes
7
+ class Response
8
+ attr_accessor :code, :message
9
+
10
+ CODES = {
11
+ :success => 0
12
+ }.freeze
13
+
14
+ def initialize(code, message)
15
+ self.code = code.to_i
16
+ self.message = message
17
+ end
18
+
19
+ def handle!
20
+ if self.code != CODES[:success]
21
+ raise(AD::LDAP::Error, "#{self.code}: #{self.message}")
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ require 'net/ldap'
2
+
3
+ module AD
4
+ module LDAP
5
+
6
+ class SearchArgs < Hash
7
+
8
+ LDAP_KEYS = [
9
+ :base, :filter, :attributes, :return_result, :attributes_only,
10
+ :scope, :size
11
+ ]
12
+
13
+ def initialize(args)
14
+ super()
15
+ conditions = {}
16
+ args.each do |key, value|
17
+ if LDAP_KEYS.include?(key.to_sym)
18
+ self[key.to_sym] = value
19
+ else
20
+ conditions[key.to_sym] = value
21
+ end
22
+ end
23
+ if !self[:filter] && (filters = self.build_filters(conditions))
24
+ self[:filter] = filters ? filters.to_s : nil
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def build_filters(conditions = {})
31
+ conditions.inject(nil) do |filters, (key, value)|
32
+ field, operator = key.to_s.split("__")
33
+ operator ||= "eq"
34
+ if mapping = AD::LDAP.config.mappings[field.to_sym]
35
+ field = (mapping || field)
36
+ end
37
+ filter = ::Net::LDAP::Filter.send(operator, field, value)
38
+ filters ? ::Net::LDAP::Filter.join(filters, filter) : filter
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ module AD
2
+ module LDAP
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ require 'mocha'
2
+
3
+ # add the current gem root path to the LOAD_PATH
4
+ root_path = File.expand_path("../..", __FILE__)
5
+ if !$LOAD_PATH.include?(root_path)
6
+ $LOAD_PATH.unshift(root_path)
7
+ end
8
+
9
+ require 'ad-ldap'
10
+
11
+ class Assert::Context
12
+ include Mocha::API
13
+ end
14
+
15
+ AD::LDAP.configure do |config|
16
+ config.silent = true
17
+ end
@@ -0,0 +1,20 @@
1
+ require 'assert'
2
+
3
+ class AD::LDAP::Adapter
4
+
5
+ class BaseTest < Assert::Context
6
+ desc "the AD::LDAP::Adapter class"
7
+ setup do
8
+ @adapter = AD::LDAP::Adapter.new(AD::LDAP.config)
9
+ end
10
+ subject{ @adapter }
11
+
12
+ should have_instance_methods :open, :add, :delete, :modify, :search
13
+ should have_instance_methods :add_attribute, :replace_attribute, :delete_attribute
14
+
15
+ should "be a kind of Net::LDAP" do
16
+ assert_kind_of Net::LDAP, subject
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,107 @@
1
+ require 'assert'
2
+
3
+ class AD::LDAP::Logger
4
+
5
+ class BaseTest < Assert::Context
6
+ desc "the AD::LDAP::Logger class"
7
+ setup do
8
+ @config = OpenStruct.new(:silent => true)
9
+ @logger = AD::LDAP::Logger.new(@config)
10
+ end
11
+ subject{ @logger }
12
+
13
+ should have_accessors :logger, :silent
14
+ should have_instance_methods :out
15
+
16
+ end
17
+
18
+ class OutTest < BaseTest
19
+ desc "out method"
20
+ setup do
21
+ @logger.expects(:generate_message)
22
+ end
23
+
24
+ should "call generate message to build the message to be logged" do
25
+ assert_nothing_raised do
26
+ @logger.out(:add, {}, [], "2")
27
+ end
28
+ end
29
+ end
30
+
31
+ class GenerateMessageTest < BaseTest
32
+ desc "generate message method"
33
+ setup do
34
+ @arg_sets = [
35
+ [ :add, [ { :dn => "something", :attributes => { :name => "something" } } ] ],
36
+ [ :replace_attribute, [ "something", :name, "something_else" ] ],
37
+ [ :delete_attribute, [ "something", :name ] ]
38
+ ]
39
+ @expected_messages = []
40
+ add_args = @arg_sets[0]
41
+ @expected_messages.push("#{add_args[0]}(#{add_args[1].first.inspect})")
42
+ rep_args = @arg_sets[1]
43
+ params_str = rep_args[1].collect(&:inspect).join(", ")
44
+ @expected_messages.push("#{rep_args[0]}(#{params_str})")
45
+ del_args = @arg_sets[2]
46
+ params_str = del_args[1].collect(&:inspect).join(", ")
47
+ @expected_messages.push("#{del_args[0]}(#{params_str})")
48
+ end
49
+
50
+ should "build the correct message strings based on different args" do
51
+ @arg_sets.each_with_index do |args, n|
52
+ assert_equal @expected_messages[n], subject.send(:generate_message, *args)
53
+ end
54
+ end
55
+ end
56
+
57
+ class WithPasswordArgTest < GenerateMessageTest
58
+ desc "with a filtered arg"
59
+ setup do
60
+ @arg_sets = [
61
+ [ :add, [ { :dn => "something", :attributes => { :password => "something" } } ] ],
62
+ [ :replace_attribute, [ "something", :unicodePwd, "something_else" ] ]
63
+ ]
64
+ @expected_messages = []
65
+ add_args = @arg_sets[0].dup
66
+ add_args[1].first[:attributes][:password] = "[FILTERED]"
67
+ @expected_messages.push("#{add_args[0]}(#{add_args[1].first.inspect})")
68
+ rep_args = @arg_sets[1].dup
69
+ rep_args[1][2] = "[FILTERED]"
70
+ params_str = rep_args[1].collect(&:inspect).join(", ")
71
+ @expected_messages.push("#{rep_args[0]}(#{params_str})")
72
+ end
73
+
74
+ should "build the correct message strings with filtered args" do
75
+ @arg_sets.each_with_index do |args, n|
76
+ assert_equal @expected_messages[n], subject.send(:generate_message, *args)
77
+ end
78
+ end
79
+ end
80
+
81
+ class WithLoggerTest < Assert::Context
82
+ desc "an AD::LDAP::Logger with a logger"
83
+ setup do
84
+ @base_logger = OpenStruct.new
85
+ @config = OpenStruct.new(:silent => true, :logger => @original_logger)
86
+ @logger = AD::LDAP::Logger.new(@config)
87
+ end
88
+ subject{ @logger }
89
+
90
+ class OutTest < WithLoggerTest
91
+ desc "out method"
92
+ setup do
93
+ @logger.expects(:generate_message)
94
+ @base_logger.expects(:debug)
95
+ end
96
+ subject{ @logger }
97
+
98
+ should "call generate message to build the message to be logged" do
99
+ assert_nothing_raised do
100
+ @logger.out(:add, {}, [], "2")
101
+ end
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ end
@@ -0,0 +1,44 @@
1
+ require 'assert'
2
+
3
+ class AD::LDAP::Response
4
+
5
+ class BaseTest < Assert::Context
6
+ desc "the AD::LDAP::Response class"
7
+ setup do
8
+ @response = AD::LDAP::Response.new(AD::LDAP::Response::CODES[:success], "success")
9
+ end
10
+ subject{ @response }
11
+
12
+ should have_accessors :code, :message
13
+ should have_instance_methods :handle!
14
+
15
+ end
16
+
17
+ class SuccessHandleTest < Assert::Context
18
+ desc "a successful ldap response handle! method"
19
+ setup do
20
+ @response = AD::LDAP::Response.new(AD::LDAP::Response::CODES[:success], "success")
21
+ end
22
+ subject{ @response }
23
+
24
+ should "not raise an error" do
25
+ assert_nothing_raised do
26
+ subject.handle!
27
+ end
28
+ end
29
+ end
30
+
31
+ class FailureHandleTest < Assert::Context
32
+ desc "a failed ldap response handle! method"
33
+ setup do
34
+ @response = AD::LDAP::Response.new(1, "failed")
35
+ end
36
+ subject{ @response }
37
+
38
+ should "not raise an error" do
39
+ assert_raises(AD::LDAP::Error) do
40
+ subject.handle!
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,144 @@
1
+ require 'assert'
2
+
3
+ class AD::LDAP::SearchArgs
4
+
5
+ class BaseTest < Assert::Context
6
+ desc "the AD::LDAP::SearchArgs class"
7
+ setup do
8
+ @search_args = AD::LDAP::SearchArgs.new({})
9
+ end
10
+ subject{ @search_args }
11
+
12
+ should "be a kind of Hash" do
13
+ assert_kind_of Hash, subject
14
+ end
15
+ end
16
+
17
+ class WithOnlyLDAPKeysTest < Assert::Context
18
+ desc "the AD::LDAP::SearchArgs initialized with only net-ldap keys"
19
+ setup do
20
+ @original = {
21
+ :base => "DN=something, DC=somewhere, DC=com",
22
+ :filter => "(name=something)"
23
+ }
24
+ @search_args = AD::LDAP::SearchArgs.new(@original)
25
+ end
26
+ subject{ @search_args }
27
+
28
+ should "not alter the hash passed to it" do
29
+ @original.each do |key, value|
30
+ assert_equal value, @search_args[key]
31
+ end
32
+ end
33
+ end
34
+
35
+ class WithNonLDAPKeysTest < Assert::Context
36
+ desc "the AD::LDAP::SearchArgs initialized with non net-ldap keys"
37
+ setup do
38
+ @original = {
39
+ :base => "DN=something, DC=somewhere, DC=com",
40
+ :name => "something"
41
+ }
42
+ @expected_filter = Net::LDAP::Filter.eq(:name, @original[:name])
43
+ @search_args = AD::LDAP::SearchArgs.new(@original)
44
+ end
45
+ subject{ @search_args }
46
+
47
+ should "set the filter based off the non net-ldap keys" do
48
+ assert_equal @original[:base], subject[:base]
49
+ assert_equal @expected_filter.to_s, subject[:filter].to_s
50
+ end
51
+ end
52
+
53
+ class WithMappingsTest < Assert::Context
54
+ desc "search args with a mapping filter"
55
+ setup do
56
+ AD::LDAP.config.mappings = { :dn => "distinguishedname" }
57
+ @original = { :dn => "something" }
58
+ @expected_filter = Net::LDAP::Filter.eq(AD::LDAP.config.mappings[:dn], @original[:dn])
59
+ @search_args = AD::LDAP::SearchArgs.new(@original)
60
+ end
61
+ subject{ @search_args }
62
+
63
+ should "use the mapping for the field" do
64
+ assert_equal @expected_filter.to_s, subject[:filter].to_s
65
+ end
66
+
67
+ teardown do
68
+ AD::LDAP.config.mappings = {}
69
+ end
70
+ end
71
+
72
+ class WithMultipleFiltersTest < Assert::Context
73
+ desc "search args with a multiple filters"
74
+ setup do
75
+ @original = { :dn => "something", :objectclass => "top" }
76
+ first_filter = Net::LDAP::Filter.eq(:dn, @original[:dn])
77
+ second_filter = Net::LDAP::Filter.eq(:objectclass, @original[:objectclass])
78
+ @expected_filter = Net::LDAP::Filter.join(second_filter, first_filter)
79
+ @search_args = AD::LDAP::SearchArgs.new(@original)
80
+ end
81
+ subject{ @search_args }
82
+
83
+ should "join the filters together" do
84
+ assert_equal @expected_filter.to_s, subject[:filter].to_s
85
+ end
86
+ end
87
+
88
+ class WithEqualFilterTest < Assert::Context
89
+ desc "search args with a equal filter"
90
+ setup do
91
+ @original = { :name__eq => "something" }
92
+ @expected_filter = Net::LDAP::Filter.eq(:name, @original[:name__eq])
93
+ @search_args = AD::LDAP::SearchArgs.new(@original)
94
+ end
95
+ subject{ @search_args }
96
+
97
+ should "create an equal filter for the field" do
98
+ assert_equal @expected_filter.to_s, subject[:filter].to_s
99
+ end
100
+ end
101
+
102
+ class WithNotEqualFilterTest < Assert::Context
103
+ desc "search args with a not equal filter"
104
+ setup do
105
+ @original = { :name__ne => "something" }
106
+ @expected_filter = Net::LDAP::Filter.ne(:name, @original[:name__ne])
107
+ @search_args = AD::LDAP::SearchArgs.new(@original)
108
+ end
109
+ subject{ @search_args }
110
+
111
+ should "create a not equal filter for the field" do
112
+ assert_equal @expected_filter.to_s, subject[:filter].to_s
113
+ end
114
+ end
115
+
116
+ class WithGreaterThanOrEqualFilterTest < Assert::Context
117
+ desc "search args with a greater than or equal filter"
118
+ setup do
119
+ @original = { :number__ge => 1 }
120
+ @expected_filter = Net::LDAP::Filter.ge(:number, @original[:number__ge])
121
+ @search_args = AD::LDAP::SearchArgs.new(@original)
122
+ end
123
+ subject{ @search_args }
124
+
125
+ should "create a greater than or equal filter for the field" do
126
+ assert_equal @expected_filter.to_s, subject[:filter].to_s
127
+ end
128
+ end
129
+
130
+ class WithLessThanOrEqualFilterTest < Assert::Context
131
+ desc "search args with a less than or equal filter"
132
+ setup do
133
+ @original = { :number__le => 1 }
134
+ @expected_filter = Net::LDAP::Filter.le(:number, @original[:number__le])
135
+ @search_args = AD::LDAP::SearchArgs.new(@original)
136
+ end
137
+ subject{ @search_args }
138
+
139
+ should "create a less than or equal filter for the field" do
140
+ assert_equal @expected_filter.to_s, subject[:filter].to_s
141
+ end
142
+ end
143
+
144
+ end
@@ -0,0 +1,139 @@
1
+ require 'assert'
2
+
3
+ module AD::LDAP
4
+
5
+ class BaseTest < Assert::Context
6
+ desc "the module AD::LDAP"
7
+ setup do
8
+ @module = AD::LDAP.dup
9
+ end
10
+ subject{ @module }
11
+
12
+ [ :configure, :config, :adapter, :logger, :add, :delete, :replace_attribute, :delete_attribute,
13
+ :search, :bind_as
14
+ ].each do |method|
15
+ should "respond to ##{method}" do
16
+ assert_respond_to subject, method
17
+ end
18
+ end
19
+
20
+ should "return an instance of OpenStruct with #config" do
21
+ assert_instance_of OpenStruct, subject.config
22
+ end
23
+ should "return an instance of AD::LDAP::Adapter with #adapter" do
24
+ assert_instance_of AD::LDAP::Adapter, subject.adapter
25
+ end
26
+ should "return an instance of AD::LDAP::Logger with #logger" do
27
+ assert_instance_of AD::LDAP::Logger, subject.logger
28
+ end
29
+
30
+ should "call it's run method when the method #add is called" do
31
+ args = [ { :dn => "something", :attributes => { :name => "something" } } ]
32
+ @module.expects(:run).with(:add, *args)
33
+ assert_nothing_raised{ @module.add(*args) }
34
+ end
35
+ should "call it's run method when the method #delete is called" do
36
+ args = [ "something" ]
37
+ @module.expects(:run).with(:delete, { :dn => args.first })
38
+ assert_nothing_raised{ @module.delete(*args) }
39
+ end
40
+ should "call it's run method when the method #replace_attribute is called" do
41
+ args = [ "something", :name, "silly" ]
42
+ @module.expects(:run).with(:replace_attribute, *args)
43
+ assert_nothing_raised{ @module.replace_attribute(*args) }
44
+ end
45
+ should "call it's run method when the method #delete_attribute is called" do
46
+ args = [ "something", :name ]
47
+ @module.expects(:run).with(:delete_attribute, *args)
48
+ assert_nothing_raised{ @module.delete_attribute(*args) }
49
+ end
50
+
51
+ should "call it's run_search method when the method #search is called" do
52
+ args = [ { :name => "something" } ]
53
+ @module.expects(:run_search).with(*args)
54
+ assert_nothing_raised{ @module.search(*args) }
55
+ end
56
+
57
+ end
58
+
59
+ class BindAsTest < AD::LDAP::BaseTest
60
+ desc "bind_as method"
61
+
62
+ should "call the run method with net-ldap args" do
63
+ args = [ { :filter => "(name=something)", :password => "poop" } ]
64
+ @module.expects(:run).with(:bind_as, *args)
65
+ assert_nothing_raised{ @module.bind_as(*args) }
66
+ end
67
+ should "call the run method with net-ldap args given no filter" do
68
+ args = { :name => "something", :password => "poop" }
69
+ search_args = args.dup.reject{|(k, v)| k == :password }
70
+ @module.expects(:run).with(:bind_as, {
71
+ :filter => AD::LDAP::SearchArgs.new(search_args)[:filter],
72
+ :password => args[:password]
73
+ })
74
+ assert_nothing_raised{ @module.bind_as(args) }
75
+ end
76
+
77
+ end
78
+
79
+ class RunTest < AD::LDAP::BaseTest
80
+ desc "run method"
81
+ setup do
82
+ mock_config = mock()
83
+ mock_config.expects(:run_commands)
84
+ @module.expects(:config).returns(mock_config)
85
+ Benchmark.expects(:measure)
86
+ mock_adapter = mock()
87
+ mock_adapter.expects(:add)
88
+ @module.expects(:adapter).returns(mock_adapter)
89
+ mock_logger = mock()
90
+ mock_logger.expects(:out)
91
+ @module.expects(:logger).returns(mock_logger)
92
+ end
93
+ subject{ @module }
94
+
95
+ should "call the method passed to it on the adapter and benchmark it" do
96
+ assert_nothing_raised do
97
+ subject.send(:run, :add, { :dn => "something" })
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ class RunSearchTest < AD::LDAP::BaseTest
104
+ desc "run_search method"
105
+ setup do
106
+ @args = { :size => 2, :name => "something" }
107
+ mock_config = mock()
108
+ mock_config.stubs(:search_size_supported).returns(false)
109
+ @module.stubs(:config).returns(mock_config)
110
+ @module.expects(:run).returns([ "a", "b", "c" ])
111
+ end
112
+ subject{ @module }
113
+
114
+ should "call run with :search and pass it the search args" do
115
+ results = nil
116
+ assert_nothing_raised do
117
+ results = subject.send(:run_search, @args)
118
+ end
119
+ assert_instance_of Array, results
120
+ assert_equal @args[:size], results.size
121
+ end
122
+ end
123
+
124
+ class ConfigureTest < AD::LDAP::BaseTest
125
+ desc "configure method"
126
+ setup do
127
+ AD::LDAP.configure do |config|
128
+ @config = config
129
+ end
130
+ end
131
+ subject{ @config }
132
+
133
+ should "yield it's config" do
134
+ assert_kind_of OpenStruct, subject
135
+ assert_equal AD::LDAP.config, subject
136
+ end
137
+ end
138
+
139
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ad-ldap
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Collin Redding
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-17 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: net-ldap
23
+ prerelease: false
24
+ version_requirements: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 19
30
+ segments:
31
+ - 0
32
+ - 2
33
+ - 2
34
+ version: 0.2.2
35
+ type: :runtime
36
+ requirement: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: assert
39
+ prerelease: false
40
+ version_requirements: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ requirement: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: mocha
53
+ prerelease: false
54
+ version_requirements: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - "="
58
+ - !ruby/object:Gem::Version
59
+ hash: 35
60
+ segments:
61
+ - 0
62
+ - 9
63
+ - 12
64
+ version: 0.9.12
65
+ type: :development
66
+ requirement: *id003
67
+ description: A small wrapper to Net::LDAP to provide some extended functionality and utility.
68
+ email:
69
+ executables: []
70
+
71
+ extensions: []
72
+
73
+ extra_rdoc_files: []
74
+
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - README.markdown
79
+ - Rakefile
80
+ - ad-ldap.gemspec
81
+ - lib/ad-ldap.rb
82
+ - lib/ad-ldap/adapter.rb
83
+ - lib/ad-ldap/exceptions.rb
84
+ - lib/ad-ldap/logger.rb
85
+ - lib/ad-ldap/response.rb
86
+ - lib/ad-ldap/search_args.rb
87
+ - lib/ad-ldap/version.rb
88
+ - test/helper.rb
89
+ - test/unit/ad-ldap/adapter_test.rb
90
+ - test/unit/ad-ldap/logger_test.rb
91
+ - test/unit/ad-ldap/response_test.rb
92
+ - test/unit/ad-ldap/search_args_test.rb
93
+ - test/unit/ad-ldap_test.rb
94
+ has_rdoc: true
95
+ homepage: http://github.com/teaminsight/ad-ldap
96
+ licenses: []
97
+
98
+ post_install_message:
99
+ rdoc_options: []
100
+
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ hash: 3
109
+ segments:
110
+ - 0
111
+ version: "0"
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ hash: 3
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project: ad-ldap
124
+ rubygems_version: 1.6.2
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: A small wrapper to Net::LDAP to provide some extended functionality and utility.
128
+ test_files:
129
+ - test/helper.rb
130
+ - test/unit/ad-ldap/adapter_test.rb
131
+ - test/unit/ad-ldap/logger_test.rb
132
+ - test/unit/ad-ldap/response_test.rb
133
+ - test/unit/ad-ldap/search_args_test.rb
134
+ - test/unit/ad-ldap_test.rb