ruby-activeldap 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/CHANGES +5 -0
  2. data/Manifest.txt +91 -25
  3. data/README +22 -0
  4. data/Rakefile +41 -8
  5. data/TODO +1 -6
  6. data/examples/config.yaml.example +5 -0
  7. data/examples/example.der +0 -0
  8. data/examples/example.jpg +0 -0
  9. data/examples/groupadd +41 -0
  10. data/examples/groupdel +35 -0
  11. data/examples/groupls +49 -0
  12. data/examples/groupmod +42 -0
  13. data/examples/lpasswd +55 -0
  14. data/examples/objects/group.rb +13 -0
  15. data/examples/objects/ou.rb +4 -0
  16. data/examples/objects/user.rb +20 -0
  17. data/examples/ouadd +38 -0
  18. data/examples/useradd +45 -0
  19. data/examples/useradd-binary +50 -0
  20. data/examples/userdel +34 -0
  21. data/examples/userls +50 -0
  22. data/examples/usermod +42 -0
  23. data/examples/usermod-binary-add +47 -0
  24. data/examples/usermod-binary-add-time +51 -0
  25. data/examples/usermod-binary-del +48 -0
  26. data/examples/usermod-lang-add +43 -0
  27. data/lib/active_ldap.rb +213 -214
  28. data/lib/active_ldap/adapter/base.rb +461 -0
  29. data/lib/active_ldap/adapter/ldap.rb +232 -0
  30. data/lib/active_ldap/adapter/ldap_ext.rb +69 -0
  31. data/lib/active_ldap/adapter/net_ldap.rb +288 -0
  32. data/lib/active_ldap/adapter/net_ldap_ext.rb +29 -0
  33. data/lib/active_ldap/association/belongs_to.rb +3 -1
  34. data/lib/active_ldap/association/belongs_to_many.rb +5 -6
  35. data/lib/active_ldap/association/has_many.rb +9 -17
  36. data/lib/active_ldap/association/has_many_wrap.rb +4 -5
  37. data/lib/active_ldap/attributes.rb +4 -0
  38. data/lib/active_ldap/base.rb +201 -56
  39. data/lib/active_ldap/configuration.rb +11 -1
  40. data/lib/active_ldap/connection.rb +15 -9
  41. data/lib/active_ldap/distinguished_name.rb +246 -0
  42. data/lib/active_ldap/ldap_error.rb +74 -0
  43. data/lib/active_ldap/object_class.rb +9 -5
  44. data/lib/active_ldap/schema.rb +50 -9
  45. data/lib/active_ldap/validations.rb +11 -13
  46. data/rails/plugin/active_ldap/generators/scaffold_al/scaffold_al_generator.rb +7 -0
  47. data/rails/plugin/active_ldap/generators/scaffold_al/templates/ldap.yml +21 -0
  48. data/rails/plugin/active_ldap/init.rb +10 -4
  49. data/test/al-test-utils.rb +46 -3
  50. data/test/run-test.rb +16 -4
  51. data/test/test-unit-ext/always-show-result.rb +28 -0
  52. data/test/test-unit-ext/priority.rb +163 -0
  53. data/test/test_adapter.rb +81 -0
  54. data/test/test_attributes.rb +8 -1
  55. data/test/test_base.rb +132 -3
  56. data/test/test_base_per_instance.rb +14 -3
  57. data/test/test_connection.rb +19 -0
  58. data/test/test_dn.rb +161 -0
  59. data/test/test_find.rb +24 -0
  60. data/test/test_object_class.rb +15 -2
  61. data/test/test_schema.rb +108 -1
  62. metadata +111 -41
  63. data/lib/active_ldap/adaptor/base.rb +0 -29
  64. data/lib/active_ldap/adaptor/ldap.rb +0 -466
  65. data/lib/active_ldap/ldap.rb +0 -113
@@ -4,6 +4,21 @@ class TestObjectClass < Test::Unit::TestCase
4
4
  include AlTestUtils
5
5
 
6
6
  priority :must
7
+ def test_ensure_recommended_classes
8
+ make_temporary_group do |group|
9
+ added_class = "labeledURIObject"
10
+
11
+ assert_equal([], group.class.recommended_classes)
12
+ group.class.recommended_classes += [added_class]
13
+ assert_equal([added_class], group.class.recommended_classes)
14
+
15
+ assert_equal([added_class],
16
+ group.class.recommended_classes - group.classes)
17
+ group.ensure_recommended_classes
18
+ assert_equal([],
19
+ group.class.recommended_classes - group.classes)
20
+ end
21
+ end
7
22
 
8
23
  priority :normal
9
24
  def test_unknown_object_class
@@ -27,6 +42,4 @@ class TestObjectClass < Test::Unit::TestCase
27
42
  assert_raises(TypeError) {group.add_class(:posixAccount)}
28
43
  end
29
44
  end
30
-
31
- priority :normal
32
45
  end
data/test/test_schema.rb CHANGED
@@ -1,6 +1,104 @@
1
1
  require 'al-test-utils'
2
2
 
3
3
  class TestSchema < Test::Unit::TestCase
4
+ priority :must
5
+ def test_empty_schema
6
+ assert_make_schema_with_empty_entries(nil)
7
+ assert_make_schema_with_empty_entries({})
8
+ assert_make_schema_with_empty_entries({"someValues" => ["aValue"]})
9
+ end
10
+
11
+ def test_empty_schema_value
12
+ schema = ActiveLdap::Schema.new({"attributeTypes" => nil})
13
+ assert_equal([], schema["attributeTypes", "cn", "DESC"])
14
+ end
15
+
16
+ priority :normal
17
+ def test_attribute_name_with_under_score
18
+ top_schema =
19
+ "( 2.5.6.0 NAME 'Top' STRUCTURAL MUST objectClass MAY ( " +
20
+ "cAPublicKey $ cAPrivateKey $ certificateValidityInterval $ " +
21
+ "authorityRevocation $ lastReferencedTime $ " +
22
+ "equivalentToMe $ ACL $ backLink $ binderyProperty $ " +
23
+ "Obituary $ Reference $ revision $ " +
24
+ "ndsCrossCertificatePair $ certificateRevocation $ " +
25
+ "usedBy $ GUID $ otherGUID $ DirXML-Associations $ " +
26
+ "creatorsName $ modifiersName $ unknownBaseClass $ " +
27
+ "unknownAuxiliaryClass $ auditFileLink $ " +
28
+ "masvProposedLabelel $ masvDefaultRange $ " +
29
+ "masvAuthorizedRange $ objectVersion $ " +
30
+ "auxClassCompatibility $ rbsAssignedRoles $ " +
31
+ "rbsOwnedCollections $ rbsAssignedRoles2 $ " +
32
+ "rbsOwnedCollections2 ) X-NDS_NONREMOVABLE '1' " +
33
+ "X-NDS_ACL_TEMPLATES '16#subtree#[Creator]#[Entry Rights]' )"
34
+
35
+ expect = {
36
+ :name => ["Top"],
37
+ :structural => ["TRUE"],
38
+ :must => ["objectClass"],
39
+ :x_nds_nonremovable => ["1"],
40
+ :x_nds_acl_templates => ['16#subtree#[Creator]#[Entry Rights]'],
41
+ }
42
+ assert_schema(expect, "Top", top_schema)
43
+ end
44
+
45
+ def test_sup_with_oid_start_with_upper_case
46
+ organizational_person_schema =
47
+ "( 2.5.6.7 NAME 'organizationalPerson' SUP Person STRUCTURAL MAY " +
48
+ "( facsimileTelephoneNumber $ l $ eMailAddress $ ou $ " +
49
+ "physicalDeliveryOfficeName $ postalAddress $ postalCode $ " +
50
+ "postOfficeBox $ st $ street $ title $ mailboxLocation $ " +
51
+ "mailboxID $ uid $ mail $ employeeNumber $ destinationIndicator $ " +
52
+ "internationaliSDNNumber $ preferredDeliveryMethod $ " +
53
+ "registeredAddress $ teletexTerminalIdentifier $ telexNumber $ " +
54
+ "x121Address $ businessCategory $ roomNumber $ x500UniqueIdentifier " +
55
+ ") X-NDS_NAMING ( 'cn' 'ou' 'uid' ) X-NDS_CONTAINMENT ( " +
56
+ "'Organization' 'organizationalUnit' 'domain' ) X-NDS_NAME " +
57
+ "'Organizational Person' X-NDS_NOT_CONTAINER '1' " +
58
+ "X-NDS_NONREMOVABLE '1' )"
59
+
60
+ expect = {
61
+ :name => ["organizationalPerson"],
62
+ :sup => ["Person"],
63
+ :structural => ["TRUE"],
64
+ :x_nds_naming => ["cn", "ou", "uid"],
65
+ :x_nds_containment => ["Organization", "organizationalUnit", "domain"],
66
+ :x_nds_name => ["Organizational Person"],
67
+ :x_nds_not_container => ["1"],
68
+ :x_nds_nonremovable => ["1"],
69
+ }
70
+ assert_schema(expect, "organizationalPerson",
71
+ organizational_person_schema)
72
+ end
73
+
74
+ def test_text_oid
75
+ text_oid_schema = "( mysite-oid NAME 'mysite' " +
76
+ "SUP groupofuniquenames STRUCTURAL " +
77
+ "MUST ( mysitelang $ mysiteurl ) " +
78
+ "MAY ( mysitealias $ mysitecmsurl ) " +
79
+ "X-ORIGIN 'user defined' )"
80
+ expect = {
81
+ :name => ["mysite"],
82
+ :sup => ["groupofuniquenames"],
83
+ :structural => ["TRUE"],
84
+ :must => %w(mysitelang mysiteurl),
85
+ :may => %w(mysitealias mysitecmsurl),
86
+ :x_origin => ["user defined"]
87
+ }
88
+ assert_schema(expect, "mysite", text_oid_schema)
89
+
90
+ text_oid_attribute_schema = "( mysiteurl-oid NAME 'mysiteurl' " +
91
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " +
92
+ "SINGLE-VALUE X-ORIGIN 'user defined' )"
93
+ expect = {
94
+ :name => ["mysiteurl"],
95
+ :syntax => ["1.3.6.1.4.1.1466.115.121.1.15"],
96
+ :single_value => ["TRUE"],
97
+ :x_origin => ["user defined"]
98
+ }
99
+ assert_schema(expect, "mysiteurl", text_oid_attribute_schema)
100
+ end
101
+
4
102
  def test_name_as_key
5
103
  top_schema = "( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' " +
6
104
  "ABSTRACT MUST objectClass )"
@@ -159,8 +257,17 @@ class TestSchema < Test::Unit::TestCase
159
257
  expect.each do |key, value|
160
258
  normalized_key = key.to_s.gsub(/_/, "-")
161
259
  normalized_expect[normalized_key] = value
162
- actual[normalized_key] = schema.attr(sub, name, normalized_key)
260
+ actual[normalized_key] = schema[sub, name, normalized_key]
163
261
  end
164
262
  assert_equal(normalized_expect, actual)
165
263
  end
264
+
265
+ def assert_make_schema_with_empty_entries(entries)
266
+ schema = ActiveLdap::Schema.new(entries)
267
+ assert_equal([], schema["attributeTypes", "cn", "DESC"])
268
+ assert_equal([], schema["ldapSyntaxes",
269
+ "1.3.6.1.4.1.1466.115.121.1.5",
270
+ "DESC"])
271
+ assert_equal([], schema["objectClasses", "posixAccount", "MUST"])
272
+ end
166
273
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: ruby-activeldap
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.8.1
7
- date: 2007-01-28 00:00:00 +00:00
6
+ version: 0.8.2
7
+ date: 2007-05-15 00:00:00 -07:00
8
8
  summary: Ruby/ActiveLdap is a object-oriented API to LDAP
9
9
  require_paths:
10
10
  - lib
@@ -32,72 +32,133 @@ authors:
32
32
  - Will Drewry
33
33
  - Kouhei Sutou
34
34
  files:
35
- - Rakefile
36
- - LICENSE
37
- - TODO
38
- - COPYING
39
35
  - CHANGES
40
- - README
36
+ - COPYING
37
+ - LICENSE
41
38
  - Manifest.txt
42
- - lib/active_ldap/adaptor/ldap.rb
43
- - lib/active_ldap/adaptor/base.rb
44
- - lib/active_ldap/association/has_many_wrap.rb
45
- - lib/active_ldap/association/has_many.rb
46
- - lib/active_ldap/association/proxy.rb
39
+ - README
40
+ - Rakefile
41
+ - TODO
42
+ - benchmark/bench-al.rb
43
+ - examples/config.yaml.example
44
+ - examples/example.der
45
+ - examples/example.jpg
46
+ - examples/groupadd
47
+ - examples/groupdel
48
+ - examples/groupls
49
+ - examples/groupmod
50
+ - examples/lpasswd
51
+ - examples/objects/group.rb
52
+ - examples/objects/ou.rb
53
+ - examples/objects/user.rb
54
+ - examples/ouadd
55
+ - examples/useradd
56
+ - examples/useradd-binary
57
+ - examples/userdel
58
+ - examples/userls
59
+ - examples/usermod
60
+ - examples/usermod-binary-add
61
+ - examples/usermod-binary-add-time
62
+ - examples/usermod-binary-del
63
+ - examples/usermod-lang-add
64
+ - lib/active_ldap.rb
65
+ - lib/active_ldap/adapter/base.rb
66
+ - lib/active_ldap/adapter/ldap.rb
67
+ - lib/active_ldap/adapter/ldap_ext.rb
68
+ - lib/active_ldap/adapter/net_ldap.rb
69
+ - lib/active_ldap/adapter/net_ldap_ext.rb
70
+ - lib/active_ldap/association/belongs_to.rb
47
71
  - lib/active_ldap/association/belongs_to_many.rb
48
72
  - lib/active_ldap/association/collection.rb
49
- - lib/active_ldap/association/belongs_to.rb
50
- - lib/active_ldap/validations.rb
51
- - lib/active_ldap/command.rb
52
- - lib/active_ldap/callbacks.rb
53
- - lib/active_ldap/ldap.rb
54
- - lib/active_ldap/timeout_stub.rb
55
- - lib/active_ldap/timeout.rb
73
+ - lib/active_ldap/association/has_many.rb
74
+ - lib/active_ldap/association/has_many_wrap.rb
75
+ - lib/active_ldap/association/proxy.rb
76
+ - lib/active_ldap/associations.rb
56
77
  - lib/active_ldap/attributes.rb
57
- - lib/active_ldap/object_class.rb
78
+ - lib/active_ldap/base.rb
79
+ - lib/active_ldap/callbacks.rb
80
+ - lib/active_ldap/command.rb
81
+ - lib/active_ldap/configuration.rb
58
82
  - lib/active_ldap/connection.rb
59
- - lib/active_ldap/associations.rb
60
- - lib/active_ldap/user_password.rb
83
+ - lib/active_ldap/distinguished_name.rb
84
+ - lib/active_ldap/ldap_error.rb
85
+ - lib/active_ldap/object_class.rb
61
86
  - lib/active_ldap/schema.rb
62
- - lib/active_ldap/configuration.rb
63
- - lib/active_ldap/base.rb
64
- - lib/active_ldap.rb
65
- - benchmark/bench-al.rb
66
- - rails/plugin/active_ldap/init.rb
87
+ - lib/active_ldap/timeout.rb
88
+ - lib/active_ldap/timeout_stub.rb
89
+ - lib/active_ldap/user_password.rb
90
+ - lib/active_ldap/validations.rb
67
91
  - rails/plugin/active_ldap/README
68
- test_files:
69
- - test/test-unit-ext
70
- - test/test_useradd.rb
71
- - test/test_user.rb
92
+ - rails/plugin/active_ldap/generators/scaffold_al/scaffold_al_generator.rb
93
+ - rails/plugin/active_ldap/generators/scaffold_al/templates/ldap.yml
94
+ - rails/plugin/active_ldap/init.rb
95
+ - test/TODO
96
+ - test/al-test-utils.rb
72
97
  - test/command.rb
73
- - test/test_usermod.rb
98
+ - test/config.yaml.sample
99
+ - test/run-test.rb
100
+ - test/test-unit-ext.rb
101
+ - test/test-unit-ext/always-show-result.rb
102
+ - test/test-unit-ext/priority.rb
103
+ - test/test_adapter.rb
104
+ - test/test_associations.rb
105
+ - test/test_attributes.rb
106
+ - test/test_base.rb
107
+ - test/test_base_per_instance.rb
74
108
  - test/test_bind.rb
109
+ - test/test_callback.rb
110
+ - test/test_connection.rb
111
+ - test/test_connection_per_class.rb
112
+ - test/test_dn.rb
113
+ - test/test_find.rb
114
+ - test/test_groupadd.rb
115
+ - test/test_groupdel.rb
116
+ - test/test_groupls.rb
117
+ - test/test_groupmod.rb
118
+ - test/test_lpasswd.rb
119
+ - test/test_object_class.rb
120
+ - test/test_reflection.rb
121
+ - test/test_schema.rb
122
+ - test/test_user.rb
123
+ - test/test_user_password.rb
124
+ - test/test_useradd-binary.rb
125
+ - test/test_useradd.rb
126
+ - test/test_userdel.rb
127
+ - test/test_userls.rb
128
+ - test/test_usermod-binary-add-time.rb
129
+ - test/test_usermod-binary-add.rb
130
+ - test/test_usermod-binary-del.rb
131
+ - test/test_usermod-lang-add.rb
132
+ - test/test_usermod.rb
133
+ - test/test_validation.rb
134
+ test_files:
135
+ - test/test_user.rb
75
136
  - test/test_usermod-binary-add-time.rb
76
137
  - test/test_usermod-lang-add.rb
77
138
  - test/test_attributes.rb
78
139
  - test/test_usermod-binary-add.rb
140
+ - test/test_useradd.rb
79
141
  - test/test_validation.rb
80
- - test/test-unit-ext.rb
81
142
  - test/test_object_class.rb
82
143
  - test/test_groupmod.rb
83
144
  - test/test_associations.rb
84
145
  - test/test_user_password.rb
85
146
  - test/test_base_per_instance.rb
86
- - test/al-test-utils.rb
87
147
  - test/test_useradd-binary.rb
88
- - test/test_groupdel.rb
148
+ - test/test_bind.rb
149
+ - test/test_usermod.rb
89
150
  - test/test_find.rb
90
- - test/test_groupls.rb
91
- - test/run-test.rb
92
- - test/TODO
93
- - test/config.yaml.sample
151
+ - test/test_adapter.rb
152
+ - test/test_groupdel.rb
153
+ - test/test_connection_per_class.rb
94
154
  - test/test_reflection.rb
155
+ - test/test_groupls.rb
95
156
  - test/test_callback.rb
96
- - test/test_connection_per_class.rb
97
157
  - test/test_connection.rb
98
158
  - test/test_schema.rb
99
- - test/test_userdel.rb
159
+ - test/test_dn.rb
100
160
  - test/test_usermod-binary-del.rb
161
+ - test/test_userdel.rb
101
162
  - test/test_groupadd.rb
102
163
  - test/test_base.rb
103
164
  - test/test_userls.rb
@@ -132,3 +193,12 @@ dependencies:
132
193
  - !ruby/object:Gem::Version
133
194
  version: 0.0.0
134
195
  version:
196
+ - !ruby/object:Gem::Dependency
197
+ name: hoe
198
+ version_requirement:
199
+ version_requirements: !ruby/object:Gem::Version::Requirement
200
+ requirements:
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: 1.2.0
204
+ version:
@@ -1,29 +0,0 @@
1
- module ActiveLdap
2
- module Adaptor
3
- class Base
4
- def initialize(config={})
5
- @connection = nil
6
- @config = config.dup
7
- @logger = @config.delete(:logger)
8
- %w(host port method timeout retry_on_timeout
9
- retry_limit retry_wait bind_dn password
10
- password_block try_sasl allow_anonymous
11
- store_password).each do |name|
12
- instance_variable_set("@#{name}", config[name.to_sym])
13
- end
14
- end
15
-
16
- private
17
- def with_timeout(try_reconnect=true, &block)
18
- begin
19
- Timeout.alarm(@timeout, &block)
20
- rescue Timeout::Error => e
21
- @logger.error {'Requested action timed out.'}
22
- retry if try_reconnect and @retry_on_timeout and reconnect
23
- @logger.error {e.message}
24
- raise TimeoutError, e.message
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,466 +0,0 @@
1
- require 'ldap'
2
- require 'ldap/ldif'
3
- require 'ldap/schema'
4
-
5
- require 'active_ldap/ldap'
6
- require 'active_ldap/schema'
7
-
8
- require 'active_ldap/adaptor/base'
9
-
10
- class LDAP::Mod
11
- unless instance_method(:to_s).arity.zero?
12
- def to_s
13
- inspect
14
- end
15
- end
16
-
17
- alias_method :_initialize, :initialize
18
- def initialize(op, type, vals)
19
- if (LDAP::VERSION.split(/\./).collect {|x| x.to_i} <=> [0, 9, 7]) <= 0
20
- @op, @type, @vals = op, type, vals # to protect from GC
21
- end
22
- _initialize(op, type, vals)
23
- end
24
- end
25
-
26
- module ActiveLdap
27
- module Adaptor
28
- class Ldap < Base
29
- module Method
30
- class SSL
31
- def connect(host, port)
32
- LDAP::SSLConn.new(host, port, false)
33
- end
34
- end
35
-
36
- class TLS
37
- def connect(host, port)
38
- LDAP::SSLConn.new(host, port, true)
39
- end
40
- end
41
-
42
- class Plain
43
- def connect(host, port)
44
- LDAP::Conn.new(host, port)
45
- end
46
- end
47
- end
48
-
49
- SCOPE = {
50
- :base => LDAP::LDAP_SCOPE_BASE,
51
- :sub => LDAP::LDAP_SCOPE_SUBTREE,
52
- :one => LDAP::LDAP_SCOPE_ONELEVEL,
53
- }
54
-
55
- def connect(options={})
56
- method = ensure_method(options[:method] || @method)
57
- host = options[:host] || @host
58
- port = options[:port] || @port
59
-
60
- @connection = method.connect(host, port)
61
- operation(options) do
62
- @connection.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
63
- end
64
- bind(options)
65
- end
66
-
67
- def schema(options={})
68
- @schema ||= operation(options) do
69
- base = options[:base]
70
- attrs = options[:attrs]
71
- sec = options[:sec] || 0
72
- usec = options[:usec] || 0
73
-
74
- attrs ||= [
75
- 'objectClasses',
76
- 'attributeTypes',
77
- 'matchingRules',
78
- 'matchingRuleUse',
79
- 'dITStructureRules',
80
- 'dITContentRules',
81
- 'nameForms',
82
- 'ldapSyntaxes',
83
- ]
84
- key = 'subschemaSubentry'
85
- base ||= @connection.root_dse([key], sec, usec)[0][key][0]
86
- base ||= 'cn=schema'
87
- result = @connection.search2(base, LDAP::LDAP_SCOPE_BASE,
88
- '(objectClass=subschema)', attrs, false,
89
- sec, usec).first
90
- Schema.new(result)
91
- end
92
- # rescue
93
- # raise ConnectionError.new("Unable to retrieve schema from " +
94
- # "server (#{@method.class.downcase})")
95
- end
96
-
97
- def disconnect!(options={})
98
- return if @connection.nil?
99
- begin
100
- unbind(options)
101
- #rescue
102
- end
103
- @connection = nil
104
- # Make sure it is cleaned up
105
- # This causes Ruby/LDAP memory corruption.
106
- # GC.start
107
- end
108
-
109
- def unbind(options={})
110
- return unless bound?
111
- operation(options) do
112
- @connection.unbind
113
- end
114
- end
115
-
116
- def rebind(options={})
117
- unbind(options) if bound?
118
- connect(options)
119
- end
120
-
121
- def bind(options={})
122
- bind_dn = options[:bind_dn] || @bind_dn
123
- try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl
124
- if options.has_key?(:allow_anonymous)
125
- allow_anonymous = options[:allow_anonymous]
126
- else
127
- allow_anonymous = @allow_anonymous
128
- end
129
-
130
- # Rough bind loop:
131
- # Attempt 1: SASL if available
132
- # Attempt 2: SIMPLE with credentials if password block
133
- # Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail (or pwblock returns '')
134
- if try_sasl and sasl_bind(bind_dn, options)
135
- @logger.info {'Bound SASL'}
136
- elsif simple_bind(bind_dn, options)
137
- @logger.info {'Bound simple'}
138
- elsif allow_anonymous and bind_as_anonymous(options)
139
- @logger.info {'Bound anonymous'}
140
- else
141
- if @connection.err.zero?
142
- message = 'All authentication methods exhausted.'
143
- else
144
- message = LDAP.err2string(@connection.err)
145
- end
146
- raise AuthenticationError, message
147
- end
148
-
149
- bound?
150
- end
151
-
152
- def bind_as_anonymous(options={})
153
- @logger.info {"Attempting anonymous authentication"}
154
- operation(options) do
155
- @connection.bind
156
- true
157
- end
158
- end
159
-
160
- def connecting?
161
- not @connection.nil?
162
- end
163
-
164
- def bound?
165
- connecting? and @connection.bound?
166
- end
167
-
168
- # search
169
- #
170
- # Wraps Ruby/LDAP connection.search to make it easier to search for
171
- # specific data without cracking open Base.connection
172
- def search(options={})
173
- filter = options[:filter] || 'objectClass=*'
174
- attrs = options[:attributes] || []
175
- scope = ensure_scope(options[:scope])
176
- base = options[:base]
177
- limit = options[:limit] || 0
178
- limit = nil if limit <= 0
179
-
180
- values = []
181
- attrs = attrs.to_a # just in case
182
-
183
- begin
184
- operation(options) do
185
- i = 0
186
- @connection.search(base, scope, filter, attrs) do |m|
187
- i += 1
188
- attributes = {}
189
- m.attrs.each do |attr|
190
- attributes[attr] = m.vals(attr)
191
- end
192
- value = [m.dn, attributes]
193
- value = yield(value) if block_given?
194
- values.push(value)
195
- break if limit and limit >= i
196
- end
197
- end
198
- rescue LDAP::Error
199
- # Do nothing on failure
200
- @logger.debug {"Ignore error #{$!.class}(#{$!.message}) " +
201
- "for #{filter} and attrs #{attrs.inspect}"}
202
- rescue RuntimeError
203
- if $!.message == "no result returned by search"
204
- @logger.debug {"No matches for #{filter} and attrs " +
205
- "#{attrs.inspect}"}
206
- else
207
- raise
208
- end
209
- end
210
-
211
- values
212
- end
213
-
214
- def to_ldif(dn, attributes)
215
- ldif = LDAP::LDIF.to_ldif("dn", [dn.dup])
216
- attributes.sort_by do |key, value|
217
- key
218
- end.each do |key, values|
219
- ldif << LDAP::LDIF.to_ldif(key, values)
220
- end
221
- ldif
222
- end
223
-
224
- def load(ldifs, options={})
225
- operation(options) do
226
- ldifs.split(/(?:\r?\n){2,}/).each do |ldif|
227
- LDAP::LDIF.parse_entry(ldif).send(@connection)
228
- end
229
- end
230
- end
231
-
232
- def delete(targets, options={})
233
- targets = [targets] unless targets.is_a?(Array)
234
- return if targets.empty?
235
- target = nil
236
- begin
237
- operation(options) do
238
- targets.each do |target|
239
- @connection.delete(target)
240
- end
241
- end
242
- rescue LDAP::NoSuchObject
243
- raise EntryNotFound, "No such entry: #{target}"
244
- end
245
- end
246
-
247
- def add(dn, entries, options={})
248
- begin
249
- operation(options) do
250
- @connection.add(dn, parse_entries(entries))
251
- end
252
- rescue LDAP::NoSuchObject
253
- raise EntryNotFound, "No such entry: #{dn}"
254
- rescue LDAP::InvalidDnSyntax
255
- raise DistinguishedNameInvalid.new(dn)
256
- rescue LDAP::AlreadyExists
257
- raise EntryAlreadyExist, "#{$!.message}: #{dn}"
258
- rescue LDAP::StrongAuthRequired
259
- raise StrongAuthenticationRequired, "#{$!.message}: #{dn}"
260
- rescue LDAP::ObjectClassViolation
261
- raise RequiredAttributeMissed, "#{$!.message}: #{dn}"
262
- rescue LDAP::UnwillingToPerform
263
- raise UnwillingToPerform, "#{$!.message}: #{dn}"
264
- end
265
- end
266
-
267
- def modify(dn, entries, options={})
268
- begin
269
- operation(options) do
270
- @connection.modify(dn, parse_entries(entries))
271
- end
272
- rescue LDAP::UndefinedType
273
- raise
274
- rescue LDAP::ObjectClassViolation
275
- raise RequiredAttributeMissed, "#{$!.message}: #{dn}"
276
- end
277
- end
278
-
279
- private
280
- def operation(options={}, &block)
281
- reconnect_if_need
282
- try_reconnect = !options.has_key?(:try_reconnect) ||
283
- options[:try_reconnect]
284
- with_timeout(try_reconnect) do
285
- begin
286
- block.call
287
- rescue LDAP::ResultError
288
- raise *LDAP::err2exception(@connection.err) if @connection.err != 0
289
- raise
290
- end
291
- end
292
- end
293
-
294
- def with_timeout(try_reconnect=true, &block)
295
- begin
296
- super
297
- rescue LDAP::ServerDown => e
298
- @logger.error {"#{e.class} exception occurred in with_timeout block"}
299
- @logger.error {"Exception message: #{e.message}"}
300
- @logger.error {"Exception backtrace: #{e.backtrace}"}
301
- retry if try_reconnect and reconnect
302
- raise ConnectionError.new(e.message)
303
- end
304
- end
305
-
306
- def ensure_method(method)
307
- Method.constants.each do |name|
308
- if method.to_s.downcase == name.downcase
309
- return Method.const_get(name).new
310
- end
311
- end
312
-
313
- available_methods = Method.constants.collect do |name|
314
- name.downcase.to_sym.inspect
315
- end.join(", ")
316
- raise ConfigurationError,
317
- "#{method.inspect} is not one of the available connect " +
318
- "methods #{available_methods}"
319
- end
320
-
321
- def ensure_scope(scope)
322
- value = SCOPE[scope || :sub]
323
- if value.nil?
324
- available_scopes = SCOPE.keys.collect {|s| s.inspect}
325
- raise ArgumentError, "#{scope.inspect} is not one of the available " +
326
- "LDAP scope #{available_scopes}"
327
- end
328
- value
329
- end
330
-
331
- # Bind to LDAP with the given DN using any available SASL methods
332
- def sasl_bind(bind_dn, options={})
333
- # Get all SASL mechanisms
334
- #
335
- mechanisms = nil
336
- exc = ConnectionError.new('Root DSE query failed')
337
- mechanisms = operation do
338
- @connection.root_dse[0]['supportedSASLMechanisms']
339
- end
340
-
341
- # Use GSSAPI if available
342
- # Currently only GSSAPI is supported with Ruby/LDAP from
343
- # http://caliban.org/files/redhat/RPMS/i386/ruby-ldap-0.8.2-4.i386.rpm
344
- # TODO: Investigate further SASL support
345
- return false unless (mechanisms || []).include?('GSSAPI')
346
- operation do
347
- @connection.sasl_quiet = @sasl_quiet unless @sasl_quit.nil?
348
- @connection.sasl_bind(bind_dn, 'GSSAPI')
349
- true
350
- end
351
- end
352
-
353
- # Bind to LDAP with the given DN and password
354
- def simple_bind(bind_dn, options={})
355
- # Bail if we have no password or password block
356
- if @password.nil? and @password_block.nil?
357
- @logger.error {'Skipping simple bind: ' +
358
- '@password_block and @password options are empty.'}
359
- return false
360
- end
361
-
362
- if @password
363
- password = @password
364
- else
365
- # TODO: Give a warning to reconnect users with password clearing
366
- # Get the passphrase for the first time, or anew if we aren't storing
367
- unless @password_block.respond_to?(:call)
368
- @logger.error {'Skipping simple bind: ' +
369
- '@password_block not nil or Proc object. Ignoring.'}
370
- return false
371
- end
372
- password = @password_block.call(bind_dn)
373
- end
374
-
375
- # Store the password for quick reference later
376
- @password = @store_password ? password : nil
377
-
378
- begin
379
- operation do
380
- @connection.bind(bind_dn, password)
381
- true
382
- end
383
- rescue LDAP::InvalidDnSyntax
384
- @logger.debug {"DN is invalid: #{bind_dn}"}
385
- raise DistinguishedNameInvalid.new(bind_dn)
386
- rescue LDAP::InvalidCredentials
387
- false
388
- end
389
- end
390
-
391
- def parse_entries(entries)
392
- result = []
393
- entries.each do |type, key, attributes|
394
- mod_type = ensure_mod_type(type)
395
- binary = schema.binary?(key)
396
- mod_type |= LDAP::LDAP_MOD_BVALUES if binary
397
- attributes.each do |name, values|
398
- result << LDAP.mod(mod_type, name, values)
399
- end
400
- end
401
- result
402
- end
403
-
404
- def ensure_mod_type(type)
405
- case type
406
- when :replace, :add
407
- LDAP.const_get("LDAP_MOD_#{type.to_s.upcase}")
408
- else
409
- raise ArgumentError, "unknown type: #{type}"
410
- end
411
- end
412
-
413
- # Attempts to reconnect up to the number of times allowed
414
- # If forced, try once then fail with ConnectionError if not connected.
415
- def reconnect(options={})
416
- options = options.dup
417
- force = options[:force]
418
- retry_limit = options[:retry_limit] || @retry_limit
419
- retry_wait = options[:retry_wait] || @retry_wait
420
- options[:reconnect_attempts] ||= 0
421
-
422
- loop do
423
- unless can_reconnect?(options)
424
- raise ConnectionError,
425
- 'Giving up trying to reconnect to LDAP server.'
426
- end
427
-
428
- @logger.debug {'Attempting to reconnect'}
429
- disconnect!
430
-
431
- # Reset the attempts if this was forced.
432
- options[:reconnect_attempts] = 0 if force
433
- options[:reconnect_attempts] += 1 if retry_limit >= 0
434
- begin
435
- connect(options)
436
- break
437
- rescue => detail
438
- @logger.error {"Reconnect to server failed: #{detail.exception}"}
439
- @logger.error {"Reconnect to server failed backtrace: " +
440
- detail.backtrace.join("\n")}
441
- # Do not loop if forced
442
- raise ConnectionError, detail.message if force
443
- end
444
-
445
- # Sleep before looping
446
- sleep retry_wait
447
- end
448
-
449
- true
450
- end
451
-
452
- def reconnect_if_need(options={})
453
- reconnect(options) if !connecting? and can_reconnect?(options)
454
- end
455
-
456
- # Determine if we have exceed the retry limit or not.
457
- # True is reconnecting is allowed - False if not.
458
- def can_reconnect?(options={})
459
- retry_limit = options[:retry_limit] || @retry_limit
460
- reconnect_attempts = options[:reconnect_attempts] || 0
461
-
462
- retry_limit < 0 or reconnect_attempts < (retry_limit - 1)
463
- end
464
- end
465
- end
466
- end