powerhome-activeldap 3.2.3

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 (145) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +6 -0
  3. data/COPYING +340 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE +59 -0
  6. data/README.textile +140 -0
  7. data/TODO +32 -0
  8. data/benchmark/README.md +64 -0
  9. data/benchmark/bench-backend.rb +247 -0
  10. data/benchmark/bench-instantiate.rb +98 -0
  11. data/benchmark/config.yaml.sample +5 -0
  12. data/doc/text/development.textile +54 -0
  13. data/doc/text/news.textile +811 -0
  14. data/doc/text/rails.textile +144 -0
  15. data/doc/text/tutorial.textile +1010 -0
  16. data/examples/config.yaml.example +5 -0
  17. data/examples/example.der +0 -0
  18. data/examples/example.jpg +0 -0
  19. data/examples/groupadd +41 -0
  20. data/examples/groupdel +35 -0
  21. data/examples/groupls +49 -0
  22. data/examples/groupmod +42 -0
  23. data/examples/lpasswd +55 -0
  24. data/examples/objects/group.rb +13 -0
  25. data/examples/objects/ou.rb +4 -0
  26. data/examples/objects/user.rb +20 -0
  27. data/examples/ouadd +38 -0
  28. data/examples/useradd +45 -0
  29. data/examples/useradd-binary +53 -0
  30. data/examples/userdel +34 -0
  31. data/examples/userls +50 -0
  32. data/examples/usermod +42 -0
  33. data/examples/usermod-binary-add +50 -0
  34. data/examples/usermod-binary-add-time +54 -0
  35. data/examples/usermod-binary-del +48 -0
  36. data/examples/usermod-lang-add +43 -0
  37. data/lib/active_ldap.rb +85 -0
  38. data/lib/active_ldap/action_controller/ldap_benchmarking.rb +55 -0
  39. data/lib/active_ldap/acts/tree.rb +78 -0
  40. data/lib/active_ldap/adapter/base.rb +707 -0
  41. data/lib/active_ldap/adapter/jndi.rb +184 -0
  42. data/lib/active_ldap/adapter/jndi_connection.rb +185 -0
  43. data/lib/active_ldap/adapter/ldap.rb +290 -0
  44. data/lib/active_ldap/adapter/ldap_ext.rb +105 -0
  45. data/lib/active_ldap/adapter/net_ldap.rb +309 -0
  46. data/lib/active_ldap/adapter/net_ldap_ext.rb +23 -0
  47. data/lib/active_ldap/association/belongs_to.rb +47 -0
  48. data/lib/active_ldap/association/belongs_to_many.rb +58 -0
  49. data/lib/active_ldap/association/children.rb +21 -0
  50. data/lib/active_ldap/association/collection.rb +105 -0
  51. data/lib/active_ldap/association/has_many.rb +31 -0
  52. data/lib/active_ldap/association/has_many_utils.rb +44 -0
  53. data/lib/active_ldap/association/has_many_wrap.rb +75 -0
  54. data/lib/active_ldap/association/proxy.rb +107 -0
  55. data/lib/active_ldap/associations.rb +205 -0
  56. data/lib/active_ldap/attribute_methods.rb +23 -0
  57. data/lib/active_ldap/attribute_methods/before_type_cast.rb +24 -0
  58. data/lib/active_ldap/attribute_methods/dirty.rb +43 -0
  59. data/lib/active_ldap/attribute_methods/query.rb +31 -0
  60. data/lib/active_ldap/attribute_methods/read.rb +44 -0
  61. data/lib/active_ldap/attribute_methods/write.rb +38 -0
  62. data/lib/active_ldap/attributes.rb +176 -0
  63. data/lib/active_ldap/base.rb +1410 -0
  64. data/lib/active_ldap/callbacks.rb +71 -0
  65. data/lib/active_ldap/command.rb +49 -0
  66. data/lib/active_ldap/compatible.rb +44 -0
  67. data/lib/active_ldap/configuration.rb +147 -0
  68. data/lib/active_ldap/connection.rb +299 -0
  69. data/lib/active_ldap/distinguished_name.rb +291 -0
  70. data/lib/active_ldap/entry_attribute.rb +78 -0
  71. data/lib/active_ldap/escape.rb +12 -0
  72. data/lib/active_ldap/get_text.rb +20 -0
  73. data/lib/active_ldap/get_text/parser.rb +161 -0
  74. data/lib/active_ldap/helper.rb +92 -0
  75. data/lib/active_ldap/human_readable.rb +133 -0
  76. data/lib/active_ldap/ldap_error.rb +74 -0
  77. data/lib/active_ldap/ldif.rb +930 -0
  78. data/lib/active_ldap/log_subscriber.rb +50 -0
  79. data/lib/active_ldap/object_class.rb +95 -0
  80. data/lib/active_ldap/operations.rb +624 -0
  81. data/lib/active_ldap/persistence.rb +100 -0
  82. data/lib/active_ldap/populate.rb +53 -0
  83. data/lib/active_ldap/railtie.rb +43 -0
  84. data/lib/active_ldap/railties/controller_runtime.rb +48 -0
  85. data/lib/active_ldap/schema.rb +701 -0
  86. data/lib/active_ldap/schema/syntaxes.rb +422 -0
  87. data/lib/active_ldap/timeout.rb +75 -0
  88. data/lib/active_ldap/timeout_stub.rb +17 -0
  89. data/lib/active_ldap/user_password.rb +99 -0
  90. data/lib/active_ldap/validations.rb +200 -0
  91. data/lib/active_ldap/version.rb +3 -0
  92. data/lib/active_ldap/xml.rb +139 -0
  93. data/lib/rails/generators/active_ldap/model/USAGE +18 -0
  94. data/lib/rails/generators/active_ldap/model/model_generator.rb +47 -0
  95. data/lib/rails/generators/active_ldap/model/templates/model_active_ldap.rb +3 -0
  96. data/lib/rails/generators/active_ldap/scaffold/scaffold_generator.rb +14 -0
  97. data/lib/rails/generators/active_ldap/scaffold/templates/ldap.yml +19 -0
  98. data/po/en/active-ldap.po +4029 -0
  99. data/po/ja/active-ldap.po +4060 -0
  100. data/test/add-phonetic-attribute-options-to-slapd.ldif +10 -0
  101. data/test/al-test-utils.rb +428 -0
  102. data/test/command.rb +111 -0
  103. data/test/config.yaml.sample +6 -0
  104. data/test/fixtures/lower_case_object_class_schema.rb +802 -0
  105. data/test/run-test.rb +34 -0
  106. data/test/test_acts_as_tree.rb +60 -0
  107. data/test/test_adapter.rb +121 -0
  108. data/test/test_associations.rb +701 -0
  109. data/test/test_attributes.rb +117 -0
  110. data/test/test_base.rb +1214 -0
  111. data/test/test_base_per_instance.rb +61 -0
  112. data/test/test_bind.rb +62 -0
  113. data/test/test_callback.rb +31 -0
  114. data/test/test_configuration.rb +40 -0
  115. data/test/test_connection.rb +82 -0
  116. data/test/test_connection_per_class.rb +112 -0
  117. data/test/test_connection_per_dn.rb +112 -0
  118. data/test/test_dirty.rb +98 -0
  119. data/test/test_dn.rb +172 -0
  120. data/test/test_find.rb +176 -0
  121. data/test/test_groupadd.rb +50 -0
  122. data/test/test_groupdel.rb +46 -0
  123. data/test/test_groupls.rb +107 -0
  124. data/test/test_groupmod.rb +51 -0
  125. data/test/test_ldif.rb +1890 -0
  126. data/test/test_load.rb +133 -0
  127. data/test/test_lpasswd.rb +75 -0
  128. data/test/test_object_class.rb +74 -0
  129. data/test/test_persistence.rb +131 -0
  130. data/test/test_reflection.rb +175 -0
  131. data/test/test_schema.rb +559 -0
  132. data/test/test_syntax.rb +444 -0
  133. data/test/test_user.rb +217 -0
  134. data/test/test_user_password.rb +108 -0
  135. data/test/test_useradd-binary.rb +62 -0
  136. data/test/test_useradd.rb +57 -0
  137. data/test/test_userdel.rb +48 -0
  138. data/test/test_userls.rb +91 -0
  139. data/test/test_usermod-binary-add-time.rb +65 -0
  140. data/test/test_usermod-binary-add.rb +64 -0
  141. data/test/test_usermod-binary-del.rb +66 -0
  142. data/test/test_usermod-lang-add.rb +59 -0
  143. data/test/test_usermod.rb +58 -0
  144. data/test/test_validation.rb +274 -0
  145. metadata +379 -0
@@ -0,0 +1,17 @@
1
+ require 'timeout'
2
+
3
+ module Timeout
4
+ # STUB
5
+ def Timeout.alarm(sec, exception=Timeout::Error, &block)
6
+ timeout(sec, exception, &block)
7
+ end
8
+ end # Timeout
9
+
10
+ if __FILE__ == $0
11
+ require 'time'
12
+ Timeout.alarm(2) do
13
+ loop do
14
+ p Time.now
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,99 @@
1
+ require 'English'
2
+ require 'base64'
3
+ require 'digest/md5'
4
+ require 'digest/sha1'
5
+
6
+ module ActiveLdap
7
+ module UserPassword
8
+ module_function
9
+ def valid?(password, hashed_password)
10
+ unless /^\{([A-Z][A-Z\d]+)\}/ =~ hashed_password
11
+ # Plain text password
12
+ return hashed_password == password
13
+ end
14
+ type = $1
15
+ hashed_password_without_type = $POSTMATCH
16
+ normalized_type = type.downcase
17
+ unless respond_to?(normalized_type)
18
+ raise ArgumentError, _("Unknown Hash type: %s") % type
19
+ end
20
+ salt_extractor = "extract_salt_for_#{normalized_type}"
21
+ if respond_to?(salt_extractor)
22
+ salt = send(salt_extractor, hashed_password_without_type)
23
+ if salt.nil?
24
+ raise ArgumentError,
25
+ _("Can't extract salt from hashed password: %s") % hashed_password
26
+ end
27
+ generated_password = send(normalized_type, password, salt)
28
+ else
29
+ generated_password = send(normalized_type, password)
30
+ end
31
+ hashed_password == generated_password
32
+ end
33
+
34
+ def crypt(password, salt=nil)
35
+ salt ||= "$1$#{Salt.generate(8)}"
36
+ "{CRYPT}#{password.crypt(salt)}"
37
+ end
38
+
39
+ def extract_salt_for_crypt(crypted_password)
40
+ if /^\$1\$/ =~ crypted_password
41
+ $MATCH + $POSTMATCH[0, 8].sub(/\$.*/, '') + "$"
42
+ else
43
+ crypted_password[0, 2]
44
+ end
45
+ end
46
+
47
+ def md5(password)
48
+ "{MD5}#{[Digest::MD5.digest(password)].pack('m').chomp}"
49
+ end
50
+
51
+ def smd5(password, salt=nil)
52
+ if salt and salt.size < 4
53
+ raise ArgumentError, _("salt size must be >= 4: %s") % salt.inspect
54
+ end
55
+ salt ||= Salt.generate(4)
56
+ md5_hash_with_salt = "#{Digest::MD5.digest(password + salt)}#{salt}"
57
+ "{SMD5}#{[md5_hash_with_salt].pack('m').chomp}"
58
+ end
59
+
60
+ def extract_salt_for_smd5(smd5ed_password)
61
+ extract_salt_at_pos(smd5ed_password, 16)
62
+ end
63
+
64
+ def sha(password)
65
+ "{SHA}#{[Digest::SHA1.digest(password)].pack('m').chomp}"
66
+ end
67
+
68
+ def ssha(password, salt=nil)
69
+ if salt and salt.size < 4
70
+ raise ArgumentError, _("salt size must be >= 4: %s") % salt.inspect
71
+ end
72
+ salt ||= Salt.generate(4)
73
+ sha1_hash_with_salt = "#{Digest::SHA1.digest(password + salt)}#{salt}"
74
+ "{SSHA}#{[sha1_hash_with_salt].pack('m').chomp}"
75
+ end
76
+
77
+ def extract_salt_for_ssha(sshaed_password)
78
+ extract_salt_at_pos(sshaed_password, 20)
79
+ end
80
+
81
+ def extract_salt_at_pos(hashed_password, position)
82
+ salt = Base64.decode64(hashed_password)[position..-1]
83
+ salt == '' ? nil : salt
84
+ end
85
+
86
+ module Salt
87
+ CHARS = ['.', '/'] + ['0'..'9', 'A'..'Z', 'a'..'z'].collect do |x|
88
+ x.to_a
89
+ end.flatten
90
+
91
+ module_function
92
+ def generate(length)
93
+ salt = ""
94
+ length.times {salt << CHARS[rand(CHARS.length)]}
95
+ salt
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,200 @@
1
+ module ActiveLdap
2
+ module Validations
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Validations
5
+
6
+ included do
7
+ alias_method :new_record?, :new_entry?
8
+ class << self
9
+ unless method_defined?(:human_attribute_name_with_gettext)
10
+ def human_attribute_name_with_gettext(attribute_key_name, options={})
11
+ logger.warn("options was ignored.") unless options.empty?
12
+ s_("#{self}|#{attribute_key_name.to_s.humanize}")
13
+ end
14
+ end
15
+ end
16
+
17
+ class_local_attr_accessor true, :validation_skip_attributes
18
+ remove_method :validation_skip_attributes
19
+ self.validation_skip_attributes = []
20
+
21
+ validate :validate_duplicated_dn_creation, :on => :create
22
+ validate :validate_duplicated_dn_rename, :on => :update
23
+ validate :validate_dn
24
+ validate :validate_excluded_classes
25
+ validate :validate_required_ldap_values
26
+ validate :validate_ldap_values
27
+ end
28
+
29
+ def validation_skip_attributes
30
+ @validation_skip_attributes ||= []
31
+ end
32
+
33
+ def validation_skip_attributes=(attributes)
34
+ @validation_skip_attributes = attributes
35
+ end
36
+
37
+ def valid?(context = nil)
38
+ context ||= (new_entry? ? :create : :update)
39
+ output = super(context)
40
+ errors.empty? && output
41
+ end
42
+
43
+ def save(*)
44
+ valid? ? super : false
45
+ end
46
+
47
+ def save!(*)
48
+ valid? ? super : raise(EntryInvalid.new(self))
49
+ end
50
+
51
+ private
52
+ def format_validation_message(format, parameters)
53
+ format % parameters
54
+ end
55
+
56
+ def validate_duplicated_dn_creation
57
+ _dn = nil
58
+ begin
59
+ _dn = dn
60
+ rescue DistinguishedNameInvalid, DistinguishedNameNotSetError
61
+ return
62
+ end
63
+ if _dn and exist?
64
+ format = _("is duplicated: %s")
65
+ message = format_validation_message(format, _dn)
66
+ errors.add("distinguishedName", message)
67
+ end
68
+ end
69
+
70
+ def validate_duplicated_dn_rename
71
+ _dn_attribute = dn_attribute_with_fallback
72
+ original_dn_value = @ldap_data[_dn_attribute]
73
+ current_dn_value = @data[_dn_attribute]
74
+ return if original_dn_value == current_dn_value
75
+ return if original_dn_value == [current_dn_value]
76
+
77
+ _dn = nil
78
+ begin
79
+ _dn = dn
80
+ rescue DistinguishedNameInvalid, DistinguishedNameNotSetError
81
+ return
82
+ end
83
+ if _dn and exist?
84
+ format = _("is duplicated: %s")
85
+ message = format_validation_message(format, _dn)
86
+ errors.add("distinguishedName", message)
87
+ end
88
+ end
89
+
90
+ def validate_dn
91
+ dn
92
+ rescue DistinguishedNameInvalid
93
+ format = _("is invalid: %s")
94
+ message = format_validation_message(format, $!.message)
95
+ errors.add("distinguishedName", message)
96
+ rescue DistinguishedNameNotSetError
97
+ format = _("isn't set: %s")
98
+ message = format_validation_message(format, $!.message)
99
+ errors.add("distinguishedName", message)
100
+ end
101
+
102
+ def validate_excluded_classes
103
+ excluded_classes = self.class.excluded_classes
104
+ return if excluded_classes.empty?
105
+
106
+ _schema = schema
107
+ _classes = classes.collect do |name|
108
+ _schema.object_class(name)
109
+ end
110
+ unexpected_classes = excluded_classes.inject([]) do |classes, name|
111
+ excluded_class = _schema.object_class(name)
112
+ if _classes.include?(excluded_class)
113
+ classes << excluded_class
114
+ end
115
+ classes
116
+ end
117
+ return if unexpected_classes.empty?
118
+
119
+ names = unexpected_classes.collect do |object_class|
120
+ self.class.human_object_class_name(object_class)
121
+ end
122
+ format = n_("has excluded value: %s",
123
+ "has excluded values: %s",
124
+ names.size)
125
+ message = format_validation_message(format, names.join(", "))
126
+ errors.add("objectClass", message)
127
+ end
128
+
129
+ # validate_required_ldap_values
130
+ #
131
+ # Basic validation:
132
+ # - Verify that every 'MUST' specified in the schema has a value defined
133
+ def validate_required_ldap_values
134
+ _schema = nil
135
+ @validation_skip_attributes ||= []
136
+ _validation_skip_attributes =
137
+ @validation_skip_attributes +
138
+ (self.class.validation_skip_attributes || [])
139
+ # Make sure all MUST attributes have a value
140
+ entry_attribute.object_classes.each do |object_class|
141
+ object_class.must.each do |required_attribute|
142
+ # Normalize to ensure we catch schema problems
143
+ # needed?
144
+ real_name = to_real_attribute_name(required_attribute.name, true)
145
+ raise UnknownAttribute.new(required_attribute) if real_name.nil?
146
+
147
+ next if required_attribute.read_only?
148
+ next if _validation_skip_attributes.include?(real_name)
149
+
150
+ value = @data[real_name] || []
151
+ next unless self.class.blank_value?(value)
152
+
153
+ _schema ||= schema
154
+ aliases = required_attribute.aliases.collect do |name|
155
+ self.class.human_attribute_name(name)
156
+ end
157
+ args = [self.class.human_object_class_name(object_class)]
158
+ if aliases.empty?
159
+ format = _("is required attribute by objectClass '%s'")
160
+ else
161
+ format = _("is required attribute by objectClass " \
162
+ "'%s': aliases: %s")
163
+ args << aliases.join(', ')
164
+ end
165
+ message = format_validation_message(format, args)
166
+ errors.add(real_name, message)
167
+ end
168
+ end
169
+ end
170
+
171
+ def validate_ldap_values
172
+ entry_attribute.schemata.each do |name, attribute|
173
+ value = self[name]
174
+ next if self.class.blank_value?(value)
175
+ validate_ldap_value(attribute, name, value)
176
+ end
177
+ end
178
+
179
+ def validate_ldap_value(attribute, name, value)
180
+ failed_reason, option = attribute.validate(value)
181
+ return if failed_reason.nil?
182
+ if attribute.binary?
183
+ inspected_value = _("<binary-value>")
184
+ else
185
+ inspected_value = self.class.human_readable_format(value)
186
+ end
187
+ params = [inspected_value,
188
+ self.class.human_syntax_description(attribute.syntax),
189
+ failed_reason]
190
+ if option
191
+ format = _("(%s) has invalid format: %s: required syntax: %s: %s")
192
+ else
193
+ format = _("has invalid format: %s: required syntax: %s: %s")
194
+ end
195
+ params.unshift(option) if option
196
+ message = format_validation_message(format, params)
197
+ errors.add(name, message)
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveLdap
2
+ VERSION = "3.2.3"
3
+ end
@@ -0,0 +1,139 @@
1
+ require 'erb'
2
+ require 'builder'
3
+
4
+ require 'active_ldap/ldif'
5
+
6
+ module ActiveLdap
7
+ class Xml
8
+ class Serializer
9
+ PRINTABLE_STRING = /[\x20-\x7e\t\r\n]*/n
10
+
11
+ def initialize(dn, attributes, schema, options={})
12
+ @dn = dn
13
+ @attributes = attributes
14
+ @schema = schema
15
+ @options = options
16
+ end
17
+
18
+ def to_s
19
+ root = @options[:root]
20
+ indent = @options[:indent] || 2
21
+ xml = @options[:builder] || Builder::XmlMarkup.new(:indent => indent)
22
+ xml.tag!(root) do
23
+ target_attributes.each do |key, values|
24
+ values = normalize_values(values).sort_by {|value, _| value}
25
+ if @schema.attribute(key).single_value?
26
+ next if values.empty?
27
+ serialize_attribute_value(xml, key, *values[0])
28
+ else
29
+ serialize_attribute_values(xml, key, values)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+ def target_attributes
37
+ except_dn = false
38
+ only = @options[:only] || []
39
+ except = @options[:except] || []
40
+ if !only.empty?
41
+ attributes = []
42
+ except_dn = true
43
+ only.each do |name|
44
+ if name == "dn"
45
+ except_dn = false
46
+ elsif @attributes.has_key?(name)
47
+ attributes << [name, @attributes[name]]
48
+ end
49
+ end
50
+ elsif !except.empty?
51
+ attributes = @attributes.dup
52
+ except.each do |name|
53
+ if name == "dn"
54
+ except_dn = true
55
+ else
56
+ attributes.delete(name)
57
+ end
58
+ end
59
+ else
60
+ attributes = @attributes.dup
61
+ end
62
+ attributes = attributes.sort_by {|key, values| key}
63
+ attributes.unshift(["dn", [@dn]]) unless except_dn
64
+ attributes
65
+ end
66
+
67
+ def normalize_values(values)
68
+ targets = []
69
+ values.each do |value|
70
+ targets.concat(normalize_value(value))
71
+ end
72
+ targets
73
+ end
74
+
75
+ def normalize_value(value, options=[])
76
+ targets = []
77
+ case value
78
+ when Hash
79
+ value.each do |real_option, real_value|
80
+ targets.concat(normalize_value(real_value, options + [real_option]))
81
+ end
82
+ when Array
83
+ value.each do |real_value|
84
+ targets.concat(normalize_value(real_value, options))
85
+ end
86
+ when DN
87
+ targets.concat(normalize_value(value.to_s, options))
88
+ when nil
89
+ # ignore
90
+ else
91
+ if /\A#{PRINTABLE_STRING}\z/ !~ value
92
+ value = [value].pack("m").gsub(/\n/u, '')
93
+ options += ["base64"]
94
+ end
95
+ xml_attributes = {}
96
+ options.each do |name, val|
97
+ xml_attributes[name] = val || "true"
98
+ end
99
+ targets << [value, xml_attributes]
100
+ end
101
+ targets
102
+ end
103
+
104
+ def serialize_attribute_values(xml, name, values)
105
+ return if values.blank?
106
+
107
+ if name == "dn" or @options[:type].to_s.downcase == "ldif"
108
+ values.each do |value, xml_attributes|
109
+ serialize_attribute_value(xml, name, value, xml_attributes)
110
+ end
111
+ else
112
+ plural_name = name.pluralize
113
+ attributes = @options[:skip_types] ? {} : {"type" => "array"}
114
+ xml.tag!(plural_name, attributes) do
115
+ values.each do |value, xml_attributes|
116
+ serialize_attribute_value(xml, name, value, xml_attributes)
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ def serialize_attribute_value(xml, name, value, xml_attributes)
123
+ xml.tag!(name, value, xml_attributes)
124
+ end
125
+ end
126
+
127
+ def initialize(dn, attributes, schema)
128
+ @dn = dn
129
+ @attributes = attributes
130
+ @schema = schema
131
+ end
132
+
133
+ def to_s(options={})
134
+ Serializer.new(@dn, @attributes, @schema, options).to_s
135
+ end
136
+ end
137
+
138
+ XML = Xml
139
+ end