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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +251 -0
- data/CONTRIBUTING.md +18 -0
- data/README.md +172 -0
- data/TODO.md +33 -0
- data/config/responses.yml +328 -0
- data/lib/dry/monitor/ldap/colorizers/default.rb +17 -0
- data/lib/dry/monitor/ldap/colorizers/rouge.rb +31 -0
- data/lib/dry/monitor/ldap/logger.rb +58 -0
- data/lib/rom-ldap.rb +1 -0
- data/lib/rom/ldap.rb +22 -0
- data/lib/rom/ldap/alias.rb +30 -0
- data/lib/rom/ldap/associations.rb +6 -0
- data/lib/rom/ldap/associations/core.rb +23 -0
- data/lib/rom/ldap/associations/many_to_many.rb +18 -0
- data/lib/rom/ldap/associations/many_to_one.rb +22 -0
- data/lib/rom/ldap/associations/one_to_many.rb +32 -0
- data/lib/rom/ldap/associations/one_to_one.rb +19 -0
- data/lib/rom/ldap/associations/self_ref.rb +35 -0
- data/lib/rom/ldap/attribute.rb +327 -0
- data/lib/rom/ldap/client.rb +185 -0
- data/lib/rom/ldap/client/authentication.rb +118 -0
- data/lib/rom/ldap/client/operations.rb +233 -0
- data/lib/rom/ldap/commands.rb +6 -0
- data/lib/rom/ldap/commands/create.rb +41 -0
- data/lib/rom/ldap/commands/delete.rb +17 -0
- data/lib/rom/ldap/commands/update.rb +35 -0
- data/lib/rom/ldap/constants.rb +193 -0
- data/lib/rom/ldap/dataset.rb +286 -0
- data/lib/rom/ldap/dataset/conversion.rb +62 -0
- data/lib/rom/ldap/dataset/dsl.rb +299 -0
- data/lib/rom/ldap/dataset/persistence.rb +44 -0
- data/lib/rom/ldap/directory.rb +126 -0
- data/lib/rom/ldap/directory/capabilities.rb +71 -0
- data/lib/rom/ldap/directory/entry.rb +200 -0
- data/lib/rom/ldap/directory/env.rb +155 -0
- data/lib/rom/ldap/directory/operations.rb +282 -0
- data/lib/rom/ldap/directory/password.rb +122 -0
- data/lib/rom/ldap/directory/root.rb +187 -0
- data/lib/rom/ldap/directory/tokenization.rb +66 -0
- data/lib/rom/ldap/directory/transactions.rb +31 -0
- data/lib/rom/ldap/directory/vendors/active_directory.rb +129 -0
- data/lib/rom/ldap/directory/vendors/apache_ds.rb +27 -0
- data/lib/rom/ldap/directory/vendors/e_directory.rb +16 -0
- data/lib/rom/ldap/directory/vendors/open_directory.rb +12 -0
- data/lib/rom/ldap/directory/vendors/open_dj.rb +25 -0
- data/lib/rom/ldap/directory/vendors/open_ldap.rb +35 -0
- data/lib/rom/ldap/directory/vendors/three_eight_nine.rb +16 -0
- data/lib/rom/ldap/directory/vendors/unknown.rb +22 -0
- data/lib/rom/ldap/dsl.rb +76 -0
- data/lib/rom/ldap/errors.rb +47 -0
- data/lib/rom/ldap/expression.rb +77 -0
- data/lib/rom/ldap/expression_encoder.rb +174 -0
- data/lib/rom/ldap/extensions.rb +50 -0
- data/lib/rom/ldap/extensions/active_support_notifications.rb +26 -0
- data/lib/rom/ldap/extensions/compatibility.rb +11 -0
- data/lib/rom/ldap/extensions/dsml.rb +165 -0
- data/lib/rom/ldap/extensions/msgpack.rb +23 -0
- data/lib/rom/ldap/extensions/optimised_json.rb +25 -0
- data/lib/rom/ldap/extensions/rails_log_subscriber.rb +38 -0
- data/lib/rom/ldap/formatter.rb +26 -0
- data/lib/rom/ldap/functions.rb +207 -0
- data/lib/rom/ldap/gateway.rb +145 -0
- data/lib/rom/ldap/ldif.rb +74 -0
- data/lib/rom/ldap/ldif/exporter.rb +77 -0
- data/lib/rom/ldap/ldif/importer.rb +95 -0
- data/lib/rom/ldap/mapper_compiler.rb +19 -0
- data/lib/rom/ldap/matchers.rb +69 -0
- data/lib/rom/ldap/message_queue.rb +7 -0
- data/lib/rom/ldap/oid.rb +101 -0
- data/lib/rom/ldap/parsers/abstract_syntax.rb +91 -0
- data/lib/rom/ldap/parsers/attribute.rb +290 -0
- data/lib/rom/ldap/parsers/filter_syntax.rb +133 -0
- data/lib/rom/ldap/pdu.rb +285 -0
- data/lib/rom/ldap/plugin/pagination.rb +145 -0
- data/lib/rom/ldap/plugins.rb +7 -0
- data/lib/rom/ldap/projection_dsl.rb +38 -0
- data/lib/rom/ldap/relation.rb +135 -0
- data/lib/rom/ldap/relation/exporting.rb +72 -0
- data/lib/rom/ldap/relation/reading.rb +461 -0
- data/lib/rom/ldap/relation/writing.rb +64 -0
- data/lib/rom/ldap/responses.rb +17 -0
- data/lib/rom/ldap/restriction_dsl.rb +45 -0
- data/lib/rom/ldap/schema.rb +123 -0
- data/lib/rom/ldap/schema/attributes_inferrer.rb +59 -0
- data/lib/rom/ldap/schema/dsl.rb +13 -0
- data/lib/rom/ldap/schema/inferrer.rb +50 -0
- data/lib/rom/ldap/schema/type_builder.rb +133 -0
- data/lib/rom/ldap/scope.rb +19 -0
- data/lib/rom/ldap/search_request.rb +249 -0
- data/lib/rom/ldap/socket.rb +210 -0
- data/lib/rom/ldap/tasks/ldap.rake +103 -0
- data/lib/rom/ldap/tasks/ldif.rake +80 -0
- data/lib/rom/ldap/transaction.rb +29 -0
- data/lib/rom/ldap/type_map.rb +88 -0
- data/lib/rom/ldap/types.rb +158 -0
- data/lib/rom/ldap/version.rb +17 -0
- data/lib/rom/plugins/relation/ldap/active_directory.rb +182 -0
- data/lib/rom/plugins/relation/ldap/auto_restrictions.rb +69 -0
- data/lib/rom/plugins/relation/ldap/e_directory.rb +27 -0
- data/lib/rom/plugins/relation/ldap/instrumentation.rb +35 -0
- data/lib/rouge/lexers/ldap.rb +72 -0
- data/lib/rouge/themes/ldap.rb +49 -0
- metadata +231 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'digest/sha1'
|
|
4
|
+
require 'digest/sha2'
|
|
5
|
+
require 'digest/md5'
|
|
6
|
+
require 'base64'
|
|
7
|
+
require 'securerandom'
|
|
8
|
+
|
|
9
|
+
# sha2-512 512bit or 64bytes
|
|
10
|
+
# sha 160bits or 20bytes
|
|
11
|
+
#
|
|
12
|
+
module ROM
|
|
13
|
+
module LDAP
|
|
14
|
+
class Directory
|
|
15
|
+
|
|
16
|
+
# @abstract
|
|
17
|
+
# Encode and validate passwords using md5, sha or ssha.
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
class Password
|
|
21
|
+
|
|
22
|
+
# Generate an ecrypted password.
|
|
23
|
+
#
|
|
24
|
+
# @example
|
|
25
|
+
# Password.generate(:ssha, 'secret magic word')
|
|
26
|
+
#
|
|
27
|
+
# @param type [Symbol] Encryption type. [:md5, :sha, :ssha].
|
|
28
|
+
# @param password [String] Plain text password to be encrypted.
|
|
29
|
+
#
|
|
30
|
+
# @return [String]
|
|
31
|
+
#
|
|
32
|
+
# @raise [PasswordError]
|
|
33
|
+
#
|
|
34
|
+
# @api public
|
|
35
|
+
def self.generate(type, password, salt = secure_salt)
|
|
36
|
+
raise PasswordError, 'No password supplied' if password.nil?
|
|
37
|
+
|
|
38
|
+
case type
|
|
39
|
+
when :md5 then _encode(type, md5(password))
|
|
40
|
+
when :sha then _encode(type, sha(password))
|
|
41
|
+
when :ssha then _encode(type, ssha(password, salt))
|
|
42
|
+
when :ssha512 then _encode(type, ssha512(password, salt))
|
|
43
|
+
else
|
|
44
|
+
raise PasswordError, "Unsupported encryption type (#{type})"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.check_ssha512(password, encrypted)
|
|
49
|
+
decoded = Base64.decode64(encrypted.gsub(/^{SSHA512}/, EMPTY_STRING))
|
|
50
|
+
# hash = decoded[0..64]
|
|
51
|
+
salt = decoded[64..-1]
|
|
52
|
+
_encode(:ssha512, ssha512(password, salt)) == encrypted
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Validate plain password against encrypted SSHA password.
|
|
56
|
+
#
|
|
57
|
+
# @return [Boolean]
|
|
58
|
+
#
|
|
59
|
+
# @api public
|
|
60
|
+
def self.check_ssha(password, encrypted)
|
|
61
|
+
decoded = Base64.decode64(encrypted.gsub(/^{SSHA}/, EMPTY_STRING))
|
|
62
|
+
# hash = decoded[0..20]
|
|
63
|
+
salt = decoded[20..-1]
|
|
64
|
+
_encode(:ssha, ssha(password, salt)) == encrypted
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private_class_method
|
|
68
|
+
|
|
69
|
+
# @return [String] Prepend type to encrypted string.
|
|
70
|
+
#
|
|
71
|
+
# @api private
|
|
72
|
+
def self._encode(type, encrypted)
|
|
73
|
+
"{#{type.upcase}}" + Base64.strict_encode64(encrypted).chomp
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Generate salt.
|
|
77
|
+
#
|
|
78
|
+
# @api private
|
|
79
|
+
def self.secure_salt
|
|
80
|
+
SecureRandom.random_bytes(16)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @param str [String]
|
|
84
|
+
#
|
|
85
|
+
# @return [String] MD5 digest.
|
|
86
|
+
#
|
|
87
|
+
# @api private
|
|
88
|
+
def self.md5(str)
|
|
89
|
+
Digest::MD5.digest(str)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# @param str [String]
|
|
93
|
+
# @param salt [String]
|
|
94
|
+
#
|
|
95
|
+
# @return [String] SHA1 digest with salt.
|
|
96
|
+
#
|
|
97
|
+
# @api private
|
|
98
|
+
def self.ssha(str, salt)
|
|
99
|
+
Digest::SHA1.digest(str + salt) + salt
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# @param str [String]
|
|
103
|
+
#
|
|
104
|
+
# @return [String] SHA1 digest without salt.
|
|
105
|
+
#
|
|
106
|
+
# @api private
|
|
107
|
+
def self.sha(str)
|
|
108
|
+
Digest::SHA1.digest(str)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# "{SSHA512}A1lCCGYzUEJ5/qQCrFUAztLVaTaWv959RnpzaOsWB9Ij4CBCeNh6i4XrZzrvwUMM/AWbEb8Gjc7FWOBSPnkRuHsexjzeQImm"
|
|
112
|
+
# initial
|
|
113
|
+
#
|
|
114
|
+
def self.ssha512(str, salt)
|
|
115
|
+
Digest::SHA512.digest(str + salt) + salt
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ROM
|
|
4
|
+
module LDAP
|
|
5
|
+
class Directory
|
|
6
|
+
|
|
7
|
+
module Root
|
|
8
|
+
# Identify the LDAP server vendor, type determines vendor extension to load.
|
|
9
|
+
#
|
|
10
|
+
# @see https://ldapwiki.com/wiki/Determine%20LDAP%20Server%20Vendor
|
|
11
|
+
#
|
|
12
|
+
# @return [Symbol]
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
def type
|
|
16
|
+
case root.first('vendorName')
|
|
17
|
+
when /389/ then :three_eight_nine
|
|
18
|
+
when /Apache/ then :apache_ds
|
|
19
|
+
when /Apple/ then :open_directory
|
|
20
|
+
when /ForgeRock/ then :open_dj
|
|
21
|
+
when /IBM/ then :ibm
|
|
22
|
+
when /Netscape/ then :netscape
|
|
23
|
+
when /Novell/ then :e_directory
|
|
24
|
+
when /Oracle/ then :open_ds
|
|
25
|
+
when /Sun/ then :sun_microsystems
|
|
26
|
+
when nil
|
|
27
|
+
return :active_directory if ad?
|
|
28
|
+
return :open_ldap if od?
|
|
29
|
+
else
|
|
30
|
+
:unknown
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [String]
|
|
35
|
+
#
|
|
36
|
+
# @api public
|
|
37
|
+
def vendor_name
|
|
38
|
+
root.first('vendorName')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [String]
|
|
42
|
+
#
|
|
43
|
+
# @api public
|
|
44
|
+
def vendor_version
|
|
45
|
+
root.first('vendorVersion')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @example
|
|
49
|
+
# [ 'Apple', '510.30' ]
|
|
50
|
+
# [ 'Apache Software Foundation', '2.0.0-M24' ]
|
|
51
|
+
#
|
|
52
|
+
# @return [Array<String>]
|
|
53
|
+
#
|
|
54
|
+
# @api public
|
|
55
|
+
def vendor
|
|
56
|
+
[vendor_name, vendor_version]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Distinguished name of subschema
|
|
60
|
+
#
|
|
61
|
+
# @return [String]
|
|
62
|
+
#
|
|
63
|
+
# @api public
|
|
64
|
+
def sub_schema_entry
|
|
65
|
+
root.first('subschemaSubentry')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @return [Array<String>] Object classes known by directory
|
|
69
|
+
#
|
|
70
|
+
# @api public
|
|
71
|
+
def schema_object_classes
|
|
72
|
+
sub_schema['objectClasses'].sort
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Query directory for all known attribute types
|
|
76
|
+
#
|
|
77
|
+
# @return [Array<String>] Attribute types known by directory
|
|
78
|
+
#
|
|
79
|
+
# @api public
|
|
80
|
+
def schema_attribute_types
|
|
81
|
+
sub_schema['attributeTypes'].sort
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @return [Array<String>]
|
|
85
|
+
#
|
|
86
|
+
# @api public
|
|
87
|
+
def supported_extensions
|
|
88
|
+
root['supportedExtension'].sort
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# @return [Array<String>]
|
|
92
|
+
#
|
|
93
|
+
# @api public
|
|
94
|
+
def supported_controls
|
|
95
|
+
root['supportedControl'].sort
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @return [Array<String>]
|
|
99
|
+
#
|
|
100
|
+
# @api public
|
|
101
|
+
def supported_mechanisms
|
|
102
|
+
root['supportedSASLMechanisms'].sort
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# @return [Array<String>]
|
|
106
|
+
#
|
|
107
|
+
# @api public
|
|
108
|
+
def supported_features
|
|
109
|
+
root['supportedFeatures'].sort
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @return [Array<Integer>]
|
|
113
|
+
#
|
|
114
|
+
# @api public
|
|
115
|
+
def supported_versions
|
|
116
|
+
root['supportedLDAPVersion'].sort.map(&:to_i)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @return [String]
|
|
120
|
+
#
|
|
121
|
+
# @api public
|
|
122
|
+
def contexts
|
|
123
|
+
root['namingContexts'].sort
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
|
|
128
|
+
# Representation of directory RootDSE
|
|
129
|
+
#
|
|
130
|
+
# @see https://ldapwiki.com/wiki/Retrieving%20RootDSE
|
|
131
|
+
#
|
|
132
|
+
# @return [Directory::Entry]
|
|
133
|
+
#
|
|
134
|
+
# @raise [ResponseError]
|
|
135
|
+
#
|
|
136
|
+
# @api private
|
|
137
|
+
def root
|
|
138
|
+
@root ||= query(
|
|
139
|
+
base: EMPTY_STRING,
|
|
140
|
+
scope: SCOPE_BASE,
|
|
141
|
+
attributes: ALL_ATTRS
|
|
142
|
+
).first
|
|
143
|
+
|
|
144
|
+
@root || raise(ResponseError, 'Directory root failed to load')
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Representation of directory SubSchema
|
|
148
|
+
#
|
|
149
|
+
# @return [Directory::Entry]
|
|
150
|
+
#
|
|
151
|
+
# @raise [ResponseError]
|
|
152
|
+
#
|
|
153
|
+
# @api private
|
|
154
|
+
def sub_schema
|
|
155
|
+
@sub_schema ||= query(
|
|
156
|
+
base: sub_schema_entry,
|
|
157
|
+
scope: SCOPE_BASE,
|
|
158
|
+
attributes: %w[objectClasses attributeTypes],
|
|
159
|
+
filter: '(objectClass=subschema)',
|
|
160
|
+
max_results: 1
|
|
161
|
+
).first
|
|
162
|
+
|
|
163
|
+
@sub_schema || raise(ResponseError, 'Directory schema failed to load')
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Check if vendor identifies as ActiveDirectory
|
|
167
|
+
#
|
|
168
|
+
# @return [Boolean]
|
|
169
|
+
#
|
|
170
|
+
# @api private
|
|
171
|
+
def ad?
|
|
172
|
+
!root['forestFunctionality'].nil?
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Check if vendor identifies as OpenLDAP
|
|
176
|
+
#
|
|
177
|
+
# @return [Boolean]
|
|
178
|
+
#
|
|
179
|
+
# @api private
|
|
180
|
+
def od?
|
|
181
|
+
root['objectClass']&.include?('OpenLDAProotDSE')
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rom/ldap/parsers/abstract_syntax'
|
|
4
|
+
require 'rom/ldap/parsers/filter_syntax'
|
|
5
|
+
require 'rom/ldap/parsers/attribute'
|
|
6
|
+
|
|
7
|
+
module ROM
|
|
8
|
+
module LDAP
|
|
9
|
+
class Directory
|
|
10
|
+
|
|
11
|
+
# Parsing Formats
|
|
12
|
+
#
|
|
13
|
+
# @api private
|
|
14
|
+
module Tokenization
|
|
15
|
+
# Allows adapters that subclass Directory to use custom parsers.
|
|
16
|
+
# Extends the class with filter abstraction behavior.
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
19
|
+
def self.included(klass)
|
|
20
|
+
klass.class_eval do
|
|
21
|
+
extend Dry::Core::ClassAttributes
|
|
22
|
+
|
|
23
|
+
defines :attribute_class
|
|
24
|
+
attribute_class Parsers::Attribute
|
|
25
|
+
|
|
26
|
+
defines :filter_class
|
|
27
|
+
filter_class Parsers::FilterSyntax
|
|
28
|
+
|
|
29
|
+
defines :ast_class
|
|
30
|
+
ast_class Parsers::AbstractSyntax
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# Convert abstract criteria or LDAP filter into an expression object.
|
|
37
|
+
# Check for parsed attributes to prevent recursion.
|
|
38
|
+
#
|
|
39
|
+
# @param input [Array, String] RFC2254 or AST
|
|
40
|
+
#
|
|
41
|
+
def to_expression(input)
|
|
42
|
+
attrs = !@attribute_types.nil? ? attribute_types : EMPTY_ARRAY
|
|
43
|
+
|
|
44
|
+
# Filter > AST
|
|
45
|
+
unless input.is_a?(Array)
|
|
46
|
+
input = self.class.filter_class.new(input, attrs).call
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# AST > Expression
|
|
50
|
+
self.class.ast_class.new(input, attrs).call
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Create parsed attribute from definiton.
|
|
54
|
+
#
|
|
55
|
+
# @param attr_def [String]
|
|
56
|
+
#
|
|
57
|
+
# @return [Hash]
|
|
58
|
+
#
|
|
59
|
+
def to_attribute(attr_def)
|
|
60
|
+
self.class.attribute_class.new(attr_def).call
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ROM
|
|
4
|
+
module LDAP
|
|
5
|
+
class Directory
|
|
6
|
+
|
|
7
|
+
# https://ldapwiki.com/wiki/Lightweight%20Directory%20Access%20Protocol%20%28LDAP%29%20Transactions
|
|
8
|
+
# https://tools.ietf.org/html/rfc5805
|
|
9
|
+
# https://ldapwiki.com/wiki/EDirectory%20LDAP%20Transaction
|
|
10
|
+
#
|
|
11
|
+
module Transactions
|
|
12
|
+
# @example
|
|
13
|
+
# directory.transaction(opts) { yield(self) }
|
|
14
|
+
#
|
|
15
|
+
# @todo Transactions WIP
|
|
16
|
+
#
|
|
17
|
+
# @api public
|
|
18
|
+
def transaction(_opts)
|
|
19
|
+
# binding.pry
|
|
20
|
+
|
|
21
|
+
# OID[:transaction_start_request]
|
|
22
|
+
# OID[:transaction_spec_request]
|
|
23
|
+
# OID[:transaction_end_request]
|
|
24
|
+
|
|
25
|
+
yield()
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ROM
|
|
4
|
+
module LDAP
|
|
5
|
+
#
|
|
6
|
+
# Microsoft Active Directory Extension
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
module ActiveDirectory
|
|
10
|
+
#
|
|
11
|
+
# @note
|
|
12
|
+
# Use the AD Forest configuration container as a search base.
|
|
13
|
+
#
|
|
14
|
+
# @see https://msdn.microsoft.com/en-us/library/ms684291(v=vs.85).aspx
|
|
15
|
+
#
|
|
16
|
+
# RootDSE domainFunctionality
|
|
17
|
+
# RootDSE domainControllerFunctionality
|
|
18
|
+
# RootDSE forestFunctionality
|
|
19
|
+
#
|
|
20
|
+
VERSION_NAMES = {
|
|
21
|
+
0 => 'Windows Server 2000 (5.0)',
|
|
22
|
+
1 => 'Windows Server 2003 (5.2)',
|
|
23
|
+
2 => 'Windows Server 2003 R2 (5.2)',
|
|
24
|
+
3 => 'Windows Server 2008 (6.0)',
|
|
25
|
+
4 => 'Windows Server 2008 R2 (6.1)',
|
|
26
|
+
5 => 'Windows Server 2012 (6.2)',
|
|
27
|
+
6 => 'Windows Server 2012 R2 (6.3)',
|
|
28
|
+
7 => 'Windows Server 2016 (10.0)',
|
|
29
|
+
8 => 'Windows Server Latest Version (?)'
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# RootDSE supportedLDAPPolicies
|
|
34
|
+
#
|
|
35
|
+
POLICIES = %w[
|
|
36
|
+
InitRecvTimeout
|
|
37
|
+
MaxBatchReturnMessages
|
|
38
|
+
MaxConnections
|
|
39
|
+
MaxConnIdleTime
|
|
40
|
+
MaxDatagramRecv
|
|
41
|
+
MaxNotificationPerConn
|
|
42
|
+
MaxPageSize
|
|
43
|
+
MaxPercentDirSyncRequests
|
|
44
|
+
MaxPoolThreads
|
|
45
|
+
MaxQueryDuration
|
|
46
|
+
MaxReceiveBuffer
|
|
47
|
+
MaxResultSetSize
|
|
48
|
+
MaxResultSetsPerConn
|
|
49
|
+
MaxTempTableSize
|
|
50
|
+
MaxValRange
|
|
51
|
+
MaxValRangeTransitive
|
|
52
|
+
MinResultSets
|
|
53
|
+
SystemMemoryLimitPercent
|
|
54
|
+
ThreadMemoryLimit
|
|
55
|
+
].freeze
|
|
56
|
+
|
|
57
|
+
# @return [String]
|
|
58
|
+
#
|
|
59
|
+
def vendor_name
|
|
60
|
+
'Microsoft'
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [String]
|
|
64
|
+
#
|
|
65
|
+
def vendor_version
|
|
66
|
+
VERSION_NAMES[domain_functionality]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [Integer]
|
|
70
|
+
#
|
|
71
|
+
def controller_functionality
|
|
72
|
+
root.first('domainControllerFunctionality').to_i
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @return [Integer]
|
|
76
|
+
#
|
|
77
|
+
def forest_functionality
|
|
78
|
+
root.first('forestFunctionality').to_i
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @return [Integer]
|
|
82
|
+
#
|
|
83
|
+
def domain_functionality
|
|
84
|
+
root.first('domainFunctionality').to_i
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# LDAP server internal clock
|
|
88
|
+
#
|
|
89
|
+
# @return [Time]
|
|
90
|
+
#
|
|
91
|
+
def directory_time
|
|
92
|
+
Functions[:to_time][root.first('currentTime')]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# @return [Array<String>]
|
|
96
|
+
#
|
|
97
|
+
def supported_capabilities
|
|
98
|
+
root['supportedCapabilities'].sort
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# OID = OID.dup.merge!(
|
|
103
|
+
# extended_dn: '1.2.840.113556.1.4.529', # Extended DN control (Stateless)
|
|
104
|
+
# get_stats: '1.2.840.113556.1.4.970', # Get stats control (Stateless)
|
|
105
|
+
# verify_name: '1.2.840.113556.1.4.1338', # Verify name control (Stateless)
|
|
106
|
+
# domain_scope: '1.2.840.113556.1.4.1339', # LDAP_SERVER_DOMAIN_SCOPE_OID
|
|
107
|
+
# unknown: '1.2.840.113556.1.4.1340',
|
|
108
|
+
# # unknown: '1.2.840.113556.1.4.1341',
|
|
109
|
+
# # unknown: '1.2.840.113556.1.4.1413',
|
|
110
|
+
# # unknown: '1.2.840.113556.1.4.1504',
|
|
111
|
+
# # unknown: '1.2.840.113556.1.4.1852',
|
|
112
|
+
# # unknown: '1.2.840.113556.1.4.1907',
|
|
113
|
+
# # unknown: '1.2.840.113556.1.4.1948',
|
|
114
|
+
# # unknown: '1.2.840.113556.1.4.1974',
|
|
115
|
+
# # unknown: '1.2.840.113556.1.4.2026',
|
|
116
|
+
# # unknown: '1.2.840.113556.1.4.2064',
|
|
117
|
+
# # unknown: '1.2.840.113556.1.4.2065',
|
|
118
|
+
# # unknown: '1.2.840.113556.1.4.2066',
|
|
119
|
+
# # unknown: '1.2.840.113556.1.4.2090',
|
|
120
|
+
# # unknown: '1.2.840.113556.1.4.2204',
|
|
121
|
+
# # unknown: '1.2.840.113556.1.4.2205',
|
|
122
|
+
# # unknown: '1.2.840.113556.1.4.2206',
|
|
123
|
+
# # unknown: '1.2.840.113556.1.4.2211',
|
|
124
|
+
# # unknown: '1.2.840.113556.1.4.2239',
|
|
125
|
+
# # unknown: '1.2.840.113556.1.4.2255',
|
|
126
|
+
# # unknown: '1.2.840.113556.1.4.2256'
|
|
127
|
+
# ).freeze
|
|
128
|
+
end
|
|
129
|
+
end
|