el_dap 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/README.rdoc +19 -1
- data/TODO.rdoc +5 -0
- data/lib/el_dap.rb +4 -0
- data/lib/el_dap/base.rb +15 -47
- data/lib/el_dap/exceptions.rb +3 -0
- data/lib/el_dap/validator.rb +11 -0
- data/lib/el_dap/version.rb +1 -1
- data/lib/el_dap/worker.rb +38 -0
- data/spec/el_dap/base_spec.rb +64 -91
- data/spec/el_dap/validator_spec.rb +31 -0
- data/spec/el_dap/worker_spec.rb +44 -0
- metadata +10 -4
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
0.0.3
|
2
|
+
=====
|
3
|
+
|
4
|
+
* When using multiple servers, return the first valid result. Only use additional servers on a timeout exception.
|
5
|
+
* An InvalidCredentialsError exception will be thrown on a search if the user credentials are invalid i.e. cannot bind
|
6
|
+
|
1
7
|
0.0.2
|
2
8
|
=====
|
3
9
|
|
data/README.rdoc
CHANGED
@@ -24,7 +24,25 @@ Just add this to your gem file:
|
|
24
24
|
|
25
25
|
== Usage
|
26
26
|
|
27
|
-
|
27
|
+
=== Confguration
|
28
|
+
|
29
|
+
To perform any ElDap operation you will need to provide at least two configuration values:
|
30
|
+
|
31
|
+
* *server_ips*: an array of server IP addresses
|
32
|
+
* *treebase*: a comma-seperated list of domain components
|
33
|
+
|
34
|
+
For search operations you will also need to provide the following configuration settings:
|
35
|
+
|
36
|
+
* *username*: the username of the account that will perform the search (typically this would be a service account)
|
37
|
+
* *password*: the password of the account that will perform the search
|
38
|
+
|
39
|
+
You also have the option of providing a timeout value:
|
40
|
+
|
41
|
+
* *timeout*: timeout in seconds for ElDap operations (default is 15 seconds)
|
42
|
+
|
43
|
+
=== Examples
|
44
|
+
|
45
|
+
Assuming we have an Active Directory called <b>ad.domain.com</b> which can also be referred to simply as *ad*,
|
28
46
|
the following examples are valid:
|
29
47
|
|
30
48
|
=== Authenticate a user
|
data/TODO.rdoc
ADDED
data/lib/el_dap.rb
CHANGED
data/lib/el_dap/base.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
module ElDap
|
2
2
|
class Base
|
3
|
-
include Constants
|
4
|
-
|
5
3
|
attr_accessor :server_ips, :username, :password, :timeout, :treebase
|
6
4
|
|
7
5
|
def initialize
|
@@ -10,69 +8,39 @@ module ElDap
|
|
10
8
|
yield(self) if block_given?
|
11
9
|
end
|
12
10
|
|
13
|
-
def validate(
|
14
|
-
return false if
|
15
|
-
|
11
|
+
def validate(username, password)
|
12
|
+
return false if Validator.blank?(username, password)
|
13
|
+
|
14
|
+
@server_ips.each do |ip_address|
|
16
15
|
begin
|
17
16
|
Timeout::timeout(self.timeout) do
|
17
|
+
worker = Worker.new(:username => username, :password => password, :ip_address => ip_address)
|
18
18
|
return worker.bind
|
19
19
|
end
|
20
20
|
rescue Timeout::Error
|
21
21
|
next
|
22
22
|
end
|
23
23
|
end
|
24
|
-
false
|
25
24
|
end
|
26
25
|
|
27
26
|
def search(search_string)
|
28
|
-
return nil if
|
29
|
-
|
30
|
-
|
27
|
+
return nil if Validator.blank?(search_string)
|
28
|
+
|
29
|
+
search_result = []
|
30
|
+
|
31
|
+
@server_ips.each do |ip_address|
|
31
32
|
begin
|
32
33
|
Timeout::timeout(self.timeout) do
|
33
|
-
|
34
|
+
worker = Worker.new(:username => self.username, :password => self.password, :ip_address => ip_address)
|
35
|
+
|
36
|
+
raise(InvalidCredentialsError, 'The user credentials provided are invalid') unless worker.bind
|
37
|
+
|
38
|
+
return worker.search_directory(search_string, self.treebase)
|
34
39
|
end
|
35
40
|
rescue Timeout::Error
|
36
41
|
next
|
37
42
|
end
|
38
43
|
end
|
39
|
-
create_result_collection search_result
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
def workers(uname = self.username, pword = self.password)
|
44
|
-
self.server_ips.map do |ip|
|
45
|
-
create_worker uname, pword, ip
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def create_worker(uname, pword, server_ip)
|
50
|
-
obj = ::Net::LDAP.new
|
51
|
-
obj.host = server_ip
|
52
|
-
obj.auth uname, pword
|
53
|
-
obj
|
54
|
-
end
|
55
|
-
|
56
|
-
def search_active_directory(worker, search_string)
|
57
|
-
filters = LDAP_FILTERS & ::Net::LDAP::Filter.eq(LDAP_SEARCH_FIELD, "*#{search_string}*")
|
58
|
-
result = worker.search(:base => self.treebase,
|
59
|
-
:filter => filters,
|
60
|
-
:attributes => LDAP_ATTRS,
|
61
|
-
:return_result => true)
|
62
|
-
# search will return false if unable to bind
|
63
|
-
# e.g. service account credentials have expired
|
64
|
-
result || []
|
65
|
-
end
|
66
|
-
|
67
|
-
def create_result_collection(collection = [])
|
68
|
-
collection ||= []
|
69
|
-
collection.map { |entry| create_struct entry }
|
70
|
-
end
|
71
|
-
|
72
|
-
def create_struct(hash = {})
|
73
|
-
key_values = {}
|
74
|
-
hash.each{|k, v| key_values[k] = v[0]}
|
75
|
-
OpenStruct.new(key_values)
|
76
44
|
end
|
77
45
|
|
78
46
|
end
|
data/lib/el_dap/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
module ElDap
|
2
|
+
class Worker < ::Net::LDAP
|
3
|
+
include Constants
|
4
|
+
|
5
|
+
def initialize(args = {})
|
6
|
+
super args
|
7
|
+
self.host = args[:ip_address]
|
8
|
+
self.auth args[:username], args[:password]
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def search_directory(search_string, treebase)
|
13
|
+
filters = LDAP_FILTERS & ::Net::LDAP::Filter.eq(LDAP_SEARCH_FIELD, "*#{search_string}*")
|
14
|
+
search_result = search(:base => treebase,
|
15
|
+
:filter => filters,
|
16
|
+
:attributes => LDAP_ATTRS,
|
17
|
+
:return_result => true)
|
18
|
+
# search will return false if unable to bind
|
19
|
+
# e.g. service account credentials have expired
|
20
|
+
return [] unless search_result
|
21
|
+
create_result_collection search_result
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def create_result_collection(collection = [])
|
27
|
+
collection ||= []
|
28
|
+
collection.map { |entry| create_struct entry }
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_struct(hash = {})
|
32
|
+
key_values = {}
|
33
|
+
hash.each{|k, v| key_values[k] = v[0]}
|
34
|
+
OpenStruct.new(key_values)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/spec/el_dap/base_spec.rb
CHANGED
@@ -2,110 +2,83 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module ElDap
|
4
4
|
describe Base do
|
5
|
-
|
6
5
|
before(:each) do
|
7
|
-
@instance = Base.new
|
8
|
-
|
6
|
+
@instance = Base.new do |i|
|
7
|
+
i.username = 'name'
|
8
|
+
i.password = 'password'
|
9
|
+
i.server_ips = ['1.1.1.1', '2.2.2.2']
|
10
|
+
i.timeout = 10
|
11
|
+
i.treebase = 'tree'
|
12
|
+
end
|
13
|
+
Validator.stub!(:blank?)
|
14
|
+
Worker.stub!(:new)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should initialize with a block" do
|
18
|
+
@instance.username.should == 'name'
|
19
|
+
@instance.password.should == 'password'
|
20
|
+
@instance.server_ips.should == ['1.1.1.1', '2.2.2.2']
|
21
|
+
@instance.timeout.should == 10
|
22
|
+
@instance.treebase.should == 'tree'
|
9
23
|
end
|
10
24
|
|
11
|
-
describe
|
12
|
-
|
13
|
-
@
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
25
|
+
describe '#Validate' do
|
26
|
+
before(:each) do
|
27
|
+
@worker = mock(Worker, :bind => true)
|
28
|
+
Worker.stub!(:new).and_return(@worker)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should check that username and password are not blank" do
|
32
|
+
Validator.should_receive(:blank?).and_return(true)
|
18
33
|
@instance.validate(nil, nil).should be_false
|
19
34
|
end
|
20
|
-
|
21
|
-
it "should
|
22
|
-
|
23
|
-
@instance.
|
24
|
-
@instance.search(nil).should be_nil
|
35
|
+
|
36
|
+
it "should create a new worker for the first ip address" do
|
37
|
+
Worker.should_receive(:new).and_return(@worker)
|
38
|
+
@instance.validate('name', 'password')
|
25
39
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
it "should create an internal worker for each ip address" do
|
30
|
-
@instance.stub!(:server_ips).and_return(['1.1.1.1', '2.2.2.2'])
|
31
|
-
@instance.should_receive(:create_worker).with('username', 'password', '1.1.1.1').and_return('worker1')
|
32
|
-
@instance.should_receive(:create_worker).with('username', 'password', '2.2.2.2').and_return('worker2')
|
33
|
-
@instance.send(:workers, 'username', 'password').should == ['worker1', 'worker2']
|
40
|
+
|
41
|
+
it "should return the result of the validation" do
|
42
|
+
@instance.validate('name', 'password').should be_true
|
34
43
|
end
|
35
|
-
|
36
|
-
it "should
|
37
|
-
|
38
|
-
@
|
39
|
-
@
|
40
|
-
@instance.send(:create_worker, 'username', 'password', '1.1.1.1').should == @ldap
|
44
|
+
|
45
|
+
it "should use the second ip address when the first one times out" do
|
46
|
+
@worker.should_receive(:bind).ordered.and_raise(Timeout::Error)
|
47
|
+
@worker.should_receive(:bind).ordered.and_return(true)
|
48
|
+
@instance.validate('name', 'password').should be_true
|
41
49
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#Search' do
|
53
|
+
it "should check that username and password are not blank" do
|
54
|
+
Validator.should_receive(:blank?).and_return(true)
|
55
|
+
@instance.search(nil).should be_nil
|
46
56
|
end
|
47
|
-
|
48
|
-
it "should create a
|
49
|
-
|
50
|
-
|
51
|
-
@instance.
|
57
|
+
|
58
|
+
it "should create a new worker for the first ip address" do
|
59
|
+
@worker = mock(Worker, :search_directory => [], :bind => true)
|
60
|
+
Worker.should_receive(:new).and_return(@worker)
|
61
|
+
@instance.search('name')
|
52
62
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@ldap.should_receive(:search).with(
|
59
|
-
:base => @instance.treebase,
|
60
|
-
:filter => filter1 & filter2,
|
61
|
-
:attributes => Base.const_get('LDAP_ATTRS'),
|
62
|
-
:return_result => true
|
63
|
-
).and_return(['result'])
|
64
|
-
@instance.send(:search_active_directory, @ldap, 'search-string').should == ['result']
|
65
|
-
end
|
66
|
-
|
67
|
-
it "should return a valid result when search cannot bind to the domain" do
|
68
|
-
@ldap.should_receive(:search).and_return(false)
|
69
|
-
@instance.send(:search_active_directory, @ldap, 'search-string').should == []
|
70
|
-
end
|
63
|
+
|
64
|
+
it "should return the result of the search" do
|
65
|
+
@worker = mock(Worker, :search_directory => ['result'], :bind => true)
|
66
|
+
Worker.stub!(:new).and_return(@worker)
|
67
|
+
@instance.search('name').should == ['result']
|
71
68
|
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
it "should stop the validation cycle when the first result if received" do
|
81
|
-
ldap2 = mock(Net::LDAP)
|
82
|
-
ldap2.should_not_receive(:bind)
|
83
|
-
@ldap.should_receive(:bind).and_return(true)
|
84
|
-
@instance.stub!(:workers).and_return([@ldap, ldap2])
|
85
|
-
@instance.validate('username', 'password').should be_true
|
86
|
-
end
|
87
|
-
|
88
|
-
it "should return false when a timeout occurs" do
|
89
|
-
@instance.stub!(:workers).and_return([@ldap])
|
90
|
-
@ldap.should_receive(:bind).and_raise(Timeout::Error)
|
91
|
-
@instance.validate('username', 'password').should be_false
|
92
|
-
end
|
69
|
+
|
70
|
+
it "should use the second ip address when the first one times out" do
|
71
|
+
@worker.stub(:bind).and_return(true)
|
72
|
+
@worker.should_receive(:search_directory).ordered.and_raise(Timeout::Error)
|
73
|
+
@worker.should_receive(:search_directory).ordered.and_return(['result'])
|
74
|
+
@instance.search('name').should == ['result']
|
93
75
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
@ldap.should_receive(:search).and_return(false)
|
99
|
-
@instance.search('string').should == []
|
100
|
-
end
|
101
|
-
|
102
|
-
it "should sanitize the Net::LDAP search result into an array of generic structures" do
|
103
|
-
@instance.should_receive(:workers).and_return([])
|
104
|
-
@instance.should_receive(:create_result_collection).and_return([])
|
105
|
-
@instance.search('search-string').should == []
|
106
|
-
end
|
76
|
+
|
77
|
+
it "should throw an exception if the search user credentials are invalid" do
|
78
|
+
@worker.should_receive(:bind).and_return(false)
|
79
|
+
lambda{ @instance.search('name') }.should raise_error(InvalidCredentialsError, 'The user credentials provided are invalid')
|
107
80
|
end
|
108
81
|
end
|
109
|
-
|
82
|
+
|
110
83
|
end
|
111
84
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ElDap
|
4
|
+
describe Validator do
|
5
|
+
|
6
|
+
it "should return false when a string is not nil or empty" do
|
7
|
+
Validator.blank?('string').should be_false
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return false when all the strings are not nil or empty" do
|
11
|
+
Validator.blank?('string', 'thing').should be_false
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return true when a string is nil or empty" do
|
15
|
+
Validator.blank?('').should be_true
|
16
|
+
Validator.blank?(nil).should be_true
|
17
|
+
Validator.blank?(nil, '').should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return true when any string is nil or empty" do
|
21
|
+
Validator.blank?('', '').should be_true
|
22
|
+
Validator.blank?(nil, nil).should be_true
|
23
|
+
Validator.blank?('', 'string', nil).should be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return false when all strings are not nil or empty" do
|
27
|
+
Validator.blank?('string', 'string', 'string').should be_false
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ElDap
|
4
|
+
describe Worker do
|
5
|
+
before(:each) do
|
6
|
+
@instance = Worker.new(:ip_address => '1.1.1.1', :username => 'name', :password => 'password')
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should instantiate with the correct arguments" do
|
10
|
+
@instance.host.should == '1.1.1.1'
|
11
|
+
@instance.instance_variable_get(:@auth).should == {:method=>:simple, :username=>"name", :password=>"password"}
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should search the directory using the correct filters and parameters" do
|
15
|
+
filter1 = Net::LDAP::Filter.eq("objectcategory", "person")
|
16
|
+
filter2 = Net::LDAP::Filter.eq("cn", "*string*")
|
17
|
+
@instance.should_receive(:search).with(
|
18
|
+
:base => 'treebase',
|
19
|
+
:filter => filter1 & filter2,
|
20
|
+
:attributes => Worker.const_get('LDAP_ATTRS'),
|
21
|
+
:return_result => true
|
22
|
+
).and_return([])
|
23
|
+
@instance.search_directory('string', 'treebase').should == []
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should transform a search result into a collection of structs" do
|
27
|
+
@instance.stub!(:create_struct).and_return('struct')
|
28
|
+
@instance.send(:create_result_collection, ['result']).should == ['struct']
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should create a struct from a search result hash" do
|
32
|
+
search_result = {:cn => ['cn'], :mail => ['mail']}
|
33
|
+
struct = OpenStruct.new(:cn => 'cn',:mail => 'mail')
|
34
|
+
@instance.send(:create_struct, search_result).should == struct
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should complete gracefully if invalid credentials are used for search (search returns false)" do
|
38
|
+
@instance.should_receive(:search).and_return(false)
|
39
|
+
@instance.search_directory('string', 'treebase').should == []
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: el_dap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-04-
|
12
|
+
date: 2011-04-20 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: net-ldap
|
16
|
-
requirement: &
|
16
|
+
requirement: &83249460 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: 0.2.2
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *83249460
|
25
25
|
description: A simple search and authentication tool for Active Directory using LDAP.
|
26
26
|
email:
|
27
27
|
- ed.james.email@gmail.com
|
@@ -37,16 +37,22 @@ files:
|
|
37
37
|
- LICENSE
|
38
38
|
- README.rdoc
|
39
39
|
- Rakefile
|
40
|
+
- TODO.rdoc
|
40
41
|
- el_dap.gemspec
|
41
42
|
- lib/el_dap.rb
|
42
43
|
- lib/el_dap/.rvmrc
|
43
44
|
- lib/el_dap/api_methods.rb
|
44
45
|
- lib/el_dap/base.rb
|
45
46
|
- lib/el_dap/constants.rb
|
47
|
+
- lib/el_dap/exceptions.rb
|
48
|
+
- lib/el_dap/validator.rb
|
46
49
|
- lib/el_dap/version.rb
|
50
|
+
- lib/el_dap/worker.rb
|
47
51
|
- spec/el_dap/api_methods_spec.rb
|
48
52
|
- spec/el_dap/base_spec.rb
|
49
53
|
- spec/el_dap/constants_spec.rb
|
54
|
+
- spec/el_dap/validator_spec.rb
|
55
|
+
- spec/el_dap/worker_spec.rb
|
50
56
|
- spec/spec_helper.rb
|
51
57
|
homepage: https://github.com/edjames/el_dap
|
52
58
|
licenses: []
|