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 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
 
@@ -24,7 +24,25 @@ Just add this to your gem file:
24
24
 
25
25
  == Usage
26
26
 
27
- Assuming we have an Active Directory called *ad.domain.com* which can also be referred to simply as *ad*,
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
@@ -0,0 +1,5 @@
1
+ == TODO
2
+
3
+ * validate the configuration settings prior to executing ElDap operation
4
+ * provide more meaningful exception messages
5
+ * implement bang methods
@@ -1,7 +1,11 @@
1
1
  require 'net/ldap'
2
2
  require 'timeout'
3
+
3
4
  require 'el_dap/api_methods'
4
5
  require 'el_dap/constants'
6
+ require 'el_dap/exceptions'
7
+ require 'el_dap/validator'
8
+ require 'el_dap/worker'
5
9
  require 'el_dap/base'
6
10
 
7
11
  module ElDap
@@ -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(uname, pword)
14
- return false if uname.nil? || uname.empty? || pword.nil? || pword.empty?
15
- workers(uname, pword).each do |worker|
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 search_string.nil? || search_string.empty?
29
- search_result = nil
30
- workers(self.username, self.password).each do |worker|
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
- search_result ||= search_active_directory(worker, search_string)
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
@@ -0,0 +1,3 @@
1
+ module ElDap
2
+ class InvalidCredentialsError < StandardError; end
3
+ end
@@ -0,0 +1,11 @@
1
+ module ElDap
2
+ class Validator
3
+
4
+ def self.blank?(*strings)
5
+ strings.inject(false) do |result, s|
6
+ result || s.nil? || s.empty?
7
+ end
8
+ end
9
+
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module ElDap
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -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
@@ -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
- @ldap = mock(Net::LDAP)
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 "ElDap check for blank parameters" do
12
- it "should not attempt validation if username or password are blank" do
13
- @instance.validate('username', '').should be_false
14
- @instance.validate('username', nil).should be_false
15
- @instance.validate('', 'password').should be_false
16
- @instance.validate(nil, 'password').should be_false
17
- @instance.validate('', '').should be_false
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 not attempt to search if no search criteria are provided" do
22
- @worker.should_not_receive(:search)
23
- @instance.search('').should be_false
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
- end
27
-
28
- describe "protected methods" do
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 create a Net::LDAP instance with the correct parameters" do
37
- Net::LDAP.should_receive(:new).and_return(@ldap)
38
- @ldap.should_receive(:host=).with('1.1.1.1')
39
- @ldap.should_receive(:auth).with(/username/, /password/)
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
- it "should transform a search result into a collection of structs" do
44
- @instance.stub!(:create_struct).and_return('struct')
45
- @instance.send(:create_result_collection, ['result']).should == ['struct']
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 struct from a search result hash" do
49
- search_result = {:cn => ['cn'], :mail => ['mail']}
50
- struct = OpenStruct.new(:cn => 'cn',:mail => 'mail')
51
- @instance.send(:create_struct, search_result).should == struct
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
- describe "search_active_directory method" do
55
- it "should search active directory using the correct filters and parameters" do
56
- filter1 = Net::LDAP::Filter.eq("objectcategory", "person")
57
- filter2 = Net::LDAP::Filter.eq("cn", "*search-string*")
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
- describe "validate method" do
74
- it "should attempt validation when username and password are provided" do
75
- @instance.should_receive(:workers).with(/username/, /password/).and_return([@ldap])
76
- @ldap.should_receive(:bind).and_return(true)
77
- @instance.validate('username', 'password').should be_true
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
- describe 'search method' do
96
- it "should complete gracefully if invalid credentials are used for search (search returns false)" do
97
- @instance.should_receive(:workers).and_return([@ldap])
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.2
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-19 00:00:00.000000000Z
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: &78758470 !ruby/object:Gem::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: *78758470
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: []