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.
- data/.gitignore +4 -0
- data/Gemfile +6 -0
- data/README.markdown +128 -0
- data/Rakefile +7 -0
- data/ad-ldap.gemspec +24 -0
- data/lib/ad-ldap.rb +97 -0
- data/lib/ad-ldap/adapter.rb +69 -0
- data/lib/ad-ldap/exceptions.rb +6 -0
- data/lib/ad-ldap/logger.rb +59 -0
- data/lib/ad-ldap/response.rb +28 -0
- data/lib/ad-ldap/search_args.rb +45 -0
- data/lib/ad-ldap/version.rb +5 -0
- data/test/helper.rb +17 -0
- data/test/unit/ad-ldap/adapter_test.rb +20 -0
- data/test/unit/ad-ldap/logger_test.rb +107 -0
- data/test/unit/ad-ldap/response_test.rb +44 -0
- data/test/unit/ad-ldap/search_args_test.rb +144 -0
- data/test/unit/ad-ldap_test.rb +139 -0
- metadata +134 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -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.
|
data/Rakefile
ADDED
data/ad-ldap.gemspec
ADDED
@@ -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
|
data/lib/ad-ldap.rb
ADDED
@@ -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,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
|
data/test/helper.rb
ADDED
@@ -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
|