ad-ldap 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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