lom 0.9.0 → 0.10.0
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 +4 -4
- data/README.md +6 -0
- data/lib/lom/filtered.rb +55 -35
- data/lib/lom/ldap/extensions.rb +1 -1
- data/lib/lom/mapper.rb +73 -23
- data/lib/lom/version.rb +1 -1
- data/lom.gemspec +4 -6
- metadata +8 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d37a56b9ef6c9dad3ae785439cb49560c641304fdb12f3d3d070541e8bf27b9
|
4
|
+
data.tar.gz: cd71e20fd089ab2b489bcdb2f857f508b8c283c0d4f2de875b81cfc722fab05b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
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
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
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(
|
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
|
164
|
-
|
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
|
|
data/lib/lom/ldap/extensions.rb
CHANGED
data/lib/lom/mapper.rb
CHANGED
@@ -37,7 +37,8 @@ module InstanceMethods
|
|
37
37
|
# @return [true, false]
|
38
38
|
#
|
39
39
|
def save!
|
40
|
-
|
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[
|
49
|
+
id, _ = Array(attrs[model.ldap_prefix])
|
49
50
|
raise MappingError, 'prefix for dn has multiple values' if _
|
50
|
-
dn =
|
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(
|
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
|
175
|
-
raise
|
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
|
-
|
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,
|
229
|
+
return enum_for(:each, type,
|
230
|
+
filter: filter, rawfilter: rawfilter, paged: paged)
|
198
231
|
end
|
199
232
|
|
200
233
|
# Merging filters
|
201
|
-
|
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 [
|
208
|
-
|
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
|
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
|
-
|
243
|
-
|
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
|
-
|
247
|
-
|
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
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
|
-
|
12
|
-
|
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://
|
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.
|
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-
|
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
|
-
|
58
|
-
|
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://
|
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
|
+
...
|