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.
- checksums.yaml +7 -0
- data/.yardopts +6 -0
- data/COPYING +340 -0
- data/Gemfile +12 -0
- data/LICENSE +59 -0
- data/README.textile +140 -0
- data/TODO +32 -0
- data/benchmark/README.md +64 -0
- data/benchmark/bench-backend.rb +247 -0
- data/benchmark/bench-instantiate.rb +98 -0
- data/benchmark/config.yaml.sample +5 -0
- data/doc/text/development.textile +54 -0
- data/doc/text/news.textile +811 -0
- data/doc/text/rails.textile +144 -0
- data/doc/text/tutorial.textile +1010 -0
- data/examples/config.yaml.example +5 -0
- data/examples/example.der +0 -0
- data/examples/example.jpg +0 -0
- data/examples/groupadd +41 -0
- data/examples/groupdel +35 -0
- data/examples/groupls +49 -0
- data/examples/groupmod +42 -0
- data/examples/lpasswd +55 -0
- data/examples/objects/group.rb +13 -0
- data/examples/objects/ou.rb +4 -0
- data/examples/objects/user.rb +20 -0
- data/examples/ouadd +38 -0
- data/examples/useradd +45 -0
- data/examples/useradd-binary +53 -0
- data/examples/userdel +34 -0
- data/examples/userls +50 -0
- data/examples/usermod +42 -0
- data/examples/usermod-binary-add +50 -0
- data/examples/usermod-binary-add-time +54 -0
- data/examples/usermod-binary-del +48 -0
- data/examples/usermod-lang-add +43 -0
- data/lib/active_ldap.rb +85 -0
- data/lib/active_ldap/action_controller/ldap_benchmarking.rb +55 -0
- data/lib/active_ldap/acts/tree.rb +78 -0
- data/lib/active_ldap/adapter/base.rb +707 -0
- data/lib/active_ldap/adapter/jndi.rb +184 -0
- data/lib/active_ldap/adapter/jndi_connection.rb +185 -0
- data/lib/active_ldap/adapter/ldap.rb +290 -0
- data/lib/active_ldap/adapter/ldap_ext.rb +105 -0
- data/lib/active_ldap/adapter/net_ldap.rb +309 -0
- data/lib/active_ldap/adapter/net_ldap_ext.rb +23 -0
- data/lib/active_ldap/association/belongs_to.rb +47 -0
- data/lib/active_ldap/association/belongs_to_many.rb +58 -0
- data/lib/active_ldap/association/children.rb +21 -0
- data/lib/active_ldap/association/collection.rb +105 -0
- data/lib/active_ldap/association/has_many.rb +31 -0
- data/lib/active_ldap/association/has_many_utils.rb +44 -0
- data/lib/active_ldap/association/has_many_wrap.rb +75 -0
- data/lib/active_ldap/association/proxy.rb +107 -0
- data/lib/active_ldap/associations.rb +205 -0
- data/lib/active_ldap/attribute_methods.rb +23 -0
- data/lib/active_ldap/attribute_methods/before_type_cast.rb +24 -0
- data/lib/active_ldap/attribute_methods/dirty.rb +43 -0
- data/lib/active_ldap/attribute_methods/query.rb +31 -0
- data/lib/active_ldap/attribute_methods/read.rb +44 -0
- data/lib/active_ldap/attribute_methods/write.rb +38 -0
- data/lib/active_ldap/attributes.rb +176 -0
- data/lib/active_ldap/base.rb +1410 -0
- data/lib/active_ldap/callbacks.rb +71 -0
- data/lib/active_ldap/command.rb +49 -0
- data/lib/active_ldap/compatible.rb +44 -0
- data/lib/active_ldap/configuration.rb +147 -0
- data/lib/active_ldap/connection.rb +299 -0
- data/lib/active_ldap/distinguished_name.rb +291 -0
- data/lib/active_ldap/entry_attribute.rb +78 -0
- data/lib/active_ldap/escape.rb +12 -0
- data/lib/active_ldap/get_text.rb +20 -0
- data/lib/active_ldap/get_text/parser.rb +161 -0
- data/lib/active_ldap/helper.rb +92 -0
- data/lib/active_ldap/human_readable.rb +133 -0
- data/lib/active_ldap/ldap_error.rb +74 -0
- data/lib/active_ldap/ldif.rb +930 -0
- data/lib/active_ldap/log_subscriber.rb +50 -0
- data/lib/active_ldap/object_class.rb +95 -0
- data/lib/active_ldap/operations.rb +624 -0
- data/lib/active_ldap/persistence.rb +100 -0
- data/lib/active_ldap/populate.rb +53 -0
- data/lib/active_ldap/railtie.rb +43 -0
- data/lib/active_ldap/railties/controller_runtime.rb +48 -0
- data/lib/active_ldap/schema.rb +701 -0
- data/lib/active_ldap/schema/syntaxes.rb +422 -0
- data/lib/active_ldap/timeout.rb +75 -0
- data/lib/active_ldap/timeout_stub.rb +17 -0
- data/lib/active_ldap/user_password.rb +99 -0
- data/lib/active_ldap/validations.rb +200 -0
- data/lib/active_ldap/version.rb +3 -0
- data/lib/active_ldap/xml.rb +139 -0
- data/lib/rails/generators/active_ldap/model/USAGE +18 -0
- data/lib/rails/generators/active_ldap/model/model_generator.rb +47 -0
- data/lib/rails/generators/active_ldap/model/templates/model_active_ldap.rb +3 -0
- data/lib/rails/generators/active_ldap/scaffold/scaffold_generator.rb +14 -0
- data/lib/rails/generators/active_ldap/scaffold/templates/ldap.yml +19 -0
- data/po/en/active-ldap.po +4029 -0
- data/po/ja/active-ldap.po +4060 -0
- data/test/add-phonetic-attribute-options-to-slapd.ldif +10 -0
- data/test/al-test-utils.rb +428 -0
- data/test/command.rb +111 -0
- data/test/config.yaml.sample +6 -0
- data/test/fixtures/lower_case_object_class_schema.rb +802 -0
- data/test/run-test.rb +34 -0
- data/test/test_acts_as_tree.rb +60 -0
- data/test/test_adapter.rb +121 -0
- data/test/test_associations.rb +701 -0
- data/test/test_attributes.rb +117 -0
- data/test/test_base.rb +1214 -0
- data/test/test_base_per_instance.rb +61 -0
- data/test/test_bind.rb +62 -0
- data/test/test_callback.rb +31 -0
- data/test/test_configuration.rb +40 -0
- data/test/test_connection.rb +82 -0
- data/test/test_connection_per_class.rb +112 -0
- data/test/test_connection_per_dn.rb +112 -0
- data/test/test_dirty.rb +98 -0
- data/test/test_dn.rb +172 -0
- data/test/test_find.rb +176 -0
- data/test/test_groupadd.rb +50 -0
- data/test/test_groupdel.rb +46 -0
- data/test/test_groupls.rb +107 -0
- data/test/test_groupmod.rb +51 -0
- data/test/test_ldif.rb +1890 -0
- data/test/test_load.rb +133 -0
- data/test/test_lpasswd.rb +75 -0
- data/test/test_object_class.rb +74 -0
- data/test/test_persistence.rb +131 -0
- data/test/test_reflection.rb +175 -0
- data/test/test_schema.rb +559 -0
- data/test/test_syntax.rb +444 -0
- data/test/test_user.rb +217 -0
- data/test/test_user_password.rb +108 -0
- data/test/test_useradd-binary.rb +62 -0
- data/test/test_useradd.rb +57 -0
- data/test/test_userdel.rb +48 -0
- data/test/test_userls.rb +91 -0
- data/test/test_usermod-binary-add-time.rb +65 -0
- data/test/test_usermod-binary-add.rb +64 -0
- data/test/test_usermod-binary-del.rb +66 -0
- data/test/test_usermod-lang-add.rb +59 -0
- data/test/test_usermod.rb +58 -0
- data/test/test_validation.rb +274 -0
- 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,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
|