dm-ldap-adapter 0.3.5 → 0.4.0.alpha2

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.
@@ -1,19 +1,21 @@
1
- require 'adapters/simple_adapter'
1
+ require "dm-core"
2
+ require 'slf4r'
3
+ # require 'adapters/noop_transaction'
2
4
 
3
5
  module Ldap
4
6
 
5
7
  # the class provides two ways of getting a LdapFacade. either
6
8
  # one which is put on the current Thread or a new one
7
9
  class LdapConnection
8
-
10
+
9
11
  include ::Slf4r::Logger
10
12
 
11
- def initialize(uri)
12
- if uri[:facade].nil?
13
+ def initialize(options)
14
+ if options[:facade].nil?
13
15
  require 'ldap/net_ldap_facade'
14
16
  @facade = ::Ldap::NetLdapFacade
15
17
  else
16
- case uri[:facade].to_sym
18
+ case options[:facade].to_sym
17
19
  when :ruby_ldap
18
20
  require 'ldap/ruby_ldap_facade'
19
21
  @facade = ::Ldap::RubyLdapFacade
@@ -26,19 +28,19 @@ module Ldap
26
28
  end
27
29
  logger.info("using #{@facade}")
28
30
  @ldaps = { }
29
- auth = {
31
+ auth = {
30
32
  :method => :simple,
31
- :username => uri[:bind_name],
32
- :password => uri[:password]
33
- }
34
- @config = {
35
- :host => uri[:host],
36
- :port => uri[:port].to_i,
37
- :auth => auth,
38
- :base => uri[:base]
33
+ :username => options[:bind_name],
34
+ :password => options[:password]
35
+ }
36
+ @config = {
37
+ :host => options[:host],
38
+ :port => options[:port].to_i,
39
+ :auth => auth,
40
+ :base => options[:base]
39
41
  }
40
42
  end
41
-
43
+
42
44
  # puts a LdapFacade into the current thread and executes the
43
45
  # given block.
44
46
  def open
@@ -48,10 +50,10 @@ module Ldap
48
50
  yield
49
51
  end
50
52
  ensure
51
- @ldaps[Thread.current] = nil
53
+ @ldaps[Thread.current] = nil
52
54
  end
53
55
  end
54
-
56
+
55
57
  # @return [Ldap::LdapFacade]
56
58
  # either the one from the current Thread or a new one
57
59
  def current
@@ -67,10 +69,32 @@ end
67
69
 
68
70
  require "dm-core"
69
71
  module DataMapper
72
+ class Query
73
+
74
+ class SortCaseInsensitive < Sort
75
+ def initialize(value, ascending = true)
76
+ if(value && value.is_a?(String))
77
+ super(value.upcase, ascending)
78
+ else
79
+ super
80
+ end
81
+ end
82
+ end
83
+
84
+ def sort_records_case_insensitive(records)
85
+ sort_order = order.map { |direction| [ direction.target, direction.operator == :asc ] }
86
+
87
+ records.sort_by do |record|
88
+ sort_order.map do |(property, ascending)|
89
+ SortCaseInsensitive.new(record_value(record, property), ascending)
90
+ end
91
+ end
92
+ end
93
+ end
70
94
  module Adapters
71
- class LdapAdapter < SimpleAdapter
95
+ class LdapAdapter < AbstractAdapter
72
96
 
73
- # @return [Ldap::LdapFacade]
97
+ # @return [Ldap::LdapFacade]
74
98
  # ready to use LdapFacade
75
99
  def ldap
76
100
  @ldap_connection.current
@@ -81,25 +105,25 @@ module DataMapper
81
105
  end
82
106
 
83
107
  def key_properties(resource)
84
- resource.send(:key_properties).first
108
+ resource.model.key.first
85
109
  end
86
110
 
87
111
  COMPARATORS = { "=" => :eql, ">=" => :gte, "<=" => :lte, "like" => :like }
88
112
 
89
113
  # helper to remove datamapper specific classes from the conditions
90
- # @param [Array] conditions
114
+ # @param [Array] conditions
91
115
  # array of tuples: (action, property, new value)
92
- # @return [Array]
116
+ # @return [Array]
93
117
  # tuples: (action, attribute name, new value)
94
118
  def to_ldap_conditions(query)
95
119
  conditions = query.conditions
96
120
  ldap_conditions = []
97
- conditions.each do |c|
98
- if c[0] == :raw
121
+ conditions.operands.each do |c|
122
+ if c.is_a? Array
99
123
  props = {}
100
124
  query.fields.each{ |f| props[f.name] = f.field}
101
125
  or_conditions = []
102
- c[1].split('or').each do |e|
126
+ c[0].split('or').each do |e|
103
127
  e.strip!
104
128
  match = e.match("=|<=|>=|like")
105
129
  or_conditions << [COMPARATORS[match.values_at(0)[0]],
@@ -108,23 +132,57 @@ module DataMapper
108
132
  end
109
133
  ldap_conditions << [:or_operator, or_conditions, nil]
110
134
  else
111
- ldap_conditions << [c[0], c[1].field, c[2]]
135
+ comparator = c.slug
136
+ case comparator
137
+ when :raw
138
+ when :not
139
+ # TODO proper recursion !!!
140
+ ldap_conditions << [comparator, c.operands.first.subject.field, c.operands.first.send(:dumped_value)]
141
+ when :in
142
+ ldap_conditions << [:eql, c.subject.field, c.send(:dumped_value)]
143
+ else
144
+ ldap_conditions << [comparator, c.subject.field, c.send(:dumped_value)]
112
145
  end
113
146
  end
147
+ end
114
148
  ldap_conditions
115
149
  end
116
150
 
151
+ include ::Slf4r::Logger
152
+
153
+ # @see AbstractAdapter
154
+ # def transaction_primitive
155
+ # NoopTransaction.new
156
+ # end
157
+
117
158
  public
118
159
 
119
- def initialize(name, uri_or_options)
120
- super(name, uri_or_options)
121
- @ldap_connection = ::Ldap::LdapConnection.new(@uri)
160
+ def initialize(name, options)
161
+ super(name, options)
162
+ @ldap_connection = ::Ldap::LdapConnection.new(@options)
163
+ end
164
+
165
+
166
+ def create(resources)
167
+ resources.select do |resource|
168
+
169
+ create_resource(resource)
170
+
171
+ end.size # just return the number of create resources
122
172
  end
123
173
 
174
+ def update(attributes, collection)
175
+ collection.each do |resource|
176
+ #puts "update"
177
+ #p resource
178
+ update_resource(resource, attributes)
179
+
180
+ end.size
181
+ end
124
182
  # @param [DataMapper::Resource] resource
125
183
  # to be created
126
184
  # @see SimpleAdapter#create_resource
127
- # @return [Fixnum]
185
+ # @return [Fixnum]
128
186
  # value for the primary key or nil
129
187
  def create_resource(resource)
130
188
  logger.debug { resource.inspect }
@@ -133,8 +191,8 @@ module DataMapper
133
191
  key = nil
134
192
  resource.send(:properties).each do |prop|
135
193
  value = prop.get!(resource)
136
- if prop.type == ::DataMapper::Types::LdapArray
137
- props[prop.field.to_sym] = value.to_s unless value.nil? or value.size == 0
194
+ if prop.class == ::Ldap::LdapArray
195
+ props[prop.field.to_sym] = value unless value.nil? or value.size == 0
138
196
  else
139
197
  props[prop.field.to_sym] = value.to_s unless value.nil?
140
198
  end
@@ -156,14 +214,14 @@ module DataMapper
156
214
  end
157
215
  logger.debug { "resource #{resource.inspect} key value: #{key_value.inspect}" + ", multivalue_field: " + resource.model.multivalue_field.to_s }
158
216
  if key_value and !key.nil?
159
- key.set!(resource, key_value.to_i)
217
+ key.set!(resource, key_value.to_i)
160
218
  resource
161
219
  elsif resource.model.multivalue_field
162
220
  multivalue_prop = resource.send(:properties).detect do |prop|
163
221
  prop.field.to_sym == resource.model.multivalue_field
164
222
  end
165
- update_resource(resource,
166
- { multivalue_prop =>
223
+ update_resource(resource,
224
+ { multivalue_prop =>
167
225
  resource.send(multivalue_prop.name.to_sym)})
168
226
  else
169
227
  nil
@@ -179,18 +237,21 @@ module DataMapper
179
237
  actions = []
180
238
  attributes.each do |property, value|
181
239
  field = property.field.to_sym #TODO sym needed or string ???
182
- if property.type == ::DataMapper::Types::LdapArray
183
- if resource.original_values[property.name].nil?
184
- actions << [:add, field, value.to_s]
240
+ if property.class == ::Ldap::LdapArray
241
+ value = property.load(value)
242
+ if resource.original_attributes[property].nil?
243
+ value.each do |v|
244
+ actions << [:add, field, v]
245
+ end
185
246
  else
186
247
  array_actions = []
187
- resource.original_values[property.name].each do |ov|
248
+ resource.original_attributes[property].each do |ov|
188
249
  unless value.member? ov
189
250
  actions << [:delete, field, ov.to_s]
190
251
  end
191
252
  end
192
253
  value.each do |v|
193
- unless resource.original_values[property.name].member? v
254
+ unless resource.original_attributes[property].member? v
194
255
  actions << [:add, field, v.to_s]
195
256
  end
196
257
  end
@@ -199,119 +260,93 @@ module DataMapper
199
260
  else
200
261
  if resource.model.multivalue_field == property.field.to_sym
201
262
  if value.nil?
202
- actions << [:delete, field, resource.original_values[property.name].to_s]
263
+ actions << [:delete, field, resource.original_attributes[property].to_s]
203
264
  else
204
265
  actions << [:add, field, value.to_s]
205
266
  end
206
267
  elsif value.nil?
207
268
  actions << [:delete, field, []]
208
- elsif resource.original_values[property.name].nil?
269
+ elsif resource.original_attributes[property].nil?
209
270
  actions << [:add, field, value.to_s]
210
271
  else
211
272
  actions << [:replace, field, value.to_s]
212
273
  end
213
274
  end
214
275
  end
215
- # puts "actions"
216
- # p actions
276
+ #puts "actions"
277
+ #p actions
217
278
  #puts
218
- ldap.update_object(resource.model.dn_prefix(resource),
219
- resource.model.treebase,
279
+ ldap.update_object(resource.model.dn_prefix(resource),
280
+ resource.model.treebase,
220
281
  actions)
221
282
  end
222
283
 
223
- # @param [DataMapper::Resource] resource
224
- # to be deleted
225
- # @see SimpleAdapter#delete_resource
226
- def delete_resource(resource)
227
- if resource.model.multivalue_field
228
- multivalue_prop = resource.send(:properties).detect do |prop|
229
- prop.field.to_sym == resource.model.multivalue_field
284
+ # @see AbstractAdapter#delete
285
+ def delete(collection)
286
+ collection.each do |resource|
287
+ if resource.model.multivalue_field
288
+ multivalue_prop = resource.send(:properties).detect do |prop|
289
+ prop.field.to_sym == resource.model.multivalue_field
290
+ end
291
+ # set the original value so update does the right thing
292
+ resource.send("#{multivalue_prop.name}=".to_sym, nil)
293
+ update_resource(resource,
294
+ { multivalue_prop => nil })
295
+ else
296
+ ldap.delete_object(resource.model.dn_prefix(resource),
297
+ resource.model.treebase)
230
298
  end
231
- # set the original value so update does the right thing
232
- resource.send("#{multivalue_prop.name}=".to_sym, nil)
233
- update_resource(resource,
234
- { multivalue_prop => nil })
235
- else
236
- ldap.delete_object(resource.model.dn_prefix(resource),
237
- resource.model.treebase)
238
299
  end
239
300
  end
240
-
241
- # @param [DataMapper::Query] query
242
- # the search criteria
243
- # @return [DataMapper::Resource]
244
- # the found resource or nil
245
- # @see SimpleAdapter#read_resource
246
- def read_resource(query)
247
- field_names = query.fields.collect {|f| f.field }
248
- result = ldap.read_objects(query.model.treebase,
249
- query.model.key.collect { |k| k.field},
250
- to_ldap_conditions(query),
251
- field_names)
252
- if query.model.multivalue_field
253
- resource = result.detect do |item|
254
- # run over all values of the multivalue field
255
- item[query.model.multivalue_field].any? do |value|
256
- values = query.fields.collect do |f|
257
- if query.model.multivalue_field == f.field.to_sym
258
- value
259
- else
260
- val = item[f.field.to_sym].first
261
- f.primitive == Integer ? val.to_i : val
262
- end
263
- end
264
- resource = query.model.load(values, query)
265
- return resource if filter_resource(resource, query.conditions)
266
- end
267
- end
268
- else
269
- values = result.first
270
- if values
271
- query.fields.collect do |f|
272
- val = values[f.field.to_sym]
273
- if f.type == DataMapper::Types::LdapArray
274
- val if val
275
- elsif val
276
- f.primitive == Integer ? val.first.to_i : val.first
277
- end
278
- end
301
+
302
+ # @see AbstractAdapter#read
303
+ def read(query)
304
+ result = []
305
+ resources = read_resources(query)
306
+ resources.each do |resource|
307
+ map = {}
308
+ query.fields.each_with_index do |property, idx|
309
+ map[property.field] = property.typecast(resource[idx])
279
310
  end
311
+ result << map
280
312
  end
313
+
314
+ #puts "read_many"
315
+ #p result.size
316
+ result = result.uniq if query.unique?
317
+ result = query.match_records(result) if query.model.multivalue_field
318
+ result = query.sort_records_case_insensitive(result)
319
+ result = query.limit_records(result)
320
+ result
281
321
  end
282
-
283
- # @param [DataMapper::Query] query
284
- # the search criteria
285
- # @return [Array<DataMapper::Resource]
286
- # the array of found resources
287
- # @see SimpleAdapter#read_resources
322
+
288
323
  def read_resources(query)
289
- order_by = query.order.first.property.field
324
+ order_by = query.order.first.target.field
290
325
  order_by_sym = order_by.to_sym
291
326
  field_names = query.fields.collect {|f| f.field }
292
- result = ldap.read_objects(query.model.treebase,
293
- query.model.key.collect { |k| k.field },
327
+ result = ldap.read_objects(query.model.treebase,
328
+ query.model.key.collect { |k| k.field },
294
329
  to_ldap_conditions(query),
295
- field_names, order_by).sort! do |u1, u2|
296
- value1 = u1[order_by_sym].first.upcase rescue ""
297
- value2 = u2[order_by_sym].first.upcase rescue ""
298
- value1 <=> value2
299
- end
330
+ field_names, order_by)
331
+ #.sort! do |u1, u2|
332
+ # value1 = u1[order_by_sym].first.upcase rescue ""
333
+ # value2 = u2[order_by_sym].first.upcase rescue ""
334
+ # value1 <=> value2
335
+ # end
300
336
  if query.model.multivalue_field
301
337
  props_result = []
302
338
  result.each do |props|
303
339
  # run over all values of the multivalue field
304
340
  (props[query.model.multivalue_field] || []).each do |value|
305
341
  values = query.fields.collect do |f|
306
- if query.model.multivalue_field == f.field.to_sym
342
+ if query.model.multivalue_field == f.field.to_sym
307
343
  value
308
- else
344
+ else
309
345
  prop = props[f.field.to_sym].first
310
346
  f.primitive == Integer ? prop.to_i : prop
311
347
  end
312
348
  end
313
- resource = query.model.load(values, query)
314
- props_result << resource if filter_resource(resource, query.conditions)
349
+ props_result << values
315
350
  end
316
351
  end
317
352
  props_result
@@ -319,7 +354,7 @@ module DataMapper
319
354
  result.collect do |props|
320
355
  query.fields.collect do |f|
321
356
  prop = props[f.field.to_sym]
322
- if f.type == DataMapper::Types::LdapArray
357
+ if f.class == Ldap::LdapArray
323
358
  prop if prop
324
359
  elsif prop
325
360
  f.primitive == Integer ? prop.first.to_i : prop.first
@@ -328,6 +363,8 @@ module DataMapper
328
363
  end
329
364
  end
330
365
  end
366
+
367
+ # include ::DataMapper::Transaction::Adapter
331
368
  end
332
369
  end
333
370
  end
@@ -0,0 +1,16 @@
1
+ require "dm-core"
2
+
3
+ module DataMapper
4
+ module Adapters
5
+ class NoopTransaction
6
+
7
+ def close ; end
8
+ def begin ; end
9
+ def prepare ; end
10
+ def commit ; end
11
+ def rollback ; end
12
+ def rollback_prepared ; end
13
+
14
+ end
15
+ end
16
+ end
@@ -4,9 +4,9 @@ require 'ldap/digest'
4
4
  # dummy implementation which turns the extra ldap configuration noops
5
5
  module DataMapper
6
6
  module Resource
7
-
7
+
8
8
  module ClassMethods
9
-
9
+
10
10
  include ::Slf4r::Logger
11
11
 
12
12
  def ldap_properties(resource = nil, &block)
@@ -21,7 +21,7 @@ module DataMapper
21
21
  logger.debug { "ldap_properties=#{@ldap_properties.inspect}" }
22
22
  end
23
23
  end
24
-
24
+
25
25
  def treebase(resource = nil, &block)
26
26
  if block
27
27
  @treebase = block
@@ -34,7 +34,7 @@ module DataMapper
34
34
  logger.debug { "treebase=#{@treebase}" }
35
35
  end
36
36
  end
37
-
37
+
38
38
  def dn_prefix(resource = nil, &block)
39
39
  if block
40
40
  @dn_prefix = block
@@ -47,12 +47,12 @@ module DataMapper
47
47
  logger.debug { "dn_prefix=#{dn_prefix}" }
48
48
  end
49
49
  end
50
-
50
+
51
51
  def multivalue_field(field = nil)
52
52
  logger.debug { "multivalue_field = #{field}" } if field
53
53
  end
54
54
  end
55
-
55
+
56
56
  def authenticate(password)
57
57
  raise "NotImplemented"
58
58
  end
data/lib/ldap/array.rb CHANGED
@@ -1,18 +1,102 @@
1
- module DataMapper
2
- module Types
3
- class LdapArray < DataMapper::Type
4
- primitive Array
5
- default Proc.new { Array.new }
1
+ require 'dm-core'
2
+ module Ldap
3
+ class Array < ::Array
6
4
 
7
- def self.dump(value, property)
8
- value || []
9
- end
5
+ def initialize(resource, property, *args)
6
+ setup(resource, property)
7
+ super(args)
8
+ end
9
+
10
+ def setup(resource, property)
11
+ @resource = resource
12
+ @property = property
13
+ self
14
+ end
15
+
16
+ alias :push! :push
17
+
18
+ def []=(k, v)
19
+ result = super
20
+ @resource.send("#{@property.name}=".to_sym, self)
21
+ result
22
+ end
23
+
24
+
25
+ def <<(element)
26
+ push(element)
27
+ end
28
+
29
+ def push(element)
30
+ result = super
31
+ @resource.send("#{@property.name}=".to_sym, self)
32
+ result
33
+ end
34
+
35
+ alias :delete! :delete
36
+
37
+ def delete(element)
38
+ result = super
39
+ @resource.send(:"#{@property.name}=", self)
40
+ result
41
+ end
42
+ end
43
+
44
+ class LdapArray < ::DataMapper::Property::Text
45
+
46
+ default Proc.new { |r,p| Ldap::Array.new(r,p) }
47
+
48
+ def custom?
49
+ true
50
+ end
51
+
52
+ def primitive?(value)
53
+ super || value.kind_of?(::Array)
54
+ end
55
+
56
+ def load(value)
57
+ result = case value
58
+ when ::String then value.gsub(/^.|.$/,'').split('","').to_a.freeze
59
+ when ::Array then value.freeze
60
+ else
61
+ []
62
+ end
63
+ end
64
+
65
+ def dump(value)
66
+ result = case value
67
+ when LdapArray then '"' + value.join('","') + '"'
68
+ when ::Array then '"' + value.join('","') + '"'
69
+ when ::String then '"' + value.to_s + '"'
70
+ else
71
+ nil
72
+ end
73
+ end
10
74
 
11
- def self.load(value, property)
12
- value || []
13
- end
75
+ def initialize(*args)
76
+ super
77
+ model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
78
+ def #{name.to_s}=(v)
79
+ case v
80
+ when Ldap::Array
81
+ v.setup(self, properties[:#{name}])
82
+ else
83
+ vv = Ldap::Array.new(self, properties[:#{name}])
84
+ vv.replace(v)
85
+ end
86
+ attribute_set(:#{name}, v)
87
+ end
14
88
 
89
+ def #{name.to_s}
90
+ v = attribute_get(:#{name})
91
+ case v
92
+ when Ldap::Array
93
+ v.setup(self, properties[:#{name}])
94
+ else
95
+ vv = Ldap::Array.new(self, properties[:#{name}])
96
+ vv.replace(v)
97
+ end
98
+ end
99
+ RUBY
15
100
  end
16
101
  end
17
- Property::TYPES << Types::LdapArray unless Property::TYPES.member? Types::LdapArray
18
102
  end