ruby-activeldap 0.7.4 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/CHANGES +375 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +58 -0
  4. data/Manifest.txt +33 -0
  5. data/README +63 -0
  6. data/Rakefile +37 -0
  7. data/TODO +31 -0
  8. data/benchmark/bench-al.rb +152 -0
  9. data/lib/{activeldap.rb → active_ldap.rb} +280 -263
  10. data/lib/active_ldap/adaptor/base.rb +29 -0
  11. data/lib/active_ldap/adaptor/ldap.rb +466 -0
  12. data/lib/active_ldap/association/belongs_to.rb +38 -0
  13. data/lib/active_ldap/association/belongs_to_many.rb +40 -0
  14. data/lib/active_ldap/association/collection.rb +80 -0
  15. data/lib/active_ldap/association/has_many.rb +48 -0
  16. data/lib/active_ldap/association/has_many_wrap.rb +56 -0
  17. data/lib/active_ldap/association/proxy.rb +89 -0
  18. data/lib/active_ldap/associations.rb +162 -0
  19. data/lib/active_ldap/attributes.rb +199 -0
  20. data/lib/active_ldap/base.rb +1343 -0
  21. data/lib/active_ldap/callbacks.rb +19 -0
  22. data/lib/active_ldap/command.rb +46 -0
  23. data/lib/active_ldap/configuration.rb +96 -0
  24. data/lib/active_ldap/connection.rb +137 -0
  25. data/lib/{activeldap → active_ldap}/ldap.rb +1 -1
  26. data/lib/active_ldap/object_class.rb +70 -0
  27. data/lib/active_ldap/schema.rb +258 -0
  28. data/lib/{activeldap → active_ldap}/timeout.rb +0 -0
  29. data/lib/{activeldap → active_ldap}/timeout_stub.rb +0 -0
  30. data/lib/active_ldap/user_password.rb +92 -0
  31. data/lib/active_ldap/validations.rb +78 -0
  32. data/rails/plugin/active_ldap/README +54 -0
  33. data/rails/plugin/active_ldap/init.rb +6 -0
  34. data/test/TODO +2 -0
  35. data/test/al-test-utils.rb +337 -0
  36. data/test/command.rb +62 -0
  37. data/test/config.yaml +8 -0
  38. data/test/config.yaml.sample +6 -0
  39. data/test/run-test.rb +17 -0
  40. data/test/test-unit-ext.rb +2 -0
  41. data/test/test_associations.rb +334 -0
  42. data/test/test_attributes.rb +71 -0
  43. data/test/test_base.rb +345 -0
  44. data/test/test_base_per_instance.rb +32 -0
  45. data/test/test_bind.rb +53 -0
  46. data/test/test_callback.rb +35 -0
  47. data/test/test_connection.rb +38 -0
  48. data/test/test_connection_per_class.rb +50 -0
  49. data/test/test_find.rb +36 -0
  50. data/test/test_groupadd.rb +50 -0
  51. data/test/test_groupdel.rb +46 -0
  52. data/test/test_groupls.rb +107 -0
  53. data/test/test_groupmod.rb +51 -0
  54. data/test/test_lpasswd.rb +75 -0
  55. data/test/test_object_class.rb +32 -0
  56. data/test/test_reflection.rb +173 -0
  57. data/test/test_schema.rb +166 -0
  58. data/test/test_user.rb +209 -0
  59. data/test/test_user_password.rb +93 -0
  60. data/test/test_useradd-binary.rb +59 -0
  61. data/test/test_useradd.rb +55 -0
  62. data/test/test_userdel.rb +48 -0
  63. data/test/test_userls.rb +86 -0
  64. data/test/test_usermod-binary-add-time.rb +62 -0
  65. data/test/test_usermod-binary-add.rb +61 -0
  66. data/test/test_usermod-binary-del.rb +64 -0
  67. data/test/test_usermod-lang-add.rb +57 -0
  68. data/test/test_usermod.rb +56 -0
  69. data/test/test_validation.rb +38 -0
  70. metadata +94 -21
  71. data/lib/activeldap/associations.rb +0 -170
  72. data/lib/activeldap/base.rb +0 -1456
  73. data/lib/activeldap/configuration.rb +0 -59
  74. data/lib/activeldap/schema2.rb +0 -217
@@ -0,0 +1,38 @@
1
+ require 'active_ldap/association/proxy'
2
+
3
+ module ActiveLdap
4
+ module Association
5
+ class BelongsTo < Proxy
6
+ def replace(entry)
7
+ if entry.nil?
8
+ @target = @owner[@options[:foreign_key_name]] = nil
9
+ else
10
+ @target = (Proxy === entry ? entry.target : entry)
11
+ unless entry.new_entry?
12
+ @owner[@options[:foreign_key_name]] = entry[primary_key]
13
+ end
14
+ @updated = true
15
+ end
16
+
17
+ loaded
18
+ entry
19
+ end
20
+
21
+ def updated?
22
+ @updated
23
+ end
24
+
25
+ private
26
+ def have_foreign_key?
27
+ not @owner[@options[:foreign_key_name]].nil?
28
+ end
29
+
30
+ def find_target
31
+ filter = "(#{primary_key}=#{@owner[@options[:foreign_key_name]]})"
32
+ result = foreign_class.find(:all, :filter => filter, :limit => 1)
33
+ raise EntryNotFound if result.empty?
34
+ result.first
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ require 'active_ldap/association/collection'
2
+
3
+ module ActiveLdap
4
+ module Association
5
+ class BelongsToMany < Collection
6
+ private
7
+ def insert_entry(entry)
8
+ old_value = entry[@options[:many], true]
9
+ new_value = old_value + @owner[@options[:foreign_key_name], true]
10
+ new_value = new_value.uniq.sort
11
+ if old_value != new_value
12
+ entry[@options[:many]] = new_value
13
+ entry.save
14
+ end
15
+ end
16
+
17
+ def delete_entries(entries)
18
+ entries.each do |entry|
19
+ old_value = entry[@options[:many], true]
20
+ new_value = old_value - @owner[@options[:foreign_key_name], true]
21
+ new_value = new_value.uniq.sort
22
+ if old_value != new_value
23
+ entry[@options[:many]] = new_value
24
+ entry.save
25
+ end
26
+ end
27
+ end
28
+
29
+ def find_target
30
+ key = @options[:many]
31
+ filter = @owner[@options[:foreign_key_name], true].reject do |value|
32
+ value.nil?
33
+ end.collect do |value|
34
+ "(#{key}=#{value})"
35
+ end.join
36
+ foreign_class.find(:all, :filter => "(|#{filter})")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,80 @@
1
+ require 'active_ldap/association/proxy'
2
+
3
+ module ActiveLdap
4
+ module Association
5
+ class Collection < Proxy
6
+ include Enumerable
7
+
8
+ def to_ary
9
+ load_target
10
+ @target.to_ary
11
+ end
12
+
13
+ def reset
14
+ @target = []
15
+ @loaded = false
16
+ end
17
+
18
+ def <<(*entries)
19
+ add_entries(*entries)
20
+ end
21
+ alias_method(:push, :<<)
22
+ alias_method(:concat, :<<)
23
+
24
+ def each(&block)
25
+ to_ary.each(&block)
26
+ end
27
+
28
+ def delete(*entries)
29
+ entries = flatten_deeper(entries).reject do |entry|
30
+ @target.delete(entry) if entry.new_entry?
31
+ entry.new_entry?
32
+ end
33
+ return if entries.empty?
34
+
35
+ delete_entries(entries)
36
+ entries.each do |entry|
37
+ @target.delete(entry)
38
+ end
39
+ end
40
+
41
+ def replace(others)
42
+ load_target
43
+ deleted_entries = @target - others
44
+ added_entries = others - @target
45
+
46
+ delete(deleted_entries)
47
+ concat(added_entries)
48
+ end
49
+
50
+ def exists?
51
+ load_target
52
+ not @target.empty?
53
+ end
54
+
55
+ private
56
+ def flatten_deeper(array)
57
+ array.collect do |element|
58
+ element.respond_to?(:flatten) ? element.flatten : element
59
+ end.flatten
60
+ end
61
+
62
+ def insert_entry(entry)
63
+ entry[@options[:foreign_key_name]] = @owner[@options[:local_key_name]]
64
+ entry.save
65
+ end
66
+
67
+ def add_entries(*entries)
68
+ result = true
69
+ load_target
70
+
71
+ flatten_deeper(entries).each do |entry|
72
+ result &&= insert_entry(entry) unless @owner.new_entry?
73
+ @target << entry
74
+ end
75
+
76
+ result && self
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,48 @@
1
+ require 'active_ldap/association/collection'
2
+
3
+ module ActiveLdap
4
+ module Association
5
+ class HasMany < Collection
6
+ private
7
+ def insert_entry(entry)
8
+ entry[primary_key] = @owner[@options[:foreign_key_name]]
9
+ entry.save
10
+ end
11
+
12
+ def find_target
13
+ foreign_base_key = primary_key
14
+ filter = @owner[@options[:foreign_key_name], true].collect do |value|
15
+ key = val = nil
16
+ if foreign_base_key == "dn"
17
+ key, val = value.split(",")[0].split("=") unless value.empty?
18
+ else
19
+ key, val = foreign_base_key, value
20
+ end
21
+ [key, val]
22
+ end.reject do |key, val|
23
+ key.nil? or val.nil?
24
+ end.collect do |key, val|
25
+ "(#{key}=#{val})"
26
+ end.join
27
+ foreign_class.find(:all, :filter => "(|#{filter})")
28
+ end
29
+
30
+ def delete_entries(entries)
31
+ key = primary_key
32
+ dn_attribute = foreign_class.dn_attribute
33
+ filter = @owner[@options[:foreign_key_name], true].reject do |value|
34
+ value.nil?
35
+ end.collect do |value|
36
+ "(#{key}=#{value})"
37
+ end.join
38
+ filter = "(&#{filter})"
39
+ entry_filter = entries.collect do |entry|
40
+ "(#{dn_attribute}=#{entry.id})"
41
+ end.join
42
+ entry_filter = "(|#{entry_filter})"
43
+ foreign_class.update_all({primary_key => []},
44
+ "(&#{filter}#{entry_filter})")
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,56 @@
1
+ require 'active_ldap/association/collection'
2
+
3
+ module ActiveLdap
4
+ module Association
5
+ class HasManyWrap < Collection
6
+ private
7
+ def insert_entry(entry)
8
+ old_value = @owner[@options[:wrap], true]
9
+ new_value = (old_value + entry[primary_key, true]).uniq.sort
10
+ if old_value != new_value
11
+ @owner[@options[:wrap]] = new_value
12
+ @owner.save
13
+ end
14
+ end
15
+
16
+ def delete_entries(entries)
17
+ old_value = @owner[@options[:wrap], true]
18
+ new_value = old_value - entries.collect {|entry| entry[primary_key]}
19
+ new_value = new_value.uniq.sort
20
+ if old_value != new_value
21
+ @owner[@options[:wrap]] = new_value
22
+ @owner.save
23
+ end
24
+ end
25
+
26
+ def find_target
27
+ foreign_base_key = primary_key
28
+ requested_targets = @owner[@options[:wrap], true]
29
+
30
+ filter = requested_targets.collect do |value|
31
+ key = val = nil
32
+ if foreign_base_key == "dn"
33
+ key, val = value.split(",")[0].split("=") unless value.empty?
34
+ else
35
+ key, val = foreign_base_key, value
36
+ end
37
+ [key, val]
38
+ end.reject do |key, val|
39
+ key.nil? or val.nil?
40
+ end.collect do |key, val|
41
+ "(#{key}=#{val})"
42
+ end.join
43
+
44
+ klass = foreign_class
45
+ found_targets = {}
46
+ klass.find(:all, :filter => "(|#{filter})").each do |target|
47
+ found_targets[target.send(foreign_base_key)] ||= target
48
+ end
49
+
50
+ requested_targets.collect do |name|
51
+ found_targets[name] || klass.new(name)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,89 @@
1
+ module ActiveLdap
2
+ module Association
3
+ class Proxy
4
+ alias_method :proxy_respond_to?, :respond_to?
5
+ alias_method :proxy_extend, :extend
6
+
7
+ def initialize(owner, options)
8
+ @owner = owner
9
+ @options = options
10
+ extend(options[:extend]) if options[:extend]
11
+ reset
12
+ end
13
+
14
+ def respond_to?(symbol, include_priv=false)
15
+ proxy_respond_to?(symbol, include_priv) or
16
+ (load_target && @target.respond_to?(symbol, include_priv))
17
+ end
18
+
19
+ def ===(other)
20
+ load_target and other === @target
21
+ end
22
+
23
+ def reset
24
+ @target = nil
25
+ @loaded = false
26
+ end
27
+
28
+ def reload
29
+ reset
30
+ load_target
31
+ end
32
+
33
+ def loaded?
34
+ @loaded
35
+ end
36
+
37
+ def loaded
38
+ @loaded = true
39
+ end
40
+
41
+ def target
42
+ @target
43
+ end
44
+
45
+ def target=(target)
46
+ @target = target
47
+ loaded
48
+ end
49
+
50
+ def exists?
51
+ load_target
52
+ not @target.nil?
53
+ end
54
+
55
+ private
56
+ def method_missing(method, *args, &block)
57
+ load_target
58
+ @target.send(method, *args, &block)
59
+ end
60
+
61
+ def foreign_class
62
+ klass = @owner.class.associated_class(@options[:association_id])
63
+ klass = @owner.class.module_eval(klass) if klass.is_a?(String)
64
+ klass
65
+ end
66
+
67
+ def have_foreign_key?
68
+ false
69
+ end
70
+
71
+ def primary_key
72
+ @options[:primary_key_name] || foreign_class.dn_attribute
73
+ end
74
+
75
+ def load_target
76
+ if !@owner.new_entry? or have_foreign_key?
77
+ begin
78
+ @target = find_target unless loaded?
79
+ rescue EntryNotFound
80
+ reset
81
+ end
82
+ end
83
+
84
+ loaded if target
85
+ target
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,162 @@
1
+ require 'active_ldap/association/belongs_to'
2
+ require 'active_ldap/association/belongs_to_many'
3
+ require 'active_ldap/association/has_many'
4
+ require 'active_ldap/association/has_many_wrap'
5
+
6
+ module ActiveLdap
7
+ # Associations
8
+ #
9
+ # Associations provides the class methods needed for
10
+ # the extension classes to create methods using
11
+ # belongs_to and has_many
12
+ module Associations
13
+ def self.append_features(base)
14
+ super
15
+ base.extend(ClassMethods)
16
+ end
17
+
18
+ module ClassMethods
19
+ def set_associated_class(name, klass)
20
+ @associated_classes ||= {}
21
+ @associated_classes[name.to_s] = klass
22
+ end
23
+
24
+ def associated_class(name)
25
+ @associated_classes[name.to_s]
26
+ end
27
+
28
+ # belongs_to
29
+ #
30
+ # This defines a method for an extension class map its DN key
31
+ # attribute value on to multiple items which reference it by
32
+ # |:foreign_key| in the other LDAP entry covered by class |:class_name|.
33
+ #
34
+ # Example:
35
+ # belongs_to :groups, :class_name => "Group",
36
+ # :many => "memberUid" # Group#memberUid
37
+ # # :foreign_key => "uid" # User#uid
38
+ # # dn attribute value is used by default
39
+ # belongs_to :primary_group, :class_name => "Group",
40
+ # :foreign_key => "gidNumber", # User#gidNumber
41
+ # :primary_key => "gidNumber" # Group#gidNumber
42
+ #
43
+ def belongs_to(association_id, options={})
44
+ validate_belongs_to_options(options)
45
+ klass = options[:class] || Inflector.classify(association_id)
46
+ foreign_key = options[:foreign_key]
47
+ primary_key = options[:primary_key]
48
+ many = options[:many]
49
+ set_associated_class(association_id, klass)
50
+
51
+ opts = {
52
+ :association_id => association_id,
53
+ :foreign_key_name => foreign_key,
54
+ :primary_key_name => primary_key,
55
+ :many => many,
56
+ :extend => options[:extend],
57
+ }
58
+ if opts[:many]
59
+ association_class = Association::BelongsToMany
60
+ opts[:foreign_key_name] ||= dn_attribute
61
+ else
62
+ association_class = Association::BelongsTo
63
+ opts[:foreign_key_name] ||= "#{association_id}_id"
64
+
65
+ before_save(<<-EOC)
66
+ if defined?(@#{association_id})
67
+ association = @#{association_id}
68
+ if association and association.updated?
69
+ self[association.__send__(:primary_key)] =
70
+ association[#{opts[:foreign_key_name].dump}]
71
+ end
72
+ end
73
+ EOC
74
+ end
75
+
76
+ association_accessor(association_id) do |target|
77
+ association_class.new(target, opts)
78
+ end
79
+ end
80
+
81
+
82
+ # has_many
83
+ #
84
+ # This defines a method for an extension class expand an
85
+ # existing multi-element attribute into ActiveLdap objects.
86
+ # This discards any calls which result in entries that
87
+ # don't exist in LDAP!
88
+ #
89
+ # Example:
90
+ # has_many :primary_members, :class_name => "User",
91
+ # :primary_key => "gidNumber", # User#gidNumber
92
+ # :foreign_key => "gidNumber" # Group#gidNumber
93
+ # has_many :members, :class_name => "User",
94
+ # :wrap => "memberUid" # Group#memberUid
95
+ def has_many(association_id, options = {})
96
+ validate_has_many_options(options)
97
+ klass = options[:class] || Inflector.classify(association_id)
98
+ foreign_key = options[:foreign_key] || association_id.to_s + "_id"
99
+ primary_key = options[:primary_key]
100
+ set_associated_class(association_id, klass)
101
+
102
+ opts = {
103
+ :association_id => association_id,
104
+ :foreign_key_name => foreign_key,
105
+ :primary_key_name => primary_key,
106
+ :wrap => options[:wrap],
107
+ :extend => options[:extend],
108
+ }
109
+ if opts[:wrap]
110
+ association_class = Association::HasManyWrap
111
+ else
112
+ association_class = Association::HasMany
113
+ end
114
+
115
+ association_accessor(association_id) do |target|
116
+ association_class.new(target, opts)
117
+ end
118
+ end
119
+
120
+ private
121
+ def association_accessor(name, &make_association)
122
+ define_method("__make_#{name}") do
123
+ make_association.call(self)
124
+ end
125
+ association_reader(name, &make_association)
126
+ association_writer(name, &make_association)
127
+ end
128
+
129
+ def association_reader(name, &make_association)
130
+ class_eval(<<-EOM, __FILE__, __LINE__ + 1)
131
+ def #{name}
132
+ @#{name} ||= __make_#{name}
133
+ end
134
+ EOM
135
+ end
136
+
137
+ def association_writer(name, &make_association)
138
+ class_eval(<<-EOM, __FILE__, __LINE__ + 1)
139
+ def #{name}=(new_value)
140
+ association = defined?(@#{name}) ? @#{name} : nil
141
+ association ||= __make_#{name}
142
+ association.replace(new_value)
143
+ @#{name} = new_value.nil? ? nil : association
144
+ @#{name}
145
+ end
146
+ EOM
147
+ end
148
+
149
+ VALID_BELONGS_TO_OPTIONS = [:class, :foreign_key, :primary_key, :many,
150
+ :extend]
151
+ def validate_belongs_to_options(options)
152
+ options.assert_valid_keys(VALID_BELONGS_TO_OPTIONS)
153
+ end
154
+
155
+ VALID_HAS_MANY_OPTIONS = [:class, :foreign_key, :primary_key, :wrap,
156
+ :extend]
157
+ def validate_has_many_options(options)
158
+ options.assert_valid_keys(VALID_HAS_MANY_OPTIONS)
159
+ end
160
+ end
161
+ end
162
+ end