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,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