lom 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 383b6cdf0a55888e40558ca5b24f46a7f5e5d033f27fc873e4625c42b385c571
4
- data.tar.gz: cef1eef2f7ee9b2b39118e9f54b2d15f1ba5d35518ed04e98cb09a047cbbb34e
3
+ metadata.gz: 6d37a56b9ef6c9dad3ae785439cb49560c641304fdb12f3d3d070541e8bf27b9
4
+ data.tar.gz: cd71e20fd089ab2b489bcdb2f857f508b8c283c0d4f2de875b81cfc722fab05b
5
5
  SHA512:
6
- metadata.gz: dd9eab41714ae145477e2756300b0041dd2d0a12f90485a3997e95266bbb121a33e8a3ed3660d8fd14e7372fd0cfa0fca6da6f800f61599a1c995da221b67b4f
7
- data.tar.gz: f435226cb86088cfa48db588b105396b79fc0172eec4a967168b507dffb1039f201f6d7261d371edf63883c7fe519ca0d6167c649259f282a7d38e592ee929e1
6
+ metadata.gz: 956540737ca9944e790aeead959ef079f61f8d9469b22867969858c993bd97897f742fd9ca43ef3b99110e76e81902474f88fd6a95e0bd216fee0b620ca5377e
7
+ data.tar.gz: 7340668aaeef6a7821e88d95bf5da772aedfcb4a3dce9ab89d858c08c2b4be9165e127f0c5871decce112edc06337ba0c7e611601d12b56a6cbfe5b52e490202
data/README.md CHANGED
@@ -104,7 +104,13 @@ class User < Dry::Struct
104
104
  }
105
105
  end
106
106
 
107
+ ldap_list :query, ->(str) do
108
+ Filtered.match(:uid, str) |
109
+ Filtered.match(:cn, str) |
110
+ Filtered.match(:givenName, str) | Filtered.match(:sn, str)
111
+ end
107
112
 
113
+
108
114
  #
109
115
  # Object structure
110
116
  #
data/lib/lom/filtered.rb CHANGED
@@ -13,10 +13,19 @@ class Filtered
13
13
  NONE = Object.new.freeze
14
14
  ANY = Object.new.freeze
15
15
 
16
- def initialize(src, filter = nil, paged: nil)
17
- @src = src
18
- @filter = filter
19
- @paged = paged
16
+ class NoSource < Error
17
+ end
18
+
19
+ def initialize(filter = nil, src: nil, paged: nil)
20
+ if filter.is_a?(Filtered)
21
+ @filter = filter.filter
22
+ @src = src || filter.src
23
+ @paged = paged || filter.paged
24
+ else
25
+ @filter = filter
26
+ @src = src
27
+ @paged = paged
28
+ end
20
29
  end
21
30
  attr_reader :src, :filter, :paged
22
31
 
@@ -30,7 +39,7 @@ class Filtered
30
39
  _operator_2('&', o)
31
40
  end
32
41
 
33
- # Take the negation of this fileter
42
+ # Take the negation of this filter
34
43
  def ~@
35
44
  _operator_1('!')
36
45
  end
@@ -51,24 +60,26 @@ class Filtered
51
60
  end
52
61
 
53
62
  # Iterate over matching data
54
- def each(*args, &block)
55
- @src.each(*args, filter: @filter, paged: self.paged, &block)
63
+ def each(*args, rawfilter: nil, &block)
64
+ raise NoSource if @src.nil?
65
+ @src.each(*args, filter: @filter, rawfilter: rawfilter,
66
+ paged: self.paged, &block)
56
67
  end
57
68
 
58
69
  # Retrieve matching data as a list of object
59
70
  #
60
71
  # @return [Array<Object>]
61
72
  #
62
- def all
63
- each(:object).to_a
73
+ def all(&rawfilter)
74
+ each(:object, rawfilter: rawfilter).to_a
64
75
  end
65
76
 
66
77
  # Retrieve matching data as a list of id
67
78
  #
68
79
  # @return [Array<String>]
69
80
  #
70
- def list
71
- each(:id).to_a
81
+ def list(&rawfilter)
82
+ each(:id, rawfilter: rawfilter).to_a
72
83
  end
73
84
 
74
85
  # Escape (and convert) a value for correct processing.
@@ -89,20 +100,20 @@ class Filtered
89
100
 
90
101
  # Test if an attribute exists
91
102
  def self.exists(attr, predicate: true)
92
- case predicate
93
- when true, nil then "(#{attr}=*)"
94
- when false, :none then "(!(#{attr}=*))"
95
- else raise ArgumentError
96
- end
103
+ self.new(case predicate
104
+ when true, nil then "(#{attr}=*)"
105
+ when false, :none then "(!(#{attr}=*))"
106
+ else raise ArgumentError
107
+ end)
97
108
  end
98
109
 
99
110
  # Test if an attribute is of the specified value
100
111
  def self.is(attr, val, predicate: true)
101
- case predicate
102
- when true, nil then "(#{attr}=#{escape(val)})"
103
- when false then "(!(#{attr}=#{escape(val)}))"
104
- else raise ArgumentError
105
- end
112
+ self.new(case predicate
113
+ when true, nil then "(#{attr}=#{escape(val)})"
114
+ when false then "(!(#{attr}=#{escape(val)}))"
115
+ else raise ArgumentError
116
+ end)
106
117
  end
107
118
 
108
119
  # Test if an attribute has the specified value.
@@ -110,11 +121,20 @@ class Filtered
110
121
  def self.has(attr, val)
111
122
  val = yield(val) if block_given?
112
123
 
113
- case val
114
- when ANY then "(#{attr}=*)"
115
- when NONE then "(!(#{attr}=*))"
116
- else "(#{attr}=#{escape(val)})"
117
- end
124
+ self.new(case val
125
+ when ANY then "(#{attr}=*)"
126
+ when NONE then "(!(#{attr}=*))"
127
+ else "(#{attr}=#{escape(val)})"
128
+ end)
129
+ end
130
+
131
+ # Test if an attribute match the specified value
132
+ def self.match(attr, val, predicate: true)
133
+ self.new(case predicate
134
+ when true, nil then "(#{attr}=*#{escape(val)}*)"
135
+ when false then "(!(#{attr}=*#{escape(val)}*))"
136
+ else raise ArgumentError
137
+ end)
118
138
  end
119
139
 
120
140
  # Test if an attribute as a time before the specified timestamp
@@ -122,7 +142,7 @@ class Filtered
122
142
  def self.before(attr, ts, predicate: true)
123
143
  ts = Date.today + ts if ts.kind_of?(Integer)
124
144
  ts = LOM.to_ldap_time(ts)
125
- "(#{attr}<=#{ts})".then {|f| predicate ? f : "(!#{f})" }
145
+ self.new("(#{attr}<=#{ts})".then {|f| predicate ? f : "(!#{f})" })
126
146
  end
127
147
 
128
148
  # Test if an attribute as a time after the specified timestamp
@@ -130,40 +150,40 @@ class Filtered
130
150
  def self.after(attr, ts, predicate: true)
131
151
  ts = Date.today - ts if ts.kind_of?(Integer)
132
152
  ts = LOM.to_ldap_time(ts)
133
- "(#{attr}>=#{ts})".then {|f| predicate ? f : "(!#{f})" }
153
+ self.new("(#{attr}>=#{ts})".then {|f| predicate ? f : "(!#{f})" })
134
154
  end
135
155
 
136
156
  private
137
157
 
138
158
  # Operation with 2 elements
139
159
  def _operator_2(op, o)
140
- if @src != o.src
160
+ if !@src.nil? && !o.src.nil? && @src != o.src
141
161
  raise ArgumentError, 'filter defined with different sources'
142
162
  end
143
163
  _filter = if !@filter.nil? && !o.filter.nil?
144
164
  then Net::LDAP.filter(op, @filter, o.filter)
145
165
  else @filter || o.filter
146
166
  end
147
- Filtered.new(@src, _filter, paged: o.paged || self.paged)
167
+ Filtered.new(_filter, src: @src || o.src, paged: o.paged || self.paged)
148
168
  end
149
169
 
150
170
  # Operation with 1 element
151
171
  def _operator_1(op)
152
- Filtered.new(@src, Net::LDAP.filter(op, @filter),
172
+ Filtered.new(Net::LDAP.filter(op, @filter), src: @src,
153
173
  paged: self.paged)
154
174
  end
155
175
 
156
176
  # Check if an ldap_list has been defined with that name
157
177
  def respond_to_missing?(method_name, include_private = false)
178
+ return super if @src.nil?
158
179
  @src.ldap_listing.include?(method_name) || super
159
180
  end
160
181
 
161
182
  # Call the ldap_list defined with that name
162
183
  def method_missing(method_name, *args, &block)
163
- if @src.ldap_listing.include?(method_name)
164
- self & @src.send(method_name, *args, &block)
165
- else
166
- super
184
+ if @src&.ldap_listing.include?(method_name)
185
+ then self & @src.send(method_name, *args, &block)
186
+ else super
167
187
  end
168
188
  end
169
189
 
@@ -188,7 +188,7 @@ module Extensions
188
188
  if LOM.debug.include?(:dry)
189
189
  return true
190
190
  end
191
- return true if op.empty? # That's a no-op
191
+ return true if ops.empty? # That's a no-op
192
192
  modify(:dn => dn, :operations => ops) # Apply modifications
193
193
  end
194
194
  end
data/lib/lom/mapper.rb CHANGED
@@ -37,7 +37,8 @@ module InstanceMethods
37
37
  # @return [true, false]
38
38
  #
39
39
  def save!
40
- attrs = instance_exec(self, &self.class._ldap_to)
40
+ model = self.class
41
+ attrs = instance_exec(self, &model.ldap_to)
41
42
  .transform_values {|v|
42
43
  # Don't use Array(), not what you think on
43
44
  # some classes such as Time
@@ -45,9 +46,9 @@ module InstanceMethods
45
46
  v = [ v ] unless v.is_a?(Array)
46
47
  v.to_ldap
47
48
  }
48
- id, _ = Array(attrs[self.class._ldap_prefix])
49
+ id, _ = Array(attrs[model.ldap_prefix])
49
50
  raise MappingError, 'prefix for dn has multiple values' if _
50
- dn = self.class.ldap_dn_from_id(id)
51
+ dn = model.ldap_dn_from_id(id)
51
52
 
52
53
  lh.update(dn: dn, attributes: attrs).then {|res|
53
54
  break res unless res.nil?
@@ -101,7 +102,10 @@ module Mapper
101
102
  @__ldap_lh = lh
102
103
  end
103
104
 
104
-
105
+ # Return the list of defined list (using ldap_list).
106
+ #
107
+ # @return [Array<Symbol>] list of defined ldap list
108
+ #
105
109
  def ldap_listing
106
110
  @__ldap_list
107
111
  end
@@ -118,16 +122,18 @@ module Mapper
118
122
  @__ldap_list << name
119
123
  define_singleton_method(name) do |*args|
120
124
  filter = body.call(*args)
121
- LOM::Filtered.new(self, filter)
125
+ LOM::Filtered.new(filter, src: self)
122
126
  end
123
127
  end
124
128
 
125
129
 
126
- def ldap_branch(v)
130
+ def ldap_branch(v = nil)
131
+ return _ldap_branch if v.nil?
127
132
  @__ldap_branch = v
128
133
  end
129
134
 
130
- def ldap_prefix(v)
135
+ def ldap_prefix(v = nil)
136
+ return _ldap_prefix if v.nil?
131
137
  @__ldap_prefix = v
132
138
  end
133
139
 
@@ -154,6 +160,8 @@ module Mapper
154
160
 
155
161
  # @note block will be executed in the mapped object instance
156
162
  def ldap_to(p=nil, &b)
163
+ return _ldap_to if p.nil? && b.nil?
164
+
157
165
  if (! p.nil? ^ b.nil?) || (p && !p.kind_of?(Proc))
158
166
  raise ArgumentError,
159
167
  'one and only one of proc/lamba/block need to be defined'
@@ -162,7 +170,15 @@ module Mapper
162
170
  end
163
171
 
164
172
 
165
-
173
+ # Convert a dn to it's corresponding id the current mapping.
174
+ #
175
+ # @raise [Error] dn belongs to this mapping (it is in the mapping
176
+ # branch), but is malformed (not a direct child, or
177
+ # wrong prefix)
178
+ #
179
+ # @return [String] id
180
+ # @return [nil] dn is not from this mapping
181
+ #
166
182
  def ldap_dn_to_id(dn)
167
183
  prefix = _ldap_prefix.to_s
168
184
  branch = _ldap_branch
@@ -171,8 +187,8 @@ module Mapper
171
187
  case prefix
172
188
  when String, Symbol
173
189
  k, v, _ = sub.to_a
174
- raise ArgumentError, "not a direct child" if _
175
- raise ArgumentError, "wrong prefix" if k.casecmp(prefix) != 0
190
+ raise Error, "not a direct child" if _
191
+ raise Error, "wrong prefix" if k.casecmp(prefix) != 0
176
192
  v
177
193
  end
178
194
  end
@@ -190,24 +206,45 @@ module Mapper
190
206
  self.new(*args)
191
207
  end
192
208
 
193
-
194
- def each(type = :object, filter: nil, paged: nil)
209
+
210
+ # Iterate over matching data.
211
+ #
212
+ # @note If using `rawfilter`, no optimization will be performed
213
+ # aned the ldap attributes will be retrieved,
214
+ # even if desired type is :id
215
+ #
216
+ # @param type [:object, :id] return object or id
217
+ # @param filter [String] extra ldap search filter
218
+ # @param rawfilter [Proc] filter on ldap entry
219
+ # @param paged [Array<Integer,Integer>] pagination information
220
+ #
221
+ # @yieldparam obj_or_id [Object, String] ldap converted element according to type
222
+ #
223
+ # @return [Enumerator] if no block given
224
+ # @return [self] if block given
225
+ #
226
+ def each(type = :object, filter: nil, rawfilter: nil, paged: nil)
195
227
  # Create Enumerator if no block given
196
228
  unless block_given?
197
- return enum_for(:each, type, filter: filter, paged: paged)
229
+ return enum_for(:each, type,
230
+ filter: filter, rawfilter: rawfilter, paged: paged)
198
231
  end
199
232
 
200
233
  # Merging filters
201
- filters = [ filter, _ldap_filter ].compact
202
- filter = filters.size == 2 ? "(&#{filters.join})" : filters.first
234
+ filter = Net::LDAP.filter('&', *[ filter, _ldap_filter ].compact)
203
235
 
204
236
  # Define attributes/converter according to selected type
205
237
  attributes, converter =
206
238
  case type
207
- when :id then [ :dn, ->(e) { ldap_dn_to_id(e.dn) } ]
208
- when :object then [ _ldap_attrs, ->(e) { _ldap_to_obj(e) } ]
239
+ when :id then [ rawfilter ? _ldap_attrs : :dn,
240
+ ->(e) { ldap_dn_to_id(e.dn) }
241
+ ]
242
+ when :object then [ _ldap_attrs,
243
+ ->(e) { _ldap_to_obj(e) }
244
+ ]
209
245
  else raise ArgumentError, 'type must be either :object or :id'
210
246
  end
247
+
211
248
 
212
249
  # Paginate
213
250
  # XXX: pagination is emulated, should be avoided
@@ -222,7 +259,9 @@ module Mapper
222
259
  :attributes => attributes,
223
260
  :scope => _ldap_scope) {|entry|
224
261
 
225
- if paged.nil?
262
+ if rawfilter && !rawfilter.call(entry)
263
+ next
264
+ elsif paged.nil?
226
265
  yield(converter.(entry))
227
266
  elsif skip > 0
228
267
  skip -= 1
@@ -233,18 +272,29 @@ module Mapper
233
272
  yield(converter.(entry))
234
273
  end
235
274
  }
275
+
276
+ # Return self
277
+ self
236
278
  end
237
279
 
238
280
  def paginate(page, page_size)
239
- LOM::Filtered.new(self, paged: [ page, page_size ])
281
+ LOM::Filtered.new(src: self, paged: [ page, page_size ])
240
282
  end
241
283
 
242
- def all
243
- each(:object).to_a
284
+ # Retrieve matching data as a list of object
285
+ #
286
+ # @return [Array<Object>]
287
+ #
288
+ def all(&rawfilter)
289
+ each(:object, rawfilter: rawfilter).to_a
244
290
  end
245
291
 
246
- def list
247
- each(:id).to_a
292
+ # Retrieve matching data as a list of id
293
+ #
294
+ # @return [Array<String>]
295
+ #
296
+ def list(&rawfilter)
297
+ each(:id, rawfilter: rawfilter).to_a
248
298
  end
249
299
 
250
300
  def get(name)
data/lib/lom/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class LOM
2
- VERSION = '0.9.0'
2
+ VERSION = '0.10.0'
3
3
  end
data/lom.gemspec CHANGED
@@ -8,15 +8,13 @@ Gem::Specification.new do |s|
8
8
  s.summary = "LDAP Object Mapper"
9
9
  s.description = <<~EOF
10
10
 
11
- Ease processing of parameters in Sinatra framework.
12
- Integrates well with dry-types, sequel, ...
11
+ Creation of mapping between ldap entry and ruby object.
12
+ Allowing easy retrieval of information, building of search filter,
13
+ and updating ldap.
13
14
 
14
- Example:
15
- want! :user, Dry::Types::String, User
16
- want? :expired, Dry::Types::Params::Bool.default(true)
17
15
  EOF
18
16
 
19
- s.homepage = 'https://gitlab.com/sdalu/lom'
17
+ s.homepage = 'https://github.com/sdalu/lom'
20
18
  s.license = 'MIT'
21
19
 
22
20
  s.authors = [ "Stéphane D'Alu" ]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stéphane D'Alu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-26 00:00:00.000000000 Z
11
+ date: 2021-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-ldap
@@ -52,14 +52,12 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '13'
55
- description: |2
55
+ description: |2+
56
56
 
57
- Ease processing of parameters in Sinatra framework.
58
- Integrates well with dry-types, sequel, ...
57
+ Creation of mapping between ldap entry and ruby object.
58
+ Allowing easy retrieval of information, building of search filter,
59
+ and updating ldap.
59
60
 
60
- Example:
61
- want! :user, Dry::Types::String, User
62
- want? :expired, Dry::Types::Params::Bool.default(true)
63
61
  email:
64
62
  - stephane.dalu@insa-lyon.fr
65
63
  executables: []
@@ -77,7 +75,7 @@ files:
77
75
  - lib/lom/mapper.rb
78
76
  - lib/lom/version.rb
79
77
  - lom.gemspec
80
- homepage: https://gitlab.com/sdalu/lom
78
+ homepage: https://github.com/sdalu/lom
81
79
  licenses:
82
80
  - MIT
83
81
  metadata: {}
@@ -101,3 +99,4 @@ signing_key:
101
99
  specification_version: 4
102
100
  summary: LDAP Object Mapper
103
101
  test_files: []
102
+ ...