rom-ldap 0.2.2

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.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +251 -0
  3. data/CONTRIBUTING.md +18 -0
  4. data/README.md +172 -0
  5. data/TODO.md +33 -0
  6. data/config/responses.yml +328 -0
  7. data/lib/dry/monitor/ldap/colorizers/default.rb +17 -0
  8. data/lib/dry/monitor/ldap/colorizers/rouge.rb +31 -0
  9. data/lib/dry/monitor/ldap/logger.rb +58 -0
  10. data/lib/rom-ldap.rb +1 -0
  11. data/lib/rom/ldap.rb +22 -0
  12. data/lib/rom/ldap/alias.rb +30 -0
  13. data/lib/rom/ldap/associations.rb +6 -0
  14. data/lib/rom/ldap/associations/core.rb +23 -0
  15. data/lib/rom/ldap/associations/many_to_many.rb +18 -0
  16. data/lib/rom/ldap/associations/many_to_one.rb +22 -0
  17. data/lib/rom/ldap/associations/one_to_many.rb +32 -0
  18. data/lib/rom/ldap/associations/one_to_one.rb +19 -0
  19. data/lib/rom/ldap/associations/self_ref.rb +35 -0
  20. data/lib/rom/ldap/attribute.rb +327 -0
  21. data/lib/rom/ldap/client.rb +185 -0
  22. data/lib/rom/ldap/client/authentication.rb +118 -0
  23. data/lib/rom/ldap/client/operations.rb +233 -0
  24. data/lib/rom/ldap/commands.rb +6 -0
  25. data/lib/rom/ldap/commands/create.rb +41 -0
  26. data/lib/rom/ldap/commands/delete.rb +17 -0
  27. data/lib/rom/ldap/commands/update.rb +35 -0
  28. data/lib/rom/ldap/constants.rb +193 -0
  29. data/lib/rom/ldap/dataset.rb +286 -0
  30. data/lib/rom/ldap/dataset/conversion.rb +62 -0
  31. data/lib/rom/ldap/dataset/dsl.rb +299 -0
  32. data/lib/rom/ldap/dataset/persistence.rb +44 -0
  33. data/lib/rom/ldap/directory.rb +126 -0
  34. data/lib/rom/ldap/directory/capabilities.rb +71 -0
  35. data/lib/rom/ldap/directory/entry.rb +200 -0
  36. data/lib/rom/ldap/directory/env.rb +155 -0
  37. data/lib/rom/ldap/directory/operations.rb +282 -0
  38. data/lib/rom/ldap/directory/password.rb +122 -0
  39. data/lib/rom/ldap/directory/root.rb +187 -0
  40. data/lib/rom/ldap/directory/tokenization.rb +66 -0
  41. data/lib/rom/ldap/directory/transactions.rb +31 -0
  42. data/lib/rom/ldap/directory/vendors/active_directory.rb +129 -0
  43. data/lib/rom/ldap/directory/vendors/apache_ds.rb +27 -0
  44. data/lib/rom/ldap/directory/vendors/e_directory.rb +16 -0
  45. data/lib/rom/ldap/directory/vendors/open_directory.rb +12 -0
  46. data/lib/rom/ldap/directory/vendors/open_dj.rb +25 -0
  47. data/lib/rom/ldap/directory/vendors/open_ldap.rb +35 -0
  48. data/lib/rom/ldap/directory/vendors/three_eight_nine.rb +16 -0
  49. data/lib/rom/ldap/directory/vendors/unknown.rb +22 -0
  50. data/lib/rom/ldap/dsl.rb +76 -0
  51. data/lib/rom/ldap/errors.rb +47 -0
  52. data/lib/rom/ldap/expression.rb +77 -0
  53. data/lib/rom/ldap/expression_encoder.rb +174 -0
  54. data/lib/rom/ldap/extensions.rb +50 -0
  55. data/lib/rom/ldap/extensions/active_support_notifications.rb +26 -0
  56. data/lib/rom/ldap/extensions/compatibility.rb +11 -0
  57. data/lib/rom/ldap/extensions/dsml.rb +165 -0
  58. data/lib/rom/ldap/extensions/msgpack.rb +23 -0
  59. data/lib/rom/ldap/extensions/optimised_json.rb +25 -0
  60. data/lib/rom/ldap/extensions/rails_log_subscriber.rb +38 -0
  61. data/lib/rom/ldap/formatter.rb +26 -0
  62. data/lib/rom/ldap/functions.rb +207 -0
  63. data/lib/rom/ldap/gateway.rb +145 -0
  64. data/lib/rom/ldap/ldif.rb +74 -0
  65. data/lib/rom/ldap/ldif/exporter.rb +77 -0
  66. data/lib/rom/ldap/ldif/importer.rb +95 -0
  67. data/lib/rom/ldap/mapper_compiler.rb +19 -0
  68. data/lib/rom/ldap/matchers.rb +69 -0
  69. data/lib/rom/ldap/message_queue.rb +7 -0
  70. data/lib/rom/ldap/oid.rb +101 -0
  71. data/lib/rom/ldap/parsers/abstract_syntax.rb +91 -0
  72. data/lib/rom/ldap/parsers/attribute.rb +290 -0
  73. data/lib/rom/ldap/parsers/filter_syntax.rb +133 -0
  74. data/lib/rom/ldap/pdu.rb +285 -0
  75. data/lib/rom/ldap/plugin/pagination.rb +145 -0
  76. data/lib/rom/ldap/plugins.rb +7 -0
  77. data/lib/rom/ldap/projection_dsl.rb +38 -0
  78. data/lib/rom/ldap/relation.rb +135 -0
  79. data/lib/rom/ldap/relation/exporting.rb +72 -0
  80. data/lib/rom/ldap/relation/reading.rb +461 -0
  81. data/lib/rom/ldap/relation/writing.rb +64 -0
  82. data/lib/rom/ldap/responses.rb +17 -0
  83. data/lib/rom/ldap/restriction_dsl.rb +45 -0
  84. data/lib/rom/ldap/schema.rb +123 -0
  85. data/lib/rom/ldap/schema/attributes_inferrer.rb +59 -0
  86. data/lib/rom/ldap/schema/dsl.rb +13 -0
  87. data/lib/rom/ldap/schema/inferrer.rb +50 -0
  88. data/lib/rom/ldap/schema/type_builder.rb +133 -0
  89. data/lib/rom/ldap/scope.rb +19 -0
  90. data/lib/rom/ldap/search_request.rb +249 -0
  91. data/lib/rom/ldap/socket.rb +210 -0
  92. data/lib/rom/ldap/tasks/ldap.rake +103 -0
  93. data/lib/rom/ldap/tasks/ldif.rake +80 -0
  94. data/lib/rom/ldap/transaction.rb +29 -0
  95. data/lib/rom/ldap/type_map.rb +88 -0
  96. data/lib/rom/ldap/types.rb +158 -0
  97. data/lib/rom/ldap/version.rb +17 -0
  98. data/lib/rom/plugins/relation/ldap/active_directory.rb +182 -0
  99. data/lib/rom/plugins/relation/ldap/auto_restrictions.rb +69 -0
  100. data/lib/rom/plugins/relation/ldap/e_directory.rb +27 -0
  101. data/lib/rom/plugins/relation/ldap/instrumentation.rb +35 -0
  102. data/lib/rouge/lexers/ldap.rb +72 -0
  103. data/lib/rouge/themes/ldap.rb +49 -0
  104. metadata +231 -0
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/commands'
4
+ require 'rom/ldap/commands/create'
5
+ require 'rom/ldap/commands/update'
6
+ require 'rom/ldap/commands/delete'
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ROM
4
+ module LDAP
5
+ module Commands
6
+ class Create < ROM::Commands::Create
7
+
8
+ adapter :ldap
9
+
10
+ use :schema
11
+
12
+ after :finalize
13
+
14
+ # Pass tuple(s) to relation for insertion.
15
+ #
16
+ # @param tuples [Hash, Array<Hash>]
17
+ #
18
+ # @return [Array<Entry>]
19
+ #
20
+ # @api public
21
+ def execute(tuples)
22
+ Array([tuples]).flatten(1).map do |tuple|
23
+ relation.insert(tuple)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ # Output through relation output_schema
30
+ #
31
+ # @param entries [Array<Entry, FalseClass>]
32
+ #
33
+ # @api private
34
+ def finalize(entries, *)
35
+ entries.map { |t| relation.output_schema[t] }
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ROM
4
+ module LDAP
5
+ module Commands
6
+ class Delete < ROM::Commands::Delete
7
+
8
+ adapter :ldap
9
+
10
+ def execute
11
+ relation.delete
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ROM
4
+ module LDAP
5
+ module Commands
6
+ class Update < ROM::Commands::Update
7
+
8
+ adapter :ldap
9
+
10
+ use :schema
11
+
12
+ after :finalize
13
+
14
+ def execute(tuple)
15
+ update(input[tuple].to_h)
16
+ end
17
+
18
+ private
19
+
20
+ #
21
+ # @param entries [Array<Directory::Entry>]
22
+ #
23
+ # @api private
24
+ def finalize(entries, *)
25
+ entries.map { |t| relation.output_schema[t] }
26
+ end
27
+
28
+ def update(*args)
29
+ relation.update(*args)
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Layout/HashAlignment
4
+
5
+ require 'rom/ldap/oid'
6
+ require 'rom/ldap/scope'
7
+ require 'rom/ldap/alias'
8
+ require 'rom/ldap/matchers'
9
+ require 'rom/ldap/type_map'
10
+ require 'rom/ldap/responses'
11
+
12
+ require 'uri'
13
+
14
+ module ROM
15
+ module LDAP
16
+ # Matches an ldap(s) url
17
+ #
18
+ # @return [Regexp]
19
+ LDAPURI_REGEX = Regexp.union(
20
+ ::URI::DEFAULT_PARSER.make_regexp('ldap'),
21
+ ::URI::DEFAULT_PARSER.make_regexp('ldaps')
22
+ ).freeze
23
+
24
+ # Any word character or hyphen, equals
25
+ #
26
+ # @return [Regexp]
27
+ DN_REGEX = /(([-\w]+=[-\w]+)*,?)/.freeze
28
+
29
+ # Something in parentheses
30
+ #
31
+ # @return [Regexp]
32
+ FILTER_REGEX = /^\s*\(.*\)\s*$/.freeze
33
+
34
+ # @return [String]
35
+ NEW_LINE = "\n"
36
+
37
+ # @return [String]
38
+ WILDCARD = '*'
39
+
40
+ # @return [String]
41
+ PERCENT_SPACE = '%20'
42
+
43
+ # @return [String]
44
+ SPACE = ' '
45
+
46
+ # @return [Array<String>]
47
+ OP_ATTRS = %w[+].freeze
48
+
49
+ # @return [Array<String>]
50
+ ALL_ATTRS = [WILDCARD, *OP_ATTRS].freeze
51
+
52
+ # @return [String]
53
+ DEFAULT_PK = 'entrydn'
54
+
55
+ # @return [Array<String>]
56
+ #
57
+ # @example [Schema]
58
+ # use :timestamps,
59
+ # attributes: %i(create_timestamp modify_timestamp),
60
+ # type: ROM::LDAP::Types::Time
61
+ #
62
+ #
63
+ # @see Relation#operational
64
+ #
65
+ TIMESTAMPS = %w[createTimestamp modifyTimestamp].freeze
66
+
67
+ # @return [String]
68
+ #
69
+ DEFAULT_FILTER = '(objectClass=*)'
70
+
71
+ # Time conversion
72
+ #
73
+ # @return [Integer]
74
+ #
75
+ # @see Functions.to_time
76
+ TEN_MILLION = 10_000_000
77
+
78
+ # @return [Integer]
79
+ #
80
+ # @see Functions.to_time
81
+ SINCE_1601 = 11_644_473_600
82
+
83
+ # Internal abstraction of LDAP string search filter constructors.
84
+ #
85
+ # @see https://www.rfc-editor.org/rfc/rfc4515.txt String Search Filter Definition
86
+ #
87
+ # @return [Hash]
88
+ #
89
+ CONSTRUCTORS = {
90
+ con_and: '&', # AND / AMPERSAND / %x26
91
+ con_or: '|', # OR / VERTBAR / %x7C
92
+ con_not: '!' # NOT / EXCLAMATION / %x21
93
+ }.freeze
94
+
95
+ CONSTRUCTOR_REGEX = Regexp.union(/\s*\|\s*/, /\s*\&\s*/).freeze
96
+
97
+ # Internal abstraction of LDAP string search filter operators.
98
+ #
99
+ # @return [Hash]
100
+ #
101
+ # @see https://www.rfc-editor.org/rfc/rfc4515.txt String Search Filter Definition
102
+ #
103
+ # equal = EQUALS
104
+ # approx = TILDE EQUALS
105
+ # greaterorequal = RANGLE EQUALS
106
+ # lessorequal = LANGLE EQUALS
107
+ # extensible = ( attr [dnattrs]
108
+ # [matchingrule] COLON EQUALS assertionvalue )
109
+ # / ( [dnattrs]
110
+ # matchingrule COLON EQUALS assertionvalue )
111
+ #
112
+ OPERATORS = {
113
+ op_bineq: '=', # Binary comparison
114
+ op_eql: '=', # Equal to
115
+ op_prx: '~=', # Approximately equal to
116
+ op_gte: '>=', # Lexicographically greater than or equal to
117
+ op_lte: '<=', # Lexicographically less than or equal to
118
+ op_ext: ':=' # Bitwise comparison of numeric values
119
+ }.freeze
120
+
121
+ OPERATOR_REGEX = Regexp.union(*OPERATORS.values).freeze
122
+
123
+ # @return [Array]
124
+ #
125
+ ABSTRACTS = [*OPERATORS.keys, *CONSTRUCTORS.keys].freeze
126
+
127
+ # Symbolic abstraction of LDIF booleans and wildcard matcher
128
+ #
129
+ # @return [Hash]
130
+ #
131
+ VALUES_MAP = {
132
+ :wildcard => WILDCARD, # ANY / ASTERISK / %x2A
133
+ true => 'TRUE',
134
+ false => 'FALSE'
135
+ }.freeze
136
+
137
+ #
138
+ # DSL dataset methods
139
+ #
140
+ # @see RFC4515 value encoding rule.
141
+ #
142
+ # @return [Hash]
143
+ #
144
+ ESCAPES = {
145
+ "\0" => '00', # NUL / %x00
146
+ '*' => '2A', # ASTERISK / %x2A
147
+ '(' => '28', # LPARENS / %x28
148
+ ')' => '29', # RPARENS / %x29
149
+ '\\' => '5C' # ESC / %x5C
150
+ }.freeze
151
+
152
+ #
153
+ # Expression Encoder
154
+ # (1)type (2)dn (4)rule
155
+ #
156
+ # <attribute name>:<matching rule OID>:=<value>
157
+ # (&(objectCategory=group)(groupType:1.2.840.113556.1.4.803:=2147483648))
158
+ #
159
+ # @return [Regexp]
160
+ #
161
+ EXTENSIBLE_REGEX = /^([-;\w]*)(:dn)?(:(\w+|[.\w]+))?$/.freeze
162
+
163
+ # ESC and HEX values
164
+ #
165
+ # The value encoding rule ensures that the entire filter string is a valid
166
+ # UTF-8 string and provides that the octets that represent theses ESCAPES
167
+ # are represented as a backslash followed by the two hexadecimal digits
168
+ # representing the value of the encoded octet.
169
+ #
170
+ UNESCAPE_REGEX = /\\([a-f\d]{2})/i.freeze
171
+
172
+ # @return [Regexp]
173
+ #
174
+ ESCAPE_REGEX = Regexp.new('[' + ESCAPES.keys.map { |e| Regexp.escape(e) }.join + ']').freeze
175
+
176
+ # @return [Regexp]
177
+ #
178
+ VAL_REGEX = %r"(?:[-\[\]{}\w*.+/:@=,#\$%&!'^~\s\xC3\x80-\xCA\xAF]|[^\x00-\x7F]|\x5C(?:[\x20-\x23]|[\x2B\x2C]|[\x3B-\x3E]|\x5C)|\\[a-fA-F\d]{2})+"u.freeze
179
+
180
+ # Local file path
181
+ #
182
+ # @return [Regexp]
183
+ #
184
+ BIN_FILE_REGEX = %r{^file://(.*)}.freeze
185
+
186
+ # $1 = attribute
187
+ # $3 = value
188
+ #
189
+ LDIF_LINE_REGEX = /^([^:]+):([\:]?)[\s]*<?(.*)$/.freeze
190
+ end
191
+ end
192
+
193
+ # rubocop:enable Layout/HashAlignment
@@ -0,0 +1,286 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/initializer'
4
+ require 'rom/ldap/functions'
5
+
6
+ require 'rom/ldap/dataset/conversion'
7
+ require 'rom/ldap/dataset/persistence'
8
+ require 'rom/ldap/dataset/dsl'
9
+
10
+ module ROM
11
+ module LDAP
12
+ #
13
+ # @api private
14
+ class Dataset
15
+
16
+ extend Initializer
17
+ include Enumerable
18
+
19
+ # Directory instance
20
+ #
21
+ # @!attribute [r] directory
22
+ # @return [Directory]
23
+ option :directory
24
+
25
+ # LDAP filter defined in relation schema.
26
+ #
27
+ # @!attribute [r] name
28
+ # @return [String] Valid LDAP filter filter string.
29
+ option :name, type: Types::Filter, reader: :private
30
+
31
+ # Valid Distinguished Name. A relation class value or the gateway default.
32
+ #
33
+ # @!attribute [r] base
34
+ # @return [String] Set when initializing a relation
35
+ option :base, type: Types::DN, reader: :private, default: proc { directory.base }
36
+
37
+ #
38
+ # @!attribute [r] criteria
39
+ # @return [Array] Query AST
40
+ option :criteria, type: Types::Strict::Array, reader: :private, default: proc { EMPTY_ARRAY }
41
+
42
+ #
43
+ # @!attribute [r] direction
44
+ # @return [Symbol]
45
+ option :direction, type: Types::Direction, reader: :private, default: proc { :asc }
46
+
47
+ #
48
+ # @!attribute [r] offset
49
+ # @return [Integer] Pagination per_page(20).
50
+ option :offset, type: Types::Strict::Integer, reader: :private, optional: true
51
+
52
+ # @option :limit [Integer] Pagination page(1).
53
+ #
54
+ option :limit, type: Types::Strict::Integer, reader: :private, optional: true
55
+
56
+ # @option :random [TrueClass] Switch for randomisation
57
+ #
58
+ option :random, type: Types::Strict::Bool, reader: :private, default: proc { false }
59
+
60
+ # Attributes to return. Needs to be set to the projected schema.
61
+ #
62
+ option :attrs, type: Types::Strict::Array, reader: :private, optional: true
63
+
64
+ # @option :aliases [Array]
65
+ #
66
+ option :aliases, type: Types::Strict::Array, reader: :private, default: proc { EMPTY_ARRAY }
67
+
68
+ # @option :sort_attrs [String,Symbol] Attribute name(s) to sort by.
69
+ #
70
+ option :sort_attrs, type: Types::Strict::Array, reader: :private, optional: true
71
+
72
+ include DSL
73
+ include Persistence
74
+ include Conversion
75
+
76
+ # Collection of Dataset::DSL module methods.
77
+ # Used by Relation to forward methods to Dataset.
78
+ #
79
+ # @return [Array<Symbol>]
80
+ #
81
+ # @example
82
+ # # => %i{equal has lt begins excludes present}
83
+ #
84
+ def self.dsl
85
+ DSL.public_instance_methods(false)
86
+ end
87
+
88
+ # Dataset#where
89
+ alias_method :where, :equal
90
+
91
+ # Initialise a new class overriding options.
92
+ #
93
+ # @return [ROM::LDAP::Dataset]
94
+ #
95
+ # @param overrides [Hash] Alternative options
96
+ #
97
+ # @api public
98
+ def with(overrides)
99
+ self.class.new(options.merge(overrides))
100
+ end
101
+
102
+ # @return [Hash] internal options
103
+ #
104
+ # @api public
105
+ def opts
106
+ options.merge(ast: to_ast, filter: to_filter).freeze
107
+ end
108
+
109
+ # Iterate over the entries return from the server.
110
+ #
111
+ # @return [Array <Directory::Entry>]
112
+ # @return [Enumerator <Directory::Entry>]
113
+ #
114
+ # @api public
115
+ def each(*args, &block)
116
+ results = paginated? ? entries[page_range] : entries
117
+ results = results.sort_by { rand } if random
118
+ results = results.reverse_each if reversed?
119
+ results = results.map { |e| apply_aliases(e) } if aliases.any?
120
+
121
+ block_given? ? results.each(*args, &block) : results.to_enum
122
+ end
123
+
124
+ # Iterate over each entry or one attribute of each entry.
125
+ #
126
+ # @return [Mixed]
127
+ #
128
+ # @api public
129
+ def map(attr = nil, &block)
130
+ each.map { |entry| attr ? entry[attr] : entry }.map(&block)
131
+ end
132
+
133
+ # Inspect dataset revealing current ast and base.
134
+ #
135
+ # @return [String]
136
+ #
137
+ # @api public
138
+ def inspect
139
+ %(#<#{self.class}: base="#{base}" #{to_ast} />)
140
+ end
141
+
142
+ # This method is present in Sequel and ensures rom-sql/rom-ldap plugin interoperability.
143
+ #
144
+ # @return [Dataset]
145
+ #
146
+ # @api public
147
+ def unfiltered
148
+ with(criteria: EMPTY_ARRAY)
149
+ end
150
+
151
+ # Wildcard search on multiple attributes.
152
+ #
153
+ # @example
154
+ # dataset.grep([:givenname, :sn], 'foo').opts[:criteria] =>
155
+ # [:con_or, [[:op_eql, :givenname, "*foo*"], [:op_eql, :sn, "*foo*"]]]
156
+ #
157
+ #
158
+ # @see Relation::Reading#grep
159
+ #
160
+ # @param attrs [Array<Symbol>] schema attribute names
161
+ #
162
+ # @param value [String] search parameter
163
+ #
164
+ # @return [Relation]
165
+ #
166
+ # @api public
167
+ def grep(attrs, value)
168
+ new_criteria = attrs.map do |attr|
169
+ match_dsl([[attr, value]], left: WILDCARD, right: WILDCARD)
170
+ end
171
+ join(new_criteria, :con_or)
172
+ end
173
+
174
+ # @see Relation#where
175
+ #
176
+ # Combine AST criteria - use AND by default
177
+ #
178
+ # @param new_criteria [Array]
179
+ # @param constructor [Symbol]
180
+ #
181
+ # @return [Relation]
182
+ #
183
+ # @api public
184
+ def join(new_criteria, constructor = :con_and)
185
+ new_chain = join_dsl(constructor, new_criteria)
186
+
187
+ # check because RestrictionDSL sometimes offers empty criteria
188
+ if new_chain.empty?
189
+ self
190
+ else
191
+ chain(*new_chain)
192
+ end
193
+ end
194
+
195
+ # Validate the password against the filtered user.
196
+ #
197
+ # @param password [String]
198
+ #
199
+ # @return [Boolean]
200
+ #
201
+ # @api public
202
+ def bind(password)
203
+ directory.bind_as(filter: to_ast, password: password)
204
+ end
205
+
206
+ # Handle different string output formats
207
+ # i.e. DSML, LDIF, JSON, YAML, MessagePack.
208
+ #
209
+ # @return [Hash, Array<Hash>]
210
+ #
211
+ def export
212
+ results = map(&:canonical)
213
+ results.one? ? results.first : results
214
+ end
215
+
216
+ # Unrestricted count of every entry under the search base
217
+ # with the domain entry discounted.
218
+ #
219
+ # @return [Integer]
220
+ #
221
+ # @api public
222
+ def total
223
+ directory.base_total - 1
224
+ end
225
+
226
+ private
227
+
228
+ # Communicate with LDAP servers.
229
+ # @see Connection::SearchRequest for #query keywords defintion.
230
+ #
231
+ # @return [Array<Hash>] Populate with a directory search.
232
+ #
233
+ # @api private
234
+ def entries
235
+ results =
236
+ directory.query(
237
+ filter: to_ast,
238
+ base: base,
239
+ attributes: renamed_select,
240
+ sorted: renamed_sort,
241
+ max: limit,
242
+ reverse: reversed?
243
+ )
244
+
245
+ options[:criteria] = []
246
+ results
247
+ end
248
+
249
+ # @api private
250
+ def renamed_select
251
+ directory.canonical_attributes(attrs) if attrs
252
+ end
253
+
254
+ # @api private
255
+ def renamed_sort
256
+ directory.canonical_attributes(sort_attrs) if sort_attrs
257
+ end
258
+
259
+ # @return [Range]
260
+ #
261
+ # @api private
262
+ def page_range
263
+ offset..(offset + limit - 1)
264
+ end
265
+
266
+ # @api private
267
+ def paginated?
268
+ limit && offset
269
+ end
270
+
271
+ # @api private
272
+ def reversed?
273
+ direction.eql?(:desc)
274
+ end
275
+
276
+ def apply_aliases(entry)
277
+ Functions[:rename_keys][alias_map, entry].invert
278
+ end
279
+
280
+ def alias_map
281
+ { dn: :dn }.merge(attrs.zip(aliases).to_h)
282
+ end
283
+
284
+ end
285
+ end
286
+ end