ruby-activeldap 0.8.3 → 0.8.3.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.
Files changed (177) hide show
  1. data/CHANGES +431 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +58 -0
  4. data/README +104 -0
  5. data/Rakefile +165 -0
  6. data/TODO +22 -0
  7. data/benchmark/bench-al.rb +202 -0
  8. data/benchmark/config.yaml.sample +5 -0
  9. data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
  10. data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
  11. data/examples/al-admin/README +182 -0
  12. data/examples/al-admin/Rakefile +10 -0
  13. data/examples/al-admin/app/controllers/account_controller.rb +50 -0
  14. data/examples/al-admin/app/controllers/application.rb +15 -0
  15. data/examples/al-admin/app/controllers/directory_controller.rb +22 -0
  16. data/examples/al-admin/app/controllers/users_controller.rb +38 -0
  17. data/examples/al-admin/app/controllers/welcome_controller.rb +4 -0
  18. data/examples/al-admin/app/helpers/account_helper.rb +2 -0
  19. data/examples/al-admin/app/helpers/application_helper.rb +6 -0
  20. data/examples/al-admin/app/helpers/directory_helper.rb +2 -0
  21. data/examples/al-admin/app/helpers/users_helper.rb +13 -0
  22. data/examples/al-admin/app/helpers/welcome_helper.rb +2 -0
  23. data/examples/al-admin/app/models/entry.rb +19 -0
  24. data/examples/al-admin/app/models/ldap_user.rb +49 -0
  25. data/examples/al-admin/app/models/user.rb +91 -0
  26. data/examples/al-admin/app/views/account/login.rhtml +12 -0
  27. data/examples/al-admin/app/views/account/sign_up.rhtml +22 -0
  28. data/examples/al-admin/app/views/directory/index.rhtml +5 -0
  29. data/examples/al-admin/app/views/directory/populate.rhtml +2 -0
  30. data/examples/al-admin/app/views/layouts/application.rhtml +41 -0
  31. data/examples/al-admin/app/views/users/_attribute_information.rhtml +22 -0
  32. data/examples/al-admin/app/views/users/_entry.rhtml +12 -0
  33. data/examples/al-admin/app/views/users/_form.rhtml +29 -0
  34. data/examples/al-admin/app/views/users/_object_class_information.rhtml +23 -0
  35. data/examples/al-admin/app/views/users/edit.rhtml +10 -0
  36. data/examples/al-admin/app/views/users/index.rhtml +9 -0
  37. data/examples/al-admin/app/views/users/show.rhtml +3 -0
  38. data/examples/al-admin/app/views/welcome/index.rhtml +16 -0
  39. data/examples/al-admin/config/boot.rb +45 -0
  40. data/examples/al-admin/config/database.yml.example +19 -0
  41. data/examples/al-admin/config/environment.rb +68 -0
  42. data/examples/al-admin/config/environments/development.rb +21 -0
  43. data/examples/al-admin/config/environments/production.rb +18 -0
  44. data/examples/al-admin/config/environments/test.rb +19 -0
  45. data/examples/al-admin/config/ldap.yml.example +21 -0
  46. data/examples/al-admin/config/routes.rb +26 -0
  47. data/examples/al-admin/db/migrate/001_create_users.rb +16 -0
  48. data/examples/al-admin/lib/accept_http_rails_relative_url_root.rb +9 -0
  49. data/examples/al-admin/lib/authenticated_system.rb +131 -0
  50. data/examples/al-admin/lib/authenticated_test_helper.rb +113 -0
  51. data/examples/al-admin/lib/tasks/gettext.rake +35 -0
  52. data/examples/al-admin/po/en/al-admin.po +190 -0
  53. data/examples/al-admin/po/ja/al-admin.po +190 -0
  54. data/examples/al-admin/po/nl/al-admin.po +202 -0
  55. data/examples/al-admin/public/.htaccess +40 -0
  56. data/examples/al-admin/public/404.html +30 -0
  57. data/examples/al-admin/public/500.html +30 -0
  58. data/examples/al-admin/public/dispatch.cgi +10 -0
  59. data/examples/al-admin/public/dispatch.fcgi +24 -0
  60. data/examples/al-admin/public/dispatch.rb +10 -0
  61. data/examples/al-admin/public/favicon.ico +0 -0
  62. data/examples/al-admin/public/images/rails.png +0 -0
  63. data/examples/al-admin/public/javascripts/application.js +2 -0
  64. data/examples/al-admin/public/javascripts/controls.js +833 -0
  65. data/examples/al-admin/public/javascripts/dragdrop.js +942 -0
  66. data/examples/al-admin/public/javascripts/effects.js +1088 -0
  67. data/examples/al-admin/public/javascripts/prototype.js +2515 -0
  68. data/examples/al-admin/public/robots.txt +1 -0
  69. data/examples/al-admin/public/stylesheets/rails.css +35 -0
  70. data/examples/al-admin/public/stylesheets/screen.css +52 -0
  71. data/examples/al-admin/script/about +3 -0
  72. data/examples/al-admin/script/breakpointer +3 -0
  73. data/examples/al-admin/script/console +3 -0
  74. data/examples/al-admin/script/destroy +3 -0
  75. data/examples/al-admin/script/generate +3 -0
  76. data/examples/al-admin/script/performance/benchmarker +3 -0
  77. data/examples/al-admin/script/performance/profiler +3 -0
  78. data/examples/al-admin/script/plugin +3 -0
  79. data/examples/al-admin/script/process/inspector +3 -0
  80. data/examples/al-admin/script/process/reaper +3 -0
  81. data/examples/al-admin/script/process/spawner +3 -0
  82. data/examples/al-admin/script/runner +3 -0
  83. data/examples/al-admin/script/server +3 -0
  84. data/examples/al-admin/test/fixtures/users.yml +9 -0
  85. data/examples/al-admin/test/functional/account_controller_test.rb +24 -0
  86. data/examples/al-admin/test/functional/directory_controller_test.rb +18 -0
  87. data/examples/al-admin/test/functional/users_controller_test.rb +18 -0
  88. data/examples/al-admin/test/functional/welcome_controller_test.rb +18 -0
  89. data/examples/al-admin/test/run-test.sh +3 -0
  90. data/examples/al-admin/test/test_helper.rb +28 -0
  91. data/examples/al-admin/test/unit/user_test.rb +13 -0
  92. data/examples/al-admin/vendor/plugins/exception_notification/README +111 -0
  93. data/examples/al-admin/vendor/plugins/exception_notification/init.rb +1 -0
  94. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifiable.rb +99 -0
  95. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier.rb +67 -0
  96. data/examples/al-admin/vendor/plugins/exception_notification/lib/exception_notifier_helper.rb +77 -0
  97. data/examples/al-admin/vendor/plugins/exception_notification/test/exception_notifier_helper_test.rb +61 -0
  98. data/examples/al-admin/vendor/plugins/exception_notification/test/test_helper.rb +7 -0
  99. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_backtrace.rhtml +1 -0
  100. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_environment.rhtml +7 -0
  101. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_inspect_model.rhtml +16 -0
  102. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_request.rhtml +3 -0
  103. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_session.rhtml +2 -0
  104. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/_title.rhtml +3 -0
  105. data/examples/al-admin/vendor/plugins/exception_notification/views/exception_notifier/exception_notification.rhtml +6 -0
  106. data/examples/config.yaml.example +5 -0
  107. data/examples/example.der +0 -0
  108. data/examples/example.jpg +0 -0
  109. data/examples/groupadd +41 -0
  110. data/examples/groupdel +35 -0
  111. data/examples/groupls +49 -0
  112. data/examples/groupmod +42 -0
  113. data/examples/lpasswd +55 -0
  114. data/examples/objects/group.rb +13 -0
  115. data/examples/objects/ou.rb +4 -0
  116. data/examples/objects/user.rb +20 -0
  117. data/examples/ouadd +38 -0
  118. data/examples/useradd +45 -0
  119. data/examples/useradd-binary +50 -0
  120. data/examples/userdel +34 -0
  121. data/examples/userls +50 -0
  122. data/examples/usermod +42 -0
  123. data/examples/usermod-binary-add +47 -0
  124. data/examples/usermod-binary-add-time +51 -0
  125. data/examples/usermod-binary-del +48 -0
  126. data/examples/usermod-lang-add +43 -0
  127. data/lib/active_ldap.rb +978 -0
  128. data/lib/active_ldap/adapter/base.rb +512 -0
  129. data/lib/active_ldap/adapter/ldap.rb +233 -0
  130. data/lib/active_ldap/adapter/ldap_ext.rb +69 -0
  131. data/lib/active_ldap/adapter/net_ldap.rb +290 -0
  132. data/lib/active_ldap/adapter/net_ldap_ext.rb +29 -0
  133. data/lib/active_ldap/association/belongs_to.rb +47 -0
  134. data/lib/active_ldap/association/belongs_to_many.rb +42 -0
  135. data/lib/active_ldap/association/collection.rb +83 -0
  136. data/lib/active_ldap/association/has_many.rb +31 -0
  137. data/lib/active_ldap/association/has_many_utils.rb +35 -0
  138. data/lib/active_ldap/association/has_many_wrap.rb +46 -0
  139. data/lib/active_ldap/association/proxy.rb +102 -0
  140. data/lib/active_ldap/associations.rb +172 -0
  141. data/lib/active_ldap/attributes.rb +211 -0
  142. data/lib/active_ldap/base.rb +1256 -0
  143. data/lib/active_ldap/callbacks.rb +19 -0
  144. data/lib/active_ldap/command.rb +48 -0
  145. data/lib/active_ldap/configuration.rb +114 -0
  146. data/lib/active_ldap/connection.rb +234 -0
  147. data/lib/active_ldap/distinguished_name.rb +250 -0
  148. data/lib/active_ldap/escape.rb +12 -0
  149. data/lib/active_ldap/get_text/parser.rb +142 -0
  150. data/lib/active_ldap/get_text_fallback.rb +53 -0
  151. data/lib/active_ldap/get_text_support.rb +12 -0
  152. data/lib/active_ldap/helper.rb +23 -0
  153. data/lib/active_ldap/ldap_error.rb +74 -0
  154. data/lib/active_ldap/object_class.rb +93 -0
  155. data/lib/active_ldap/operations.rb +419 -0
  156. data/lib/active_ldap/populate.rb +44 -0
  157. data/lib/active_ldap/schema.rb +427 -0
  158. data/lib/active_ldap/timeout.rb +75 -0
  159. data/lib/active_ldap/timeout_stub.rb +17 -0
  160. data/lib/active_ldap/user_password.rb +93 -0
  161. data/lib/active_ldap/validations.rb +112 -0
  162. data/po/en/active-ldap.po +3011 -0
  163. data/po/ja/active-ldap.po +3044 -0
  164. data/rails/plugin/active_ldap/README +54 -0
  165. data/rails/plugin/active_ldap/generators/scaffold_al/scaffold_al_generator.rb +7 -0
  166. data/rails/plugin/active_ldap/generators/scaffold_al/templates/ldap.yml +21 -0
  167. data/rails/plugin/active_ldap/init.rb +19 -0
  168. data/test/al-test-utils.rb +362 -0
  169. data/test/command.rb +62 -0
  170. data/test/config.yaml.sample +6 -0
  171. data/test/run-test.rb +31 -0
  172. data/test/test-unit-ext.rb +4 -0
  173. data/test/test-unit-ext/always-show-result.rb +28 -0
  174. data/test/test-unit-ext/backtrace-filter.rb +17 -0
  175. data/test/test-unit-ext/long-display-for-emacs.rb +25 -0
  176. data/test/test-unit-ext/priority.rb +163 -0
  177. metadata +211 -4
@@ -0,0 +1,233 @@
1
+ require 'active_ldap/adapter/base'
2
+
3
+ module ActiveLdap
4
+ module Adapter
5
+ class Base
6
+ class << self
7
+ def ldap_connection(options)
8
+ unless defined?(::LDAP)
9
+ require 'active_ldap/adapter/ldap_ext'
10
+ end
11
+ Ldap.new(options)
12
+ end
13
+ end
14
+ end
15
+
16
+ class Ldap < Base
17
+ module Method
18
+ class SSL
19
+ def connect(host, port)
20
+ LDAP::SSLConn.new(host, port, false)
21
+ end
22
+ end
23
+
24
+ class TLS
25
+ def connect(host, port)
26
+ LDAP::SSLConn.new(host, port, true)
27
+ end
28
+ end
29
+
30
+ class Plain
31
+ def connect(host, port)
32
+ LDAP::Conn.new(host, port)
33
+ end
34
+ end
35
+ end
36
+
37
+ def connect(options={})
38
+ super do |host, port, method|
39
+ method.connect(host, port)
40
+ end
41
+ end
42
+
43
+ def unbind(options={})
44
+ return unless bound?
45
+ operation(options) do
46
+ execute(:unbind)
47
+ end
48
+ end
49
+
50
+ def bind(options={})
51
+ super do
52
+ @connection.error_message
53
+ end
54
+ end
55
+
56
+ def bind_as_anonymous(options={})
57
+ super do
58
+ execute(:bind)
59
+ true
60
+ end
61
+ end
62
+
63
+ def bound?
64
+ connecting? and @connection.bound?
65
+ end
66
+
67
+ def search(options={}, &block)
68
+ super(options) do |base, scope, filter, attrs, limit, callback|
69
+ begin
70
+ i = 0
71
+ execute(:search, base, scope, filter, attrs) do |entry|
72
+ i += 1
73
+ attributes = {}
74
+ entry.attrs.each do |attr|
75
+ attributes[attr] = entry.vals(attr)
76
+ end
77
+ callback.call([entry.dn, attributes], block)
78
+ break if limit and limit <= i
79
+ end
80
+ rescue RuntimeError
81
+ if $!.message == "no result returned by search"
82
+ @logger.debug do
83
+ args = [filter, attrs.inspect]
84
+ _("No matches: filter: %s: attributes: %s") % args
85
+ end
86
+ else
87
+ raise
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ def to_ldif(dn, attributes)
94
+ ldif = LDAP::LDIF.to_ldif("dn", [dn.dup])
95
+ attributes.sort_by do |key, value|
96
+ key
97
+ end.each do |key, values|
98
+ ldif << LDAP::LDIF.to_ldif(key, values)
99
+ end
100
+ ldif
101
+ end
102
+
103
+ def load(ldifs, options={})
104
+ super do |ldif|
105
+ LDAP::LDIF.parse_entry(ldif).send(@connection)
106
+ end
107
+ end
108
+
109
+ def delete(targets, options={})
110
+ super do |target|
111
+ execute(:delete, target)
112
+ end
113
+ end
114
+
115
+ def add(dn, entries, options={})
116
+ super do |dn, entries|
117
+ execute(:add, dn, parse_entries(entries))
118
+ end
119
+ end
120
+
121
+ def modify(dn, entries, options={})
122
+ super do |dn, entries|
123
+ execute(:modify, dn, parse_entries(entries))
124
+ end
125
+ end
126
+
127
+ private
128
+ def prepare_connection(options={})
129
+ operation(options) do
130
+ @connection.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
131
+ end
132
+ end
133
+
134
+ def root_dse(attributes, options={})
135
+ sec = options[:sec] || 0
136
+ usec = options[:usec] || 0
137
+ @connection.root_dse(attributes, sec, usec)
138
+ end
139
+
140
+ def execute(method, *args, &block)
141
+ begin
142
+ @connection.send(method, *args, &block)
143
+ rescue LDAP::ResultError
144
+ @connection.assert_error_code
145
+ raise $!.message
146
+ end
147
+ end
148
+
149
+ def with_timeout(try_reconnect=true, options={}, &block)
150
+ begin
151
+ super
152
+ rescue LDAP::ServerDown => e
153
+ @logger.error {_("LDAP server is down: %s") % e.message}
154
+ retry if try_reconnect and reconnect(options)
155
+ raise ConnectionError.new(e.message)
156
+ end
157
+ end
158
+
159
+ def ensure_method(method)
160
+ Method.constants.each do |name|
161
+ if method.to_s.downcase == name.downcase
162
+ return Method.const_get(name).new
163
+ end
164
+ end
165
+
166
+ available_methods = Method.constants.collect do |name|
167
+ name.downcase.to_sym.inspect
168
+ end.join(", ")
169
+ format = _("%s is not one of the available connect methods: %s")
170
+ raise ConfigurationError, format % [method.inspect, available_methods]
171
+ end
172
+
173
+ def ensure_scope(scope)
174
+ scope_map = {
175
+ :base => LDAP::LDAP_SCOPE_BASE,
176
+ :sub => LDAP::LDAP_SCOPE_SUBTREE,
177
+ :one => LDAP::LDAP_SCOPE_ONELEVEL,
178
+ }
179
+ value = scope_map[scope || :sub]
180
+ if value.nil?
181
+ available_scopes = scope_map.keys.inspect
182
+ format = _("%s is not one of the available LDAP scope: %s")
183
+ raise ArgumentError, format % [scope.inspect, available_scopes]
184
+ end
185
+ value
186
+ end
187
+
188
+ def sasl_bind(bind_dn, options={})
189
+ super do |bind_dn, mechanism, quiet|
190
+ begin
191
+ sasl_quiet = @connection.sasl_quiet
192
+ @connection.sasl_quiet = quiet unless quiet.nil?
193
+ args = [bind_dn, mechanism]
194
+ if need_credential_sasl_mechanism?(mechanism)
195
+ args << password(bind_dn, options)
196
+ end
197
+ execute(:sasl_bind, *args)
198
+ ensure
199
+ @connection.sasl_quiet = sasl_quiet
200
+ end
201
+ end
202
+ end
203
+
204
+ def simple_bind(bind_dn, options={})
205
+ super do |bind_dn, passwd|
206
+ execute(:bind, bind_dn, passwd)
207
+ end
208
+ end
209
+
210
+ def parse_entries(entries)
211
+ result = []
212
+ entries.each do |type, key, attributes|
213
+ mod_type = ensure_mod_type(type)
214
+ binary = schema.attribute(key).binary?
215
+ mod_type |= LDAP::LDAP_MOD_BVALUES if binary
216
+ attributes.each do |name, values|
217
+ result << LDAP.mod(mod_type, name, values)
218
+ end
219
+ end
220
+ result
221
+ end
222
+
223
+ def ensure_mod_type(type)
224
+ case type
225
+ when :replace, :add
226
+ LDAP.const_get("LDAP_MOD_#{type.to_s.upcase}")
227
+ else
228
+ raise ArgumentError, _("unknown type: %s") % type
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,69 @@
1
+ require_library_or_gem 'ldap'
2
+ require 'ldap/ldif'
3
+ require 'ldap/schema'
4
+
5
+ module LDAP
6
+ class Mod
7
+ unless instance_method(:to_s).arity.zero?
8
+ alias_method :original_to_s, :to_s
9
+ def to_s
10
+ inspect
11
+ end
12
+ end
13
+
14
+ alias_method :_initialize, :initialize
15
+ def initialize(op, type, vals)
16
+ if (LDAP::VERSION.split(/\./).collect {|x| x.to_i} <=> [0, 9, 7]) <= 0
17
+ @op, @type, @vals = op, type, vals # to protect from GC
18
+ end
19
+ _initialize(op, type, vals)
20
+ end
21
+ end
22
+
23
+ IMPLEMENT_SPECIFIC_ERRORS = {}
24
+ {
25
+ 0x51 => "SERVER_DOWN",
26
+ 0x52 => "LOCAL_ERROR",
27
+ 0x53 => "ENCODING_ERROR",
28
+ 0x54 => "DECODING_ERROR",
29
+ 0x55 => "TIMEOUT",
30
+ 0x56 => "AUTH_UNKNOWN",
31
+ 0x57 => "FILTER_ERROR",
32
+ 0x58 => "USER_CANCELLED",
33
+ 0x59 => "PARAM_ERROR",
34
+ 0x5a => "NO_MEMORY",
35
+
36
+ 0x5b => "CONNECT_ERROR",
37
+ 0x5c => "NOT_SUPPORTED",
38
+ 0x5d => "CONTROL_NOT_FOUND",
39
+ 0x5e => "NO_RESULTS_RETURNED",
40
+ 0x5f => "MORE_RESULTS_TO_RETURN",
41
+ 0x60 => "CLIENT_LOOP",
42
+ 0x61 => "REFERRAL_LIMIT_EXCEEDED",
43
+ }.each do |code, name|
44
+ IMPLEMENT_SPECIFIC_ERRORS[code] =
45
+ ActiveLdap::LdapError.define(code, name, self)
46
+ end
47
+
48
+ class Conn
49
+ def failed?
50
+ not err.zero?
51
+ end
52
+
53
+ def error_message
54
+ if failed?
55
+ LDAP.err2string(err)
56
+ else
57
+ nil
58
+ end
59
+ end
60
+
61
+ def assert_error_code
62
+ return unless failed?
63
+ klass = ActiveLdap::LdapError::ERRORS[err]
64
+ klass ||= IMPLEMENT_SPECIFIC_ERRORS[err]
65
+ klass ||= ActiveLdap::LdapError
66
+ raise klass, LDAP.err2string(err)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,290 @@
1
+ require 'digest/md5'
2
+
3
+ require 'active_ldap/adapter/base'
4
+
5
+ module ActiveLdap
6
+ module Adapter
7
+ class Base
8
+ class << self
9
+ def net_ldap_connection(options)
10
+ unless defined?(::Net::LDAP)
11
+ require 'active_ldap/adapter/net_ldap_ext'
12
+ end
13
+ NetLdap.new(options)
14
+ end
15
+ end
16
+ end
17
+
18
+ class NetLdap < Base
19
+ METHOD = {
20
+ :ssl => :simple_tls,
21
+ :tls => :start_tls,
22
+ :plain => nil,
23
+ }
24
+
25
+ def connect(options={})
26
+ @bound = false
27
+ super do |host, port, method|
28
+ config = {
29
+ :host => host,
30
+ :port => port,
31
+ }
32
+ config[:encryption] = {:method => method} if method
33
+ Net::LDAP::Connection.new(config)
34
+ end
35
+ end
36
+
37
+ def unbind(options={})
38
+ @bound = false
39
+ end
40
+
41
+ def bind(options={})
42
+ @bound = false
43
+ begin
44
+ super
45
+ rescue Net::LDAP::LdapError
46
+ raise AuthenticationError, $!.message
47
+ end
48
+ end
49
+
50
+ def bind_as_anonymous(options={})
51
+ super do
52
+ @bound = false
53
+ execute(:bind, :method => :anonymous)
54
+ @bound = true
55
+ end
56
+ end
57
+
58
+ def bound?
59
+ connecting? and @bound
60
+ end
61
+
62
+ def search(options={}, &block)
63
+ super(options) do |base, scope, filter, attrs, limit, callback|
64
+ args = {
65
+ :base => base,
66
+ :scope => scope,
67
+ :filter => filter,
68
+ :attributes => attrs,
69
+ :size => limit,
70
+ }
71
+ execute(:search, args) do |entry|
72
+ attributes = {}
73
+ entry.original_attribute_names.each do |name|
74
+ attributes[name] = entry[name]
75
+ end
76
+ callback.call([entry.dn, attributes], block)
77
+ end
78
+ end
79
+ end
80
+
81
+ def to_ldif(dn, attributes)
82
+ entry = Net::LDAP::Entry.new(dn.dup)
83
+ attributes.each do |key, values|
84
+ entry[key] = values.flatten
85
+ end
86
+ entry.to_ldif
87
+ end
88
+
89
+ def load(ldifs, options={})
90
+ super do |ldif|
91
+ entry = Net::LDAP::Entry.from_single_ldif_string(ldif)
92
+ attributes = {}
93
+ entry.each do |name, values|
94
+ attributes[name] = values
95
+ end
96
+ attributes.delete(:dn)
97
+ execute(:add,
98
+ :dn => entry.dn,
99
+ :attributes => attributes)
100
+ end
101
+ end
102
+
103
+ def delete(targets, options={})
104
+ super do |target|
105
+ execute(:delete, :dn => target)
106
+ end
107
+ end
108
+
109
+ def add(dn, entries, options={})
110
+ super do |dn, entries|
111
+ attributes = {}
112
+ entries.each do |type, key, attrs|
113
+ attrs.each do |name, values|
114
+ attributes[name] = values
115
+ end
116
+ end
117
+ execute(:add, :dn => dn, :attributes => attributes)
118
+ end
119
+ end
120
+
121
+ def modify(dn, entries, options={})
122
+ super do |dn, entries|
123
+ execute(:modify,
124
+ :dn => dn,
125
+ :operations => parse_entries(entries))
126
+ end
127
+ end
128
+
129
+ private
130
+ def execute(method, *args, &block)
131
+ result = @connection.send(method, *args, &block)
132
+ message = nil
133
+ if result.is_a?(Hash)
134
+ message = result[:errorMessage]
135
+ result = result[:resultCode]
136
+ end
137
+ unless result.zero?
138
+ klass = LdapError::ERRORS[result]
139
+ klass ||= LdapError
140
+ raise klass,
141
+ [Net::LDAP.result2string(result), message].compact.join(": ")
142
+ end
143
+ end
144
+
145
+ def root_dse(attrs, options={})
146
+ search(:base => "",
147
+ :scope => :base,
148
+ :attributes => attrs).collect do |dn, attributes|
149
+ attributes
150
+ end
151
+ end
152
+
153
+ def ensure_method(method)
154
+ method ||= "plain"
155
+ normalized_method = method.to_s.downcase.to_sym
156
+ return METHOD[normalized_method] if METHOD.has_key?(normalized_method)
157
+
158
+ available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ")
159
+ format = _("%s is not one of the available connect methods: %s")
160
+ raise ConfigurationError, format % [method.inspect, available_methods]
161
+ end
162
+
163
+ def ensure_scope(scope)
164
+ scope_map = {
165
+ :base => Net::LDAP::SearchScope_BaseObject,
166
+ :sub => Net::LDAP::SearchScope_WholeSubtree,
167
+ :one => Net::LDAP::SearchScope_SingleLevel,
168
+ }
169
+ value = scope_map[scope || :sub]
170
+ if value.nil?
171
+ available_scopes = scope_map.keys.inspect
172
+ format = _("%s is not one of the available LDAP scope: %s")
173
+ raise ArgumentError, format % [scope.inspect, available_scopes]
174
+ end
175
+ value
176
+ end
177
+
178
+ def sasl_bind(bind_dn, options={})
179
+ super do |bind_dn, mechanism, quiet|
180
+ normalized_mechanism = mechanism.downcase.gsub(/-/, '_')
181
+ sasl_bind_setup = "sasl_bind_setup_#{normalized_mechanism}"
182
+ next unless respond_to?(sasl_bind_setup, true)
183
+ initial_credential, challenge_response =
184
+ send(sasl_bind_setup, bind_dn, options)
185
+ args = {
186
+ :method => :sasl,
187
+ :initial_credential => initial_credential,
188
+ :mechanism => mechanism,
189
+ :challenge_response => challenge_response,
190
+ }
191
+ @bound = false
192
+ execute(:bind, args)
193
+ @bound = true
194
+ end
195
+ end
196
+
197
+ def sasl_bind_setup_digest_md5(bind_dn, options)
198
+ initial_credential = ""
199
+ nonce_count = 1
200
+ challenge_response = Proc.new do |cred|
201
+ params = parse_sasl_digest_md5_credential(cred)
202
+ qops = params["qop"].split(/,/)
203
+ unless qops.include?("auth")
204
+ raise ActiveLdap::AuthenticationError,
205
+ _("unsupported qops: %s") % qops.inspect
206
+ end
207
+ qop = "auth"
208
+ server = @connection.instance_variable_get("@conn").addr[2]
209
+ realm = params['realm']
210
+ uri = "ldap/#{server}"
211
+ nc = "%08x" % nonce_count
212
+ nonce = params["nonce"]
213
+ cnonce = generate_client_nonce
214
+ requests = {
215
+ :username => bind_dn.inspect,
216
+ :realm => realm.inspect,
217
+ :nonce => nonce.inspect,
218
+ :cnonce => cnonce.inspect,
219
+ :nc => nc,
220
+ :qop => qop,
221
+ :maxbuf => "65536",
222
+ "digest-uri" => uri.inspect,
223
+ }
224
+ a1 = "#{bind_dn}:#{realm}:#{password(cred, options)}"
225
+ a1 = "#{Digest::MD5.digest(a1)}:#{nonce}:#{cnonce}"
226
+ ha1 = Digest::MD5.hexdigest(a1)
227
+ a2 = "AUTHENTICATE:#{uri}"
228
+ ha2 = Digest::MD5.hexdigest(a2)
229
+ response = "#{ha1}:#{nonce}:#{nc}:#{cnonce}:#{qop}:#{ha2}"
230
+ requests["response"] = Digest::MD5.hexdigest(response)
231
+ nonce_count += 1
232
+ requests.collect do |key, value|
233
+ "#{key}=#{value}"
234
+ end.join(",")
235
+ end
236
+ [initial_credential, challenge_response]
237
+ end
238
+
239
+ def parse_sasl_digest_md5_credential(cred)
240
+ params = {}
241
+ cred.scan(/(\w+)=(\"?)(.+?)\2(?:,|$)/) do |name, sep, value|
242
+ params[name] = value
243
+ end
244
+ params
245
+ end
246
+
247
+ CHARS = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
248
+ def generate_client_nonce(size=32)
249
+ nonce = ""
250
+ size.times do |i|
251
+ nonce << CHARS[rand(CHARS.size)]
252
+ end
253
+ nonce
254
+ end
255
+
256
+ def simple_bind(bind_dn, options={})
257
+ super do |bind_dn, passwd|
258
+ args = {
259
+ :method => :simple,
260
+ :username => bind_dn,
261
+ :password => passwd,
262
+ }
263
+ @bound = false
264
+ execute(:bind, args)
265
+ @bound = true
266
+ end
267
+ end
268
+
269
+ def parse_entries(entries)
270
+ result = []
271
+ entries.each do |type, key, attributes|
272
+ mod_type = ensure_mod_type(type)
273
+ attributes.each do |name, values|
274
+ result << [mod_type, name, values]
275
+ end
276
+ end
277
+ result
278
+ end
279
+
280
+ def ensure_mod_type(type)
281
+ case type
282
+ when :replace, :add
283
+ type
284
+ else
285
+ raise ArgumentError, _("unknown type: %s") % type
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end