el_dap 0.0.2 → 0.0.3

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/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: []