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,19 @@
1
+ require 'active_record/callbacks'
2
+
3
+ module ActiveLdap
4
+ module Callbacks
5
+ def self.append_features(base)
6
+ super
7
+
8
+ base.class_eval do
9
+ include ActiveRecord::Callbacks
10
+
11
+ def callback(method)
12
+ super
13
+ rescue ActiveRecord::ActiveRecordError
14
+ raise Error, $!.message
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,48 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+
4
+ module ActiveLdap
5
+ module Command
6
+ include GetTextSupport
7
+
8
+ module_function
9
+ def parse_options(argv=nil, version=nil)
10
+ argv ||= ARGV.dup
11
+ options = OpenStruct.new
12
+ opts = OptionParser.new do |opts|
13
+ yield(opts, options)
14
+
15
+ opts.separator ""
16
+ opts.separator _("Common options:")
17
+
18
+ opts.on_tail("--config=CONFIG",
19
+ _("Specify configuration file written as YAML")) do |file|
20
+ require 'yaml'
21
+ config = YAML.load(File.read(file)).symbolize_keys
22
+ Configuration::DEFAULT_CONFIG.update(config)
23
+ end
24
+
25
+ opts.on_tail("-h", "--help", _("Show this message")) do
26
+ puts opts
27
+ exit
28
+ end
29
+
30
+ opts.on_tail("--version", _("Show version")) do
31
+ puts(version || VERSION)
32
+ exit
33
+ end
34
+ end
35
+ opts.parse!(argv)
36
+ [argv, opts, options]
37
+ end
38
+
39
+ def read_password(prompt, input=$stdin, output=$stdout)
40
+ output.print prompt
41
+ system "/bin/stty -echo" if input.tty?
42
+ input.gets.chomp
43
+ ensure
44
+ system "/bin/stty echo" if input.tty?
45
+ output.puts
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,114 @@
1
+
2
+ module ActiveLdap
3
+ # Configuration
4
+ #
5
+ # Configuration provides the default settings required for
6
+ # ActiveLdap to work with your LDAP server. All of these
7
+ # settings can be passed in at initialization time.
8
+ module Configuration
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ end
12
+
13
+ DEFAULT_CONFIG = {}
14
+ DEFAULT_CONFIG[:host] = '127.0.0.1'
15
+ DEFAULT_CONFIG[:port] = 389
16
+ DEFAULT_CONFIG[:method] = :plain # :ssl, :tls, :plain allowed
17
+
18
+ DEFAULT_CONFIG[:bind_dn] = nil
19
+ DEFAULT_CONFIG[:password_block] = nil
20
+ DEFAULT_CONFIG[:password] = nil
21
+ DEFAULT_CONFIG[:store_password] = true
22
+ DEFAULT_CONFIG[:allow_anonymous] = true
23
+ DEFAULT_CONFIG[:sasl_quiet] = false
24
+ DEFAULT_CONFIG[:try_sasl] = false
25
+ # See http://www.iana.org/assignments/sasl-mechanisms
26
+ DEFAULT_CONFIG[:sasl_mechanisms] = ["GSSAPI", "DIGEST-MD5",
27
+ "CRAM-MD5", "EXTERNAL"]
28
+
29
+ DEFAULT_CONFIG[:retry_limit] = 3
30
+ DEFAULT_CONFIG[:retry_wait] = 3
31
+ DEFAULT_CONFIG[:timeout] = 0 # in seconds; 0 <= Never timeout
32
+ # Whether or not to retry on timeouts
33
+ DEFAULT_CONFIG[:retry_on_timeout] = true
34
+
35
+ DEFAULT_CONFIG[:logger] = nil
36
+
37
+ module ClassMethods
38
+ @@defined_configurations = {}
39
+
40
+ def default_configuration
41
+ DEFAULT_CONFIG.dup
42
+ end
43
+
44
+ def ensure_configuration(config=nil)
45
+ if config.nil?
46
+ if defined?(LDAP_ENV)
47
+ config = LDAP_ENV
48
+ elsif defined?(RAILS_ENV)
49
+ config = RAILS_ENV
50
+ else
51
+ config = {}
52
+ end
53
+ end
54
+
55
+ if config.is_a?(Symbol) or config.is_a?(String)
56
+ _config = configurations[config.to_s]
57
+ unless _config
58
+ raise ConnectionError,
59
+ _("%s connection is not configured") % config
60
+ end
61
+ config = _config
62
+ end
63
+
64
+ config
65
+ end
66
+
67
+ def configuration(key=nil)
68
+ @@defined_configurations[key || active_connection_name]
69
+ end
70
+
71
+ def define_configuration(key, config)
72
+ @@defined_configurations[key] = config
73
+ end
74
+
75
+ def defined_configurations
76
+ @@defined_configurations
77
+ end
78
+
79
+ def remove_configuration_by_configuration(config)
80
+ @@defined_configurations.delete_if {|key, value| value == config}
81
+ end
82
+
83
+ CONNECTION_CONFIGURATION_KEYS = [:base, :adapter]
84
+ def remove_connection_related_configuration(config)
85
+ config.reject do |key, value|
86
+ CONNECTION_CONFIGURATION_KEYS.include?(key)
87
+ end
88
+ end
89
+
90
+ def merge_configuration(config, target=self)
91
+ configuration = default_configuration
92
+ config.symbolize_keys.each do |key, value|
93
+ case key
94
+ when :base
95
+ # Scrub before inserting
96
+ target.base = value.gsub(/['}{#]/, '')
97
+ when :scope, :ldap_scope
98
+ if key == :ldap_scope
99
+ logger.warning do
100
+ _(":ldap_scope configuration option is deprecated. " \
101
+ "Use :scope instead.")
102
+ end
103
+ end
104
+ target.scope = value
105
+ configuration[:scope] = value
106
+ else
107
+ configuration[key] = value
108
+ end
109
+ end
110
+ configuration
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,234 @@
1
+ module ActiveLdap
2
+ module Connection
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ @@active_connections = {}
9
+ @@allow_concurrency = false
10
+
11
+ def thread_safe_active_connections
12
+ @@active_connections[Thread.current.object_id] ||= {}
13
+ end
14
+
15
+ def single_threaded_active_connections
16
+ @@active_connections
17
+ end
18
+
19
+ if @@allow_concurrency
20
+ alias_method :active_connections, :thread_safe_active_connections
21
+ else
22
+ alias_method :active_connections, :single_threaded_active_connections
23
+ end
24
+
25
+ def allow_concurrency=(threaded) #:nodoc:
26
+ logger.debug {"allow_concurrency=#{threaded}"} if logger
27
+ return if @@allow_concurrency == threaded
28
+ clear_all_cached_connections!
29
+ @@allow_concurrency = threaded
30
+ method_prefix = threaded ? "thread_safe" : "single_threaded"
31
+ sing = (class << self; self; end)
32
+ [:active_connections].each do |method|
33
+ sing.send(:alias_method, method, "#{method_prefix}_#{method}")
34
+ end
35
+ end
36
+
37
+ def active_connection_name
38
+ @active_connection_name ||= determine_active_connection_name
39
+ end
40
+
41
+ def remove_active_connections!
42
+ active_connections.keys.each do |key|
43
+ remove_connection(key)
44
+ end
45
+ end
46
+
47
+ def clear_active_connections!
48
+ connections = active_connections
49
+ connections.each do |key, connection|
50
+ connection.disconnect!
51
+ end
52
+ connections.clear
53
+ end
54
+
55
+ def clear_active_connection_name
56
+ @active_connection_name = nil
57
+ ObjectSpace.each_object(Class) do |klass|
58
+ if klass < self and !klass.name.empty?
59
+ klass.instance_variable_set("@active_connection_name", nil)
60
+ end
61
+ end
62
+ end
63
+
64
+ def connection
65
+ conn = nil
66
+ @active_connection_name ||= nil
67
+ if @active_connection_name
68
+ conn = active_connections[@active_connection_name]
69
+ end
70
+ unless conn
71
+ conn = retrieve_connection
72
+ active_connections[@active_connection_name] = conn
73
+ end
74
+ conn
75
+ end
76
+
77
+ def connection=(adapter)
78
+ if adapter.is_a?(Adapter::Base)
79
+ active_connections[active_connection_name] = adapter
80
+ elsif adapter.is_a?(Hash)
81
+ config = adapter
82
+ self.connection = instantiate_adapter(config)
83
+ elsif adapter.nil?
84
+ raise ConnectionNotEstablished
85
+ else
86
+ establish_connection(adapter)
87
+ end
88
+ end
89
+
90
+ def instantiate_adapter(config)
91
+ adapter = (config[:adapter] || "ldap")
92
+ normalized_adapter = adapter.downcase.gsub(/-/, "_")
93
+ adapter_method = "#{normalized_adapter}_connection"
94
+ unless Adapter::Base.respond_to?(adapter_method)
95
+ raise AdapterNotFound.new(adapter)
96
+ end
97
+ if config.has_key?(:ldap_scope)
98
+ logger.warning do
99
+ _(":ldap_scope connection option is deprecated. Use :scope instead.")
100
+ end
101
+ config[:scope] ||= config.delete(:ldap_scope)
102
+ end
103
+ config = remove_connection_related_configuration(config)
104
+ Adapter::Base.send(adapter_method, config)
105
+ end
106
+
107
+ def connected?
108
+ active_connections[active_connection_name] ? true : false
109
+ end
110
+
111
+ def retrieve_connection
112
+ conn = nil
113
+ name = active_connection_name
114
+ raise ConnectionNotEstablished unless name
115
+ conn = active_connections[name]
116
+ if conn.nil?
117
+ config = configuration(name)
118
+ raise ConnectionNotEstablished unless config
119
+ self.connection = config
120
+ conn = active_connections[name]
121
+ end
122
+ raise ConnectionNotEstablished if conn.nil?
123
+ conn
124
+ end
125
+
126
+ def remove_connection(klass_or_key=self)
127
+ if klass_or_key.is_a?(Module)
128
+ key = active_connection_key(klass_or_key)
129
+ else
130
+ key = klass_or_key
131
+ end
132
+ config = configuration(key)
133
+ conn = active_connections[key]
134
+ remove_configuration_by_configuration(config)
135
+ active_connections.delete_if {|key, value| value == conn}
136
+ conn.disconnect! if conn
137
+ config
138
+ end
139
+
140
+ def establish_connection(config=nil)
141
+ config = ensure_configuration(config)
142
+ remove_connection
143
+
144
+ clear_active_connection_name
145
+ key = active_connection_key
146
+ @active_connection_name = key
147
+ define_configuration(key, merge_configuration(config))
148
+ end
149
+
150
+ # Return the schema object
151
+ def schema
152
+ connection.schema
153
+ end
154
+
155
+ private
156
+ def active_connection_key(k=self)
157
+ k.name.empty? ? k.object_id : k.name
158
+ end
159
+
160
+ def determine_active_connection_name
161
+ key = active_connection_key
162
+ if active_connections[key] or configuration(key)
163
+ key
164
+ elsif self == ActiveLdap::Base
165
+ nil
166
+ else
167
+ superclass.active_connection_name
168
+ end
169
+ end
170
+
171
+ def clear_all_cached_connections!
172
+ if @@allow_concurrency
173
+ @@active_connections.each_value do |connection_hash_for_thread|
174
+ connection_hash_for_thread.each_value {|conn| conn.disconnect!}
175
+ connection_hash_for_thread.clear
176
+ end
177
+ else
178
+ @@active_connections.each_value {|conn| conn.disconnect!}
179
+ end
180
+ @@active_connections.clear
181
+ end
182
+ end
183
+
184
+ def establish_connection(config=nil)
185
+ config = self.class.ensure_configuration(config)
186
+ config = self.class.configuration.merge(config)
187
+ config = self.class.merge_configuration(config, self)
188
+
189
+ remove_connection
190
+ self.class.define_configuration(dn, config)
191
+ end
192
+
193
+ def remove_connection
194
+ self.class.remove_connection(dn)
195
+ end
196
+
197
+ def connection
198
+ conn = @connection
199
+ conn ||= self.class.active_connections[dn] || retrieve_connection if id
200
+ conn || self.class.connection
201
+ end
202
+
203
+ def connected?
204
+ connection != self.class.connection
205
+ end
206
+
207
+ def connection=(adapter)
208
+ if adapter.nil? or adapter.is_a?(Adapter::Base)
209
+ @connection = adapter
210
+ elsif adapter.is_a?(Hash)
211
+ config = adapter
212
+ @connection = self.class.instantiate_adapter(config)
213
+ else
214
+ establish_connection(adapter)
215
+ end
216
+ end
217
+
218
+ def retrieve_connection
219
+ conn = self.class.active_connections[dn]
220
+ return conn if conn
221
+
222
+ config = self.class.configuration(dn)
223
+ return nil unless config
224
+
225
+ conn = self.class.instantiate_adapter(config)
226
+ @connection = self.class.active_connections[dn] = conn
227
+ conn
228
+ end
229
+
230
+ def schema
231
+ connection.schema
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,250 @@
1
+ require 'strscan'
2
+
3
+ module ActiveLdap
4
+ class DistinguishedName
5
+ include GetTextSupport
6
+
7
+ class Parser
8
+ include GetTextSupport
9
+
10
+ attr_reader :dn
11
+ def initialize(source)
12
+ @dn = nil
13
+ @source = source
14
+ end
15
+
16
+ def parse
17
+ return @dn if @dn
18
+
19
+ rdns = []
20
+ scanner = StringScanner.new(@source)
21
+
22
+ scanner.scan(/\s*/)
23
+ raise rdn_is_missing if scanner.scan(/\s*\+\s*/)
24
+ raise name_component_is_missing if scanner.scan(/\s*,\s*/)
25
+
26
+ rdn = {}
27
+ until scanner.eos?
28
+ type = scan_attribute_type(scanner)
29
+ skip_attribute_type_and_value_separator(scanner)
30
+ value = scan_attribute_value(scanner)
31
+ rdn[type] = value
32
+ if scanner.scan(/\s*\+\s*/)
33
+ raise rdn_is_missing if scanner.eos?
34
+ elsif scanner.scan(/\s*\,\s*/)
35
+ rdns << rdn
36
+ rdn = {}
37
+ raise name_component_is_missing if scanner.eos?
38
+ else
39
+ scanner.scan(/\s*/)
40
+ rdns << rdn if scanner.eos?
41
+ end
42
+ end
43
+
44
+ @dn = DN.new(*rdns)
45
+ @dn
46
+ end
47
+
48
+ private
49
+ ATTRIBUTE_TYPE_RE = /\s*([a-zA-Z][a-zA-Z\d\-]*|\d+(?:\.\d+)*)\s*/
50
+ def scan_attribute_type(scanner)
51
+ raise attribute_type_is_missing unless scanner.scan(ATTRIBUTE_TYPE_RE)
52
+ scanner[1]
53
+ end
54
+
55
+ def skip_attribute_type_and_value_separator(scanner)
56
+ raise attribute_value_is_missing unless scanner.scan(/\s*=\s*/)
57
+ end
58
+
59
+ HEX_PAIR = "(?:[\\da-fA-F]{2})"
60
+ STRING_CHARS_RE = /[^,=\+<>\#;\\\"]*/ #
61
+ PAIR_RE = /\\([,=\+<>\#;]|\\|\"|(#{HEX_PAIR}))/ #
62
+ HEX_STRING_RE = /\#(#{HEX_PAIR}+)/ #
63
+ def scan_attribute_value(scanner)
64
+ if scanner.scan(HEX_STRING_RE)
65
+ value = scanner[1].scan(/../).collect do |hex_pair|
66
+ hex_pair.hex
67
+ end.pack("C*")
68
+ elsif scanner.scan(/\"/)
69
+ value = scan_quoted_attribute_value(scanner)
70
+ else
71
+ value = scan_not_quoted_attribute_value(scanner)
72
+ end
73
+ raise attribute_value_is_missing if value.blank?
74
+
75
+ value
76
+ end
77
+
78
+ def scan_quoted_attribute_value(scanner)
79
+ result = ""
80
+ until scanner.scan(/\"/)
81
+ scanner.scan(/([^\\\"]*)/)
82
+ quoted_strings = scanner[1]
83
+ pairs = collect_pairs(scanner)
84
+
85
+ if scanner.eos? or (quoted_strings.empty? and pairs.empty?)
86
+ raise found_unmatched_quotation
87
+ end
88
+
89
+ result << quoted_strings
90
+ result << pairs
91
+ end
92
+ result
93
+ end
94
+
95
+ def scan_not_quoted_attribute_value(scanner)
96
+ result = ""
97
+ until scanner.eos?
98
+ prev_size = result.size
99
+ pairs = collect_pairs(scanner)
100
+ strings = scanner.scan(STRING_CHARS_RE)
101
+ result << pairs if !pairs.nil? and !pairs.empty?
102
+ unless strings.nil?
103
+ if scanner.peek(1) == ","
104
+ result << strings.rstrip
105
+ else
106
+ result << strings
107
+ end
108
+ end
109
+ break if prev_size == result.size
110
+ end
111
+ result
112
+ end
113
+
114
+ def collect_pairs(scanner)
115
+ result = ""
116
+ while scanner.scan(PAIR_RE)
117
+ if scanner[2]
118
+ result << [scanner[2].hex].pack("C*")
119
+ else
120
+ result << scanner[1]
121
+ end
122
+ end
123
+ result
124
+ end
125
+
126
+ def invalid_dn(reason)
127
+ DistinguishedNameInvalid.new(@source, reason)
128
+ end
129
+
130
+ def name_component_is_missing
131
+ invalid_dn(_("name component is missing"))
132
+ end
133
+
134
+ def rdn_is_missing
135
+ invalid_dn(_("relative distinguished name (RDN) is missing"))
136
+ end
137
+
138
+ def attribute_type_is_missing
139
+ invalid_dn(_("attribute type is missing"))
140
+ end
141
+
142
+ def attribute_value_is_missing
143
+ invalid_dn(_("attribute value is missing"))
144
+ end
145
+
146
+ def found_unmatched_quotation
147
+ invalid_dn(_("found unmatched quotation"))
148
+ end
149
+ end
150
+
151
+ class << self
152
+ def parse(source)
153
+ Parser.new(source).parse
154
+ end
155
+ end
156
+
157
+ attr_reader :rdns
158
+ def initialize(*rdns)
159
+ @rdns = rdns.collect do |rdn|
160
+ rdn = {rdn[0] => rdn[1]} if rdn.is_a?(Array) and rdn.size == 2
161
+ rdn
162
+ end
163
+ end
164
+
165
+ def -(other)
166
+ rdns = @rdns.dup
167
+ normalized_rdns = normalize(@rdns)
168
+ normalize(other.rdns).reverse_each do |rdn|
169
+ if rdn == normalized_rdns.pop
170
+ rdns.pop
171
+ else
172
+ raise ArgumentError, _("%s isn't sub DN of %s") % [other, self]
173
+ end
174
+ end
175
+ self.class.new(*rdns)
176
+ end
177
+
178
+ def <<(rdn)
179
+ @rdns << rdn
180
+ end
181
+
182
+ def unshift(rdn)
183
+ @rdns.unshift(rdn)
184
+ end
185
+
186
+ def <=>(other)
187
+ normalize_for_comparing(@rdns) <=>
188
+ normalize_for_comparing(other.rdns)
189
+ end
190
+
191
+ def ==(other)
192
+ other.is_a?(self.class) and
193
+ normalize(@rdns) == normalize(other.rdns)
194
+ end
195
+
196
+ def eql?(other)
197
+ other.is_a?(self.class) and
198
+ normalize(@rdns).to_s.eql?(normalize(other.rdns).to_s)
199
+ end
200
+
201
+ def hash
202
+ normalize(@rdns).to_s.hash
203
+ end
204
+
205
+ def inspect
206
+ super
207
+ end
208
+
209
+ def to_s
210
+ @rdns.collect do |rdn|
211
+ rdn.sort_by do |type, value|
212
+ type.upcase
213
+ end.collect do |type, value|
214
+ "#{type}=#{escape(value)}"
215
+ end.join("+")
216
+ end.join(",")
217
+ end
218
+
219
+ private
220
+ def normalize(rdns)
221
+ rdns.collect do |rdn|
222
+ normalized_rdn = {}
223
+ rdn.each do |key, value|
224
+ normalized_rdn[key.upcase] = value.upcase
225
+ end
226
+ normalized_rdn
227
+ end
228
+ end
229
+
230
+ def normalize_for_comparing(rdns)
231
+ normalize(rdns).collect do |rdn|
232
+ rdn.sort_by do |key, value|
233
+ key
234
+ end
235
+ end.collect do |key, value|
236
+ [key, value]
237
+ end
238
+ end
239
+
240
+ def escape(value)
241
+ if /(\A | \z)/.match(value)
242
+ '"' + value.gsub(/([\\\"])/, '\\\\\1') + '"'
243
+ else
244
+ value.gsub(/([,=\+<>#;\\\"])/, '\\\\\1')
245
+ end
246
+ end
247
+ end
248
+
249
+ DN = DistinguishedName
250
+ end