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,12 @@
1
+ module ActiveLdap
2
+ module Escape
3
+ module_function
4
+ def ldap_filter_escape(str)
5
+ str.to_s.gsub(/\*/, "**")
6
+ end
7
+
8
+ def ldap_filter_unescape(str)
9
+ str.to_s.gsub(/\*\*/, "*")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,142 @@
1
+ require 'active_ldap'
2
+ require 'gettext/parser/ruby'
3
+
4
+ module ActiveLdap
5
+ module GetText
6
+ class Parser
7
+ include GetText
8
+
9
+ def initialize(configuration=nil)
10
+ configuration = ensure_configuration(configuration)
11
+ configuration = default_configuration.merge(configuration)
12
+
13
+ configuration = extract_options(configuration)
14
+ ActiveLdap::Base.establish_connection(configuration)
15
+ end
16
+
17
+ def parse(file, targets=[])
18
+ targets = RubyParser.parse(file, targets) if RubyParser.target?(file)
19
+ extract(targets) do
20
+ load_constants(file).each do |name|
21
+ klass = name.constantize
22
+ next unless klass.is_a?(Class)
23
+ next unless klass < ActiveLdap::Base
24
+ register(klass.name.singularize.underscore.gsub(/_/, " "), file)
25
+ next unless @extract_schema
26
+ klass.classes.each do |object_class|
27
+ register_object_class(object_class, file)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ def target?(file)
34
+ @classes_re.match(File.read(file))
35
+ end
36
+
37
+ def extract_all_in_schema(targets=[])
38
+ extract(targets) do
39
+ ActiveLdap::Base.schema.object_classes.each do |object_class|
40
+ register_object_class(object_class, "-")
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+ def extract_options(configuration)
47
+ configuration = configuration.dup
48
+ classes = configuration.delete(:classes) || ["ActiveLdap::Base"]
49
+ @classes_re = /class.*#{Regexp.union(*classes)}/ #
50
+ @extract_schema = configuration.delete(:extract_schema)
51
+ configuration
52
+ end
53
+
54
+ def default_configuration
55
+ {
56
+ :host => "127.0.0.1",
57
+ :allow_anonymous => true,
58
+ :extract_schema => false,
59
+ }
60
+ end
61
+
62
+ def ensure_configuration(configuration)
63
+ configuration ||= ENV["RAILS_ENV"] || {}
64
+ if configuration.is_a?(String)
65
+ if File.exists?(configuration)
66
+ require 'erb'
67
+ require 'yaml'
68
+ configuration = YAML.load(ERB.new(File.read(configuration)).result)
69
+ else
70
+ ENV["RAILS_ENV"] = configuration
71
+ require 'config/environment'
72
+ configuration = ActiveLdap::Base.configurations[configuration]
73
+ end
74
+ end
75
+ if Object.const_defined?(:RAILS_ENV)
76
+ rails_configuration = ActiveLdap::Base.configurations[RAILS_ENV]
77
+ configuration = rails_configuration.merge(configuration)
78
+ end
79
+ configuration = configuration.symbolize_keys
80
+ end
81
+
82
+ def load_constants(file)
83
+ old_constants = Object.constants
84
+ begin
85
+ eval(File.read(file), TOPLEVEL_BINDING, file)
86
+ rescue
87
+ format = _("Ignored '%{file}'. Solve dependencies first.")
88
+ $stderr.puts(format % {:file => file})
89
+ $stderr.puts($!)
90
+ end
91
+ Object.constants - old_constants
92
+ end
93
+
94
+ def extract(targets)
95
+ @targets = {}
96
+ targets.each do |id, *file_infos|
97
+ @targets[id] = file_infos
98
+ end
99
+ yield
100
+ @targets.collect do |id, file_infos|
101
+ [id, *file_infos.uniq]
102
+ end.sort_by do |id,|
103
+ id
104
+ end
105
+ end
106
+
107
+ def register(id, file)
108
+ file_info = "#{file}:-"
109
+ @targets[id] ||= []
110
+ @targets[id] << file_info
111
+ end
112
+
113
+ def register_object_class(object_class, file)
114
+ [object_class.name, *object_class.aliases].each do |name|
115
+ register(ActiveLdap::Base.human_object_class_name_msgid(name), file)
116
+ end
117
+ if object_class.description
118
+ msgid =
119
+ ActiveLdap::Base.human_object_class_description_msgid(object_class)
120
+ register(msgid, file)
121
+ end
122
+ (object_class.must(false) + object_class.may(false)).each do |attribute|
123
+ register_attribute(attribute, file)
124
+ end
125
+ object_class.super_classes.each do |super_class|
126
+ register_object_class(super_class, file)
127
+ end
128
+ end
129
+
130
+ def register_attribute(attribute, file)
131
+ [attribute.name, *attribute.aliases].each do |name|
132
+ msgid = ActiveLdap::Base.human_attribute_name_msgid(name)
133
+ register(msgid, file) if msgid
134
+ end
135
+ if attribute.description
136
+ msgid = ActiveLdap::Base.human_attribute_description_msgid(attribute)
137
+ register(msgid, file)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,53 @@
1
+ module ActiveLdap
2
+ module GetTextFallback
3
+ class << self
4
+ def included(base)
5
+ base.extend(self)
6
+ end
7
+ end
8
+
9
+ module_function
10
+ def bindtextdomain(domain_name, *args)
11
+ end
12
+
13
+ def gettext(msg_id)
14
+ msg_id
15
+ end
16
+
17
+ def ngettext(arg1, arg2, arg3=nil)
18
+ if arg1.kind_of?(Array)
19
+ msg_id = arg1[0]
20
+ msg_id_plural = arg1[1]
21
+ n = arg2
22
+ else
23
+ msg_id = arg1
24
+ msg_id_plural = arg2
25
+ n = arg3
26
+ end
27
+ n == 1 ? msg_id : msg_id_plural
28
+ end
29
+
30
+ def N_(msg_id)
31
+ msg_id
32
+ end
33
+
34
+ def Nn_(msg_id, msg_id_plural)
35
+ [msg_id, msg_id_plural]
36
+ end
37
+
38
+ def sgettext(msg_id, div='|')
39
+ index = msg.rindex(div)
40
+ if index
41
+ msg[(index + 1)..-1]
42
+ else
43
+ msg
44
+ end
45
+ end
46
+
47
+ alias_method(:_, :gettext)
48
+ alias_method(:n_, :ngettext)
49
+ alias_method(:s_, :sgettext)
50
+ end
51
+
52
+ GetText = GetTextFallback
53
+ end
@@ -0,0 +1,12 @@
1
+ module ActiveLdap
2
+ module GetTextSupport
3
+ class << self
4
+ def included(base)
5
+ base.class_eval do
6
+ include(GetText)
7
+ bindtextdomain("active-ldap")
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ module ActiveLdap
2
+ module Helper
3
+ def ldap_attribute_name_gettext(attribute)
4
+ Base.human_attribute_name(attribute)
5
+ end
6
+ alias_method(:la_, :ldap_attribute_name_gettext)
7
+
8
+ def ldap_attribute_description_gettext(attribute)
9
+ Base.human_attribute_description(attribute)
10
+ end
11
+ alias_method(:lad_, :ldap_attribute_description_gettext)
12
+
13
+ def ldap_object_class_name_gettext(object_class)
14
+ Base.human_object_class_name(object_class)
15
+ end
16
+ alias_method(:loc_, :ldap_object_class_name_gettext)
17
+
18
+ def ldap_object_class_description_gettext(object_class)
19
+ Base.human_object_class_description(object_class)
20
+ end
21
+ alias_method(:locd_, :ldap_object_class_description_gettext)
22
+ end
23
+ end
@@ -0,0 +1,74 @@
1
+ module ActiveLdap
2
+ class LdapError < Error
3
+ class << self
4
+ def define(code, name, target)
5
+ klass_name = name.downcase.camelize
6
+ target.module_eval(<<-EOC, __FILE__, __LINE__ + 1)
7
+ class #{klass_name} < #{self}
8
+ CODE = #{code}
9
+ def code
10
+ CODE
11
+ end
12
+ end
13
+ EOC
14
+ target.const_get(klass_name)
15
+ end
16
+ end
17
+
18
+ ERRORS = {}
19
+ {
20
+ 0x00 => "SUCCESS",
21
+ 0x01 => "OPERATIONS_ERROR",
22
+ 0x02 => "PROTOCOL_ERROR",
23
+ 0x03 => "TIMELIMIT_EXCEEDED",
24
+ 0x04 => "SIZELIMIT_EXCEEDED",
25
+ 0x05 => "COMPARE_FALSE",
26
+ 0x06 => "COMPARE_TRUE",
27
+ 0x07 => "AUTH_METHOD_NOT_SUPPORTED",
28
+ 0x08 => "STRONG_AUTH_REQUIRED",
29
+ 0x09 => "PARTIAL_RESULTS", # LDAPv2+ (not LDAPv3)
30
+
31
+ 0x0a => "REFERRAL",
32
+ 0x0b => "ADMINLIMIT_EXCEEDED",
33
+ 0x0c => "UNAVAILABLE_CRITICAL_EXTENSION",
34
+ 0x0d => "CONFIDENTIALITY_REQUIRED",
35
+ 0x0e => "LDAP_SASL_BIND_IN_PROGRESS",
36
+
37
+ 0x10 => "NO_SUCH_ATTRIBUTE",
38
+ 0x11 => "UNDEFINED_TYPE",
39
+ 0x12 => "INAPPROPRIATE_MATCHING",
40
+ 0x13 => "CONSTRAINT_VIOLATION",
41
+ 0x14 => "TYPE_OR_VALUE_EXISTS",
42
+ 0x15 => "INVALID_SYNTAX",
43
+
44
+ 0x20 => "NO_SUCH_OBJECT",
45
+ 0x21 => "ALIAS_PROBLEM",
46
+ 0x22 => "INVALID_DN_SYNTAX",
47
+ 0x23 => "IS_LEAF",
48
+ 0x24 => "ALIAS_DEREF_PROBLEM",
49
+
50
+ 0x2F => "PROXY_AUTHZ_FAILURE",
51
+ 0x30 => "INAPPROPRIATE_AUTH",
52
+ 0x31 => "INVALID_CREDENTIALS",
53
+ 0x32 => "INSUFFICIENT_ACCESS",
54
+
55
+ 0x33 => "BUSY",
56
+ 0x34 => "UNAVAILABLE",
57
+ 0x35 => "UNWILLING_TO_PERFORM",
58
+ 0x36 => "LOOP_DETECT",
59
+
60
+ 0x40 => "NAMING_VIOLATION",
61
+ 0x41 => "OBJECT_CLASS_VIOLATION",
62
+ 0x42 => "NOT_ALLOWED_ON_NONLEAF",
63
+ 0x43 => "NOT_ALLOWED_ON_RDN",
64
+ 0x44 => "ALREADY_EXISTS",
65
+ 0x45 => "NO_OBJECT_CLASS_MODS",
66
+ 0x46 => "RESULTS_TOO_LARGE",
67
+ 0x47 => "AFFECTS_MULTIPLE_DSAS",
68
+
69
+ 0x50 => "OTHER",
70
+ }.each do |code, name|
71
+ ERRORS[code] = LdapError.define(code, name, self)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,93 @@
1
+ module ActiveLdap
2
+ module ObjectClass
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def classes
9
+ required_classes.collect do |name|
10
+ schema.object_class(name)
11
+ end
12
+ end
13
+ end
14
+
15
+ def add_class(*target_classes)
16
+ replace_class((classes + target_classes.flatten).uniq)
17
+ end
18
+
19
+ def ensure_recommended_classes
20
+ add_class(self.class.recommended_classes)
21
+ end
22
+
23
+ def remove_class(*target_classes)
24
+ replace_class((classes - target_classes.flatten).uniq)
25
+ end
26
+
27
+ def replace_class(*target_classes)
28
+ new_classes = target_classes.flatten.uniq
29
+ assert_object_classes(new_classes)
30
+ if new_classes.sort != classes.sort
31
+ set_attribute('objectClass', new_classes)
32
+ end
33
+ end
34
+
35
+ def classes
36
+ (get_attribute('objectClass', true) || []).dup
37
+ end
38
+
39
+ private
40
+ def assert_object_classes(new_classes)
41
+ assert_valid_object_class_value_type(new_classes)
42
+ assert_valid_object_class_value(new_classes)
43
+ assert_have_all_required_classes(new_classes)
44
+ end
45
+
46
+ def assert_valid_object_class_value_type(new_classes)
47
+ invalid_classes = new_classes.reject do |new_class|
48
+ new_class.is_a?(String)
49
+ end
50
+ unless invalid_classes.empty?
51
+ format = _("Value in objectClass array is not a String: %s")
52
+ invalid_classes_info = invalid_classes.collect do |invalid_class|
53
+ "#{invalid_class.class}:#{invalid_class.inspect}"
54
+ end.join(", ")
55
+ raise TypeError, format % invalid_classes_info
56
+ end
57
+ end
58
+
59
+ def assert_valid_object_class_value(new_classes)
60
+ _schema = schema
61
+ invalid_classes = new_classes.reject do |new_class|
62
+ !_schema.object_class(new_class).id.nil?
63
+ end
64
+ unless invalid_classes.empty?
65
+ format = _("unknown objectClass in LDAP server: %s")
66
+ message = format % invalid_classes.join(', ')
67
+ raise ObjectClassError, message
68
+ end
69
+ end
70
+
71
+ def assert_have_all_required_classes(new_classes)
72
+ _schema = schema
73
+ normalized_new_classes = new_classes.collect(&:downcase)
74
+ required_classes = self.class.required_classes
75
+ required_classes = required_classes.reject do |required_class_name|
76
+ normalized_new_classes.include?(required_class_name.downcase) or
77
+ (normalized_new_classes.find do |new_class|
78
+ required_class = _schema.object_class(required_class_name)
79
+ _schema.object_class(new_class).super_class?(required_class)
80
+ end)
81
+ end
82
+ unless required_classes.empty?
83
+ format = _("Can't remove required objectClass: %s")
84
+ required_class_names = required_classes.collect do |required_class|
85
+ required_class = _schema.object_class(required_class)
86
+ self.class.human_object_class_name(required_class)
87
+ end
88
+ message = format % required_class_names.join(", ")
89
+ raise RequiredObjectClassMissed, message
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,419 @@
1
+ module ActiveLdap
2
+ module Operations
3
+ class << self
4
+ def included(base)
5
+ super
6
+ base.class_eval do
7
+ extend(Common)
8
+ extend(Find)
9
+ extend(LDIF)
10
+ extend(Delete)
11
+ extend(Update)
12
+
13
+ include(Common)
14
+ include(Find)
15
+ include(LDIF)
16
+ include(Delete)
17
+ include(Update)
18
+ end
19
+ end
20
+ end
21
+
22
+ module Common
23
+ VALID_SEARCH_OPTIONS = [:attribute, :value, :filter, :prefix,
24
+ :classes, :scope, :limit, :attributes,
25
+ :sort_by, :order, :connection]
26
+
27
+ def search(options={}, &block)
28
+ validate_search_options(options)
29
+ attr = options[:attribute]
30
+ value = options[:value] || '*'
31
+ filter = options[:filter]
32
+ prefix = options[:prefix]
33
+ classes = options[:classes]
34
+
35
+ value = value.first if value.is_a?(Array) and value.first.size == 1
36
+ if filter.nil? and !value.is_a?(String)
37
+ message = _("Search value must be a String: %s") % value.inspect
38
+ raise ArgumentError, message
39
+ end
40
+
41
+ _attr, value, _prefix = split_search_value(value)
42
+ attr ||= _attr || dn_attribute || "objectClass"
43
+ prefix ||= _prefix
44
+ filter ||= [attr, value]
45
+ filter = [:and, filter, *object_class_filters(classes)]
46
+ _base = [prefix, base].compact.reject{|x| x.empty?}.join(",")
47
+ if options.has_key?(:ldap_scope)
48
+ logger.warning do
49
+ _(":ldap_scope search option is deprecated. Use :scope instead.")
50
+ end
51
+ options[:scope] ||= options[:ldap_scope]
52
+ end
53
+ search_options = {
54
+ :base => _base,
55
+ :scope => options[:scope] || scope,
56
+ :filter => filter,
57
+ :limit => options[:limit],
58
+ :attributes => options[:attributes],
59
+ :sort_by => options[:sort_by],
60
+ :order => options[:order],
61
+ }
62
+
63
+ conn = options[:connection] || connection
64
+ conn.search(search_options) do |dn, attrs|
65
+ attributes = {}
66
+ attrs.each do |key, value|
67
+ normalized_attr, normalized_value =
68
+ normalize_attribute_options(key, value)
69
+ attributes[normalized_attr] ||= []
70
+ attributes[normalized_attr].concat(normalized_value)
71
+ end
72
+ value = [dn, attributes]
73
+ value = yield(value) if block_given?
74
+ value
75
+ end
76
+ end
77
+
78
+ def exist?(dn, options={})
79
+ attr, value, prefix = split_search_value(dn)
80
+
81
+ options_for_leaf = {
82
+ :attribute => attr,
83
+ :value => value,
84
+ :prefix => prefix,
85
+ }
86
+
87
+ attribute = attr || dn_attribute || "objectClass"
88
+ options_for_non_leaf = {
89
+ :attribute => attr,
90
+ :value => value,
91
+ :prefix => ["#{attribute}=#{value}", prefix].compact.join(","),
92
+ :scope => :base,
93
+ }
94
+
95
+ !search(options_for_leaf.merge(options)).empty? or
96
+ !search(options_for_non_leaf.merge(options)).empty?
97
+ end
98
+ alias_method :exists?, :exist?
99
+
100
+ def count(options={})
101
+ search(options).size
102
+ end
103
+
104
+ private
105
+ def validate_search_options(options)
106
+ options.assert_valid_keys(VALID_SEARCH_OPTIONS)
107
+ end
108
+
109
+ def extract_options_from_args!(args)
110
+ args.last.is_a?(Hash) ? args.pop : {}
111
+ end
112
+
113
+ def ensure_dn_attribute(target)
114
+ "#{dn_attribute}=" +
115
+ target.gsub(/^\s*#{Regexp.escape(dn_attribute)}\s*=\s*/i, '')
116
+ end
117
+
118
+ def ensure_base(target)
119
+ [truncate_base(target), base].join(',')
120
+ end
121
+
122
+ def truncate_base(target)
123
+ if /,/ =~ target
124
+ begin
125
+ (DN.parse(target) - DN.parse(base)).to_s
126
+ rescue DistinguishedNameInvalid, ArgumentError
127
+ target
128
+ end
129
+ else
130
+ target
131
+ end
132
+ end
133
+
134
+ def object_class_filters(classes=nil)
135
+ (classes || required_classes).collect do |name|
136
+ ["objectClass", Escape.ldap_filter_escape(name)]
137
+ end
138
+ end
139
+
140
+ def split_search_value(value)
141
+ attr = prefix = nil
142
+ begin
143
+ dn = DN.parse(value)
144
+ attr, value = dn.rdns.first.to_a.first
145
+ rest = dn.rdns[1..-1]
146
+ prefix = DN.new(*rest).to_s unless rest.empty?
147
+ rescue DistinguishedNameInvalid
148
+ begin
149
+ dn = DN.parse("DUMMY=#{value}")
150
+ _, value = dn.rdns.first.to_a.first
151
+ rest = dn.rdns[1..-1]
152
+ prefix = DN.new(*rest).to_s unless rest.empty?
153
+ rescue DistinguishedNameInvalid
154
+ end
155
+ end
156
+
157
+ prefix = nil if prefix == base
158
+ prefix = truncate_base(prefix) if prefix
159
+ [attr, value, prefix]
160
+ end
161
+ end
162
+
163
+ module Find
164
+ # find
165
+ #
166
+ # Finds the first match for value where |value| is the value of some
167
+ # |field|, or the wildcard match. This is only useful for derived classes.
168
+ # usage: Subclass.find(:attribute => "cn", :value => "some*val")
169
+ # Subclass.find('some*val')
170
+ def find(*args)
171
+ options = extract_options_from_args!(args)
172
+ args = [:first] if args.empty? and !options.empty?
173
+ case args.first
174
+ when :first
175
+ find_initial(options)
176
+ when :all
177
+ options[:value] ||= args[1]
178
+ find_every(options)
179
+ else
180
+ find_from_dns(args, options)
181
+ end
182
+ end
183
+
184
+ private
185
+ def find_initial(options)
186
+ find_every(options.merge(:limit => 1)).first
187
+ end
188
+
189
+ def normalize_sort_order(value)
190
+ case value.to_s
191
+ when /\Aasc(?:end)?\z/i
192
+ :ascend
193
+ when /\Adesc(?:end)?\z/i
194
+ :descend
195
+ else
196
+ raise ArgumentError, _("Invalid order: %s") % value.inspect
197
+ end
198
+ end
199
+
200
+ def find_every(options)
201
+ options = options.dup
202
+ sort_by = options.delete(:sort_by)
203
+ order = options.delete(:order)
204
+ limit = options.delete(:limit) if sort_by or order
205
+
206
+ results = search(options).collect do |dn, attrs|
207
+ instantiate([dn, attrs, {:connection => options[:connection]}])
208
+ end
209
+ return results if sort_by.nil? and order.nil?
210
+
211
+ sort_by ||= "dn"
212
+ if sort_by.downcase == "dn"
213
+ results = results.sort_by {|result| DN.parse(result.dn)}
214
+ else
215
+ results = results.sort_by {|result| result.send(sort_by)}
216
+ end
217
+
218
+ results.reverse! if normalize_sort_order(order || "ascend") == :descend
219
+ results = results[0, limit] if limit
220
+ results
221
+ end
222
+
223
+ def find_from_dns(dns, options)
224
+ expects_array = dns.first.is_a?(Array)
225
+ return [] if expects_array and dns.first.empty?
226
+
227
+ dns = dns.flatten.compact.uniq
228
+
229
+ case dns.size
230
+ when 0
231
+ raise EntryNotFound, _("Couldn't find %s without a DN") % name
232
+ when 1
233
+ result = find_one(dns.first, options)
234
+ expects_array ? [result] : result
235
+ else
236
+ find_some(dns, options)
237
+ end
238
+ end
239
+
240
+ def find_one(dn, options)
241
+ attr, value, prefix = split_search_value(dn)
242
+ filter = [attr || dn_attribute, Escape.ldap_filter_escape(value)]
243
+ filter = [:and, filter, options[:filter]] if options[:filter]
244
+ options = {:prefix => prefix}.merge(options.merge(:filter => filter))
245
+ result = find_initial(options)
246
+ if result
247
+ result
248
+ else
249
+ args = [name, dn]
250
+ if options[:filter]
251
+ format = _("Couldn't find %s: DN: %s: filter: %s")
252
+ args << options[:filter].inspect
253
+ else
254
+ format = _("Couldn't find %s: DN: %s")
255
+ end
256
+ raise EntryNotFound, format % args
257
+ end
258
+ end
259
+
260
+ def find_some(dns, options)
261
+ dn_filters = dns.collect do |dn|
262
+ attr, value, prefix = split_search_value(dn)
263
+ attr ||= dn_attribute
264
+ filter = [attr, value]
265
+ if prefix
266
+ filter = [:and,
267
+ filter,
268
+ [dn, "*,#{Escape.ldap_filter_escape(prefix)},#{base}"]]
269
+ end
270
+ filter
271
+ end
272
+ filter = [:or, *dn_filters]
273
+ filter = [:and, filter, options[:filter]] if options[:filter]
274
+ result = find_every(options.merge(:filter => filter))
275
+ if result.size == dns.size
276
+ result
277
+ else
278
+ args = [name, dns.join(', ')]
279
+ if options[:filter]
280
+ format = _("Couldn't find all %s: DNs (%s): filter: %s")
281
+ args << options[:filter].inspect
282
+ else
283
+ format = _("Couldn't find all %s: DNs (%s)")
284
+ end
285
+ raise EntryNotFound, format % args
286
+ end
287
+ end
288
+
289
+ def ensure_dn(target)
290
+ attr, value, prefix = split_search_value(target)
291
+ "#{attr || dn_attribute}=#{value},#{prefix || base}"
292
+ end
293
+ end
294
+
295
+ module LDIF
296
+ def dump(options={})
297
+ ldifs = []
298
+ options = {:base => base, :scope => scope}.merge(options)
299
+ conn = options[:connection] || connection
300
+ conn.search(options) do |dn, attributes|
301
+ ldifs << to_ldif(dn, attributes)
302
+ end
303
+ ldifs.join("\n")
304
+ end
305
+
306
+ def to_ldif(dn, attributes, options={})
307
+ conn = options[:connection] || connection
308
+ conn.to_ldif(dn, unnormalize_attributes(attributes))
309
+ end
310
+
311
+ def load(ldifs, options={})
312
+ conn = options[:connection] || connection
313
+ conn.load(ldifs)
314
+ end
315
+ end
316
+
317
+ module Delete
318
+ def destroy(targets, options={})
319
+ targets = [targets] unless targets.is_a?(Array)
320
+ targets.each do |target|
321
+ find(target, options).destroy
322
+ end
323
+ end
324
+
325
+ def destroy_all(filter=nil, options={})
326
+ targets = []
327
+ if filter.is_a?(Hash)
328
+ options = options.merge(filter)
329
+ filter = nil
330
+ end
331
+ options = options.merge(:filter => filter) if filter
332
+ find(:all, options).sort_by do |target|
333
+ target.dn.reverse
334
+ end.reverse.each do |target|
335
+ target.destroy
336
+ end
337
+ end
338
+
339
+ def delete(targets, options={})
340
+ targets = [targets] unless targets.is_a?(Array)
341
+ targets = targets.collect do |target|
342
+ ensure_dn_attribute(ensure_base(target))
343
+ end
344
+ conn = options[:connection] || connection
345
+ conn.delete(targets, options)
346
+ end
347
+
348
+ def delete_all(filter=nil, options={})
349
+ options = {:base => base, :scope => scope}.merge(options)
350
+ options = options.merge(:filter => filter) if filter
351
+ conn = options[:connection] || connection
352
+ targets = conn.search(options).collect do |dn, attributes|
353
+ dn
354
+ end.sort_by do |dn|
355
+ dn.upcase.reverse
356
+ end.reverse
357
+
358
+ conn.delete(targets)
359
+ end
360
+ end
361
+
362
+ module Update
363
+ def add_entry(dn, attributes, options={})
364
+ unnormalized_attributes = attributes.collect do |type, key, value|
365
+ [type, key, unnormalize_attribute(key, value)]
366
+ end
367
+ conn = options[:connection] || connection
368
+ conn.add(dn, unnormalized_attributes, options)
369
+ end
370
+
371
+ def modify_entry(dn, attributes, options={})
372
+ unnormalized_attributes = attributes.collect do |type, key, value|
373
+ [type, key, unnormalize_attribute(key, value)]
374
+ end
375
+ conn = options[:connection] || connection
376
+ conn.modify(dn, unnormalized_attributes, options)
377
+ end
378
+
379
+ def update(dn, attributes, options={})
380
+ if dn.is_a?(Array)
381
+ i = -1
382
+ dns = dn
383
+ dns.collect do |dn|
384
+ i += 1
385
+ update(dn, attributes[i], options)
386
+ end
387
+ else
388
+ object = find(dn, options)
389
+ object.update_attributes(attributes)
390
+ object
391
+ end
392
+ end
393
+
394
+ def update_all(attributes, filter=nil, options={})
395
+ search_options = options.dup
396
+ if filter
397
+ if filter.is_a?(String) and /[=\(\)&\|]/ !~ filter
398
+ search_options = search_options.merge(:value => filter)
399
+ else
400
+ search_options = search_options.merge(:filter => filter)
401
+ end
402
+ end
403
+ targets = search(search_options).collect do |dn, attrs|
404
+ dn
405
+ end
406
+
407
+ unnormalized_attributes = attributes.collect do |name, value|
408
+ normalized_name, normalized_value = normalize_attribute(name, value)
409
+ [:replace, normalized_name,
410
+ unnormalize_attribute(normalized_name, normalized_value)]
411
+ end
412
+ conn = options[:connection] || connection
413
+ targets.each do |dn|
414
+ conn.modify(dn, unnormalized_attributes, options)
415
+ end
416
+ end
417
+ end
418
+ end
419
+ end