ruby-activeldap 0.8.3 → 0.8.3.1

Sign up to get free protection for your applications and to get access to all the features.
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