net-ldap 0.13.0 → 0.14.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.
Potentially problematic release.
This version of net-ldap might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +12 -0
- data/.rubocop_todo.yml +282 -145
- data/Contributors.rdoc +1 -0
- data/History.rdoc +17 -0
- data/README.rdoc +1 -1
- data/lib/net/ber.rb +3 -2
- data/lib/net/ber/ber_parser.rb +1 -1
- data/lib/net/ber/core_ext/array.rb +1 -1
- data/lib/net/ber/core_ext/integer.rb +1 -1
- data/lib/net/ber/core_ext/string.rb +1 -1
- data/lib/net/ldap.rb +89 -24
- data/lib/net/ldap/auth_adapter/gss_spnego.rb +2 -2
- data/lib/net/ldap/auth_adapter/sasl.rb +2 -2
- data/lib/net/ldap/connection.rb +108 -25
- data/lib/net/ldap/dataset.rb +2 -2
- data/lib/net/ldap/entry.rb +3 -3
- data/lib/net/ldap/filter.rb +5 -5
- data/lib/net/ldap/pdu.rb +26 -2
- data/lib/net/ldap/version.rb +1 -1
- data/lib/net/snmp.rb +18 -18
- data/test/ber/core_ext/test_array.rb +1 -1
- data/test/ber/test_ber.rb +2 -2
- data/test/fixtures/openldap/slapd.conf.ldif +1 -1
- data/test/integration/test_add.rb +1 -1
- data/test/integration/test_ber.rb +1 -1
- data/test/integration/test_delete.rb +1 -1
- data/test/integration/test_open.rb +1 -1
- data/test/integration/test_password_modify.rb +80 -0
- data/test/integration/test_search.rb +1 -1
- data/test/test_auth_adapter.rb +6 -3
- data/test/test_dn.rb +3 -3
- data/test/test_filter.rb +4 -4
- data/test/test_ldap.rb +51 -10
- data/test/test_ldap_connection.rb +62 -51
- data/test/test_ldif.rb +10 -10
- data/test/test_search.rb +2 -2
- data/test/test_snmp.rb +4 -4
- data/testserver/ldapserver.rb +11 -12
- metadata +5 -4
data/Contributors.rdoc
CHANGED
data/History.rdoc
CHANGED
@@ -1,5 +1,22 @@
|
|
1
|
+
=== Net::LDAP 0.14.0
|
2
|
+
|
3
|
+
* Normalize the encryption parameter passed to the LDAP constructor {#264}[https://github.com/ruby-ldap/ruby-net-ldap/pull/264]
|
4
|
+
* Update Docs: Net::LDAP now requires ruby >= 2 {#261}[https://github.com/ruby-ldap/ruby-net-ldap/pull/261]
|
5
|
+
* fix symbol proc {#255}[https://github.com/ruby-ldap/ruby-net-ldap/pull/255]
|
6
|
+
* fix trailing commas {#256}[https://github.com/ruby-ldap/ruby-net-ldap/pull/256]
|
7
|
+
* fix deprecated hash methods {#254}[https://github.com/ruby-ldap/ruby-net-ldap/pull/254]
|
8
|
+
* fix space after comma {#253}[https://github.com/ruby-ldap/ruby-net-ldap/pull/253]
|
9
|
+
* fix space inside brackets {#252}[https://github.com/ruby-ldap/ruby-net-ldap/pull/252]
|
10
|
+
* Rubocop style fixes {#249}[https://github.com/ruby-ldap/ruby-net-ldap/pull/249]
|
11
|
+
* Lazy initialize Net::LDAP::Connection's internal socket {#235}[https://github.com/ruby-ldap/ruby-net-ldap/pull/235]
|
12
|
+
* Support for rfc3062 Password Modify, closes #163 {#178}[https://github.com/ruby-ldap/ruby-net-ldap/pull/178]
|
13
|
+
|
1
14
|
=== Net::LDAP 0.13.0
|
2
15
|
|
16
|
+
Avoid this release for because of an backwards incompatibility in how encryption
|
17
|
+
is initialized https://github.com/ruby-ldap/ruby-net-ldap/pull/264. We did not
|
18
|
+
yank it because people have already worked around it.
|
19
|
+
|
3
20
|
* Set a connect_timeout for the creation of a socket {#243}[https://github.com/ruby-ldap/ruby-net-ldap/pull/243]
|
4
21
|
* Update bundler before installing gems with bundler {#245}[https://github.com/ruby-ldap/ruby-net-ldap/pull/245]
|
5
22
|
* Net::LDAP#encryption accepts string {#239}[https://github.com/ruby-ldap/ruby-net-ldap/pull/239]
|
data/README.rdoc
CHANGED
data/lib/net/ber.rb
CHANGED
@@ -106,6 +106,7 @@ module Net # :nodoc:
|
|
106
106
|
# <tr><th>CHARACTER STRING</th><th>C</th><td>29: 61 (0x3d, 0b00111101)</td></tr>
|
107
107
|
# <tr><th>BMPString</th><th>P</th><td>30: 30 (0x1e, 0b00011110)</td></tr>
|
108
108
|
# <tr><th>BMPString</th><th>C</th><td>30: 62 (0x3e, 0b00111110)</td></tr>
|
109
|
+
# <tr><th>ExtendedResponse</th><th>C</th><td>107: 139 (0x8b, 0b010001011)</td></tr>
|
109
110
|
# </table>
|
110
111
|
module BER
|
111
112
|
VERSION = Net::LDAP::VERSION
|
@@ -234,7 +235,7 @@ module Net # :nodoc:
|
|
234
235
|
# TODO 20100327 AZ: Should we be allocating an array of 256 values
|
235
236
|
# that will either be +nil+ or an object type symbol, or should we
|
236
237
|
# allocate an empty Hash since unknown values return +nil+ anyway?
|
237
|
-
out = [
|
238
|
+
out = [nil] * 256
|
238
239
|
syntax.each do |tag_class_id, encodings|
|
239
240
|
tag_class = TAG_CLASS[tag_class_id]
|
240
241
|
encodings.each do |encoding_id, classes|
|
@@ -269,7 +270,7 @@ class Net::BER::BerIdentifiedOid
|
|
269
270
|
|
270
271
|
def initialize(oid)
|
271
272
|
if oid.is_a?(String)
|
272
|
-
oid = oid.split(/\./).map
|
273
|
+
oid = oid.split(/\./).map(&:to_i)
|
273
274
|
end
|
274
275
|
@value = oid
|
275
276
|
end
|
data/lib/net/ber/ber_parser.rb
CHANGED
@@ -89,7 +89,7 @@ module Net::BER::Extensions::Array
|
|
89
89
|
#if our array does not contain at least one array then wrap it in an array before going forward
|
90
90
|
ary = self[0].kind_of?(Array) ? self : [self]
|
91
91
|
ary = ary.collect do |control_sequence|
|
92
|
-
control_sequence.collect
|
92
|
+
control_sequence.collect(&:to_ber).to_ber_sequence.reject_empty_ber_arrays
|
93
93
|
end
|
94
94
|
ary.to_ber_sequence.reject_empty_ber_arrays
|
95
95
|
end
|
data/lib/net/ldap.rb
CHANGED
@@ -264,14 +264,14 @@ class Net::LDAP
|
|
264
264
|
SearchScope_BaseObject = 0
|
265
265
|
SearchScope_SingleLevel = 1
|
266
266
|
SearchScope_WholeSubtree = 2
|
267
|
-
SearchScopes = [
|
268
|
-
SearchScope_WholeSubtree
|
267
|
+
SearchScopes = [SearchScope_BaseObject, SearchScope_SingleLevel,
|
268
|
+
SearchScope_WholeSubtree]
|
269
269
|
|
270
270
|
DerefAliases_Never = 0
|
271
271
|
DerefAliases_Search = 1
|
272
272
|
DerefAliases_Find = 2
|
273
273
|
DerefAliases_Always = 3
|
274
|
-
DerefAliasesArray = [
|
274
|
+
DerefAliasesArray = [DerefAliases_Never, DerefAliases_Search, DerefAliases_Find, DerefAliases_Always]
|
275
275
|
|
276
276
|
primitive = { 2 => :null } # UnbindRequest body
|
277
277
|
constructed = {
|
@@ -323,7 +323,14 @@ class Net::LDAP
|
|
323
323
|
:constructed => constructed,
|
324
324
|
}
|
325
325
|
|
326
|
+
universal = {
|
327
|
+
constructed: {
|
328
|
+
107 => :array, #ExtendedResponse (PasswdModifyResponseValue)
|
329
|
+
},
|
330
|
+
}
|
331
|
+
|
326
332
|
AsnSyntax = Net::BER.compile_syntax(:application => application,
|
333
|
+
:universal => universal,
|
327
334
|
:context_specific => context_specific)
|
328
335
|
|
329
336
|
DefaultHost = "127.0.0.1"
|
@@ -332,7 +339,8 @@ class Net::LDAP
|
|
332
339
|
DefaultTreebase = "dc=com"
|
333
340
|
DefaultForceNoPage = false
|
334
341
|
|
335
|
-
StartTlsOid =
|
342
|
+
StartTlsOid = '1.3.6.1.4.1.1466.20037'
|
343
|
+
PasswdModifyOid = '1.3.6.1.4.1.4203.1.11.1'
|
336
344
|
|
337
345
|
# https://tools.ietf.org/html/rfc4511#section-4.1.9
|
338
346
|
# https://tools.ietf.org/html/rfc4511#appendix-A
|
@@ -381,14 +389,14 @@ class Net::LDAP
|
|
381
389
|
ResultCodeCompareFalse,
|
382
390
|
ResultCodeCompareTrue,
|
383
391
|
ResultCodeReferral,
|
384
|
-
ResultCodeSaslBindInProgress
|
392
|
+
ResultCodeSaslBindInProgress,
|
385
393
|
]
|
386
394
|
|
387
395
|
# nonstandard list of "successful" result codes for searches
|
388
396
|
ResultCodesSearchSuccess = [
|
389
397
|
ResultCodeSuccess,
|
390
398
|
ResultCodeTimeLimitExceeded,
|
391
|
-
ResultCodeSizeLimitExceeded
|
399
|
+
ResultCodeSizeLimitExceeded,
|
392
400
|
]
|
393
401
|
|
394
402
|
# map of result code to human message
|
@@ -430,7 +438,7 @@ class Net::LDAP
|
|
430
438
|
ResultCodeEntryAlreadyExists => "Entry Already Exists",
|
431
439
|
ResultCodeObjectClassModsProhibited => "ObjectClass Modifications Prohibited",
|
432
440
|
ResultCodeAffectsMultipleDSAs => "Affects Multiple DSAs",
|
433
|
-
ResultCodeOther => "Other"
|
441
|
+
ResultCodeOther => "Other",
|
434
442
|
}
|
435
443
|
|
436
444
|
module LDAPControls
|
@@ -531,7 +539,7 @@ class Net::LDAP
|
|
531
539
|
@auth = args[:auth] || DefaultAuth
|
532
540
|
@base = args[:base] || DefaultTreebase
|
533
541
|
@force_no_page = args[:force_no_page] || DefaultForceNoPage
|
534
|
-
@encryption = args[:encryption] # may be nil
|
542
|
+
@encryption = normalize_encryption(args[:encryption]) # may be nil
|
535
543
|
@connect_timeout = args[:connect_timeout]
|
536
544
|
|
537
545
|
if pr = @auth[:password] and pr.respond_to?(:call)
|
@@ -583,7 +591,7 @@ class Net::LDAP
|
|
583
591
|
@auth = {
|
584
592
|
:method => :simple,
|
585
593
|
:username => username,
|
586
|
-
:password => password
|
594
|
+
:password => password,
|
587
595
|
}
|
588
596
|
end
|
589
597
|
alias_method :auth, :authenticate
|
@@ -601,13 +609,7 @@ class Net::LDAP
|
|
601
609
|
def encryption(args)
|
602
610
|
warn "Deprecation warning: please give :encryption option as a Hash to Net::LDAP.new"
|
603
611
|
return if args.nil?
|
604
|
-
|
605
|
-
|
606
|
-
case method = args.to_sym
|
607
|
-
when :simple_tls, :start_tls
|
608
|
-
args = { :method => method, :tls_options => {} }
|
609
|
-
end
|
610
|
-
@encryption = args
|
612
|
+
@encryption = normalize_encryption(args)
|
611
613
|
end
|
612
614
|
|
613
615
|
# #open takes the same parameters as #new. #open makes a network
|
@@ -651,8 +653,11 @@ class Net::LDAP
|
|
651
653
|
#++
|
652
654
|
def get_operation_result
|
653
655
|
result = @result
|
654
|
-
result = result.result if result.is_a?(Net::LDAP::PDU)
|
655
656
|
os = OpenStruct.new
|
657
|
+
if result.is_a?(Net::LDAP::PDU)
|
658
|
+
os.extended_response = result.extended_response
|
659
|
+
result = result.result
|
660
|
+
end
|
656
661
|
if result.is_a?(Hash)
|
657
662
|
# We might get a hash of LDAP response codes instead of a simple
|
658
663
|
# numeric code.
|
@@ -764,10 +769,10 @@ class Net::LDAP
|
|
764
769
|
|
765
770
|
instrument "search.net_ldap", args do |payload|
|
766
771
|
@result = use_connection(args) do |conn|
|
767
|
-
conn.search(args)
|
772
|
+
conn.search(args) do |entry|
|
768
773
|
result_set << entry if result_set
|
769
774
|
yield entry if block_given?
|
770
|
-
|
775
|
+
end
|
771
776
|
end
|
772
777
|
|
773
778
|
if return_result_set
|
@@ -906,7 +911,7 @@ class Net::LDAP
|
|
906
911
|
# end
|
907
912
|
def bind_as(args = {})
|
908
913
|
result = false
|
909
|
-
open
|
914
|
+
open do |me|
|
910
915
|
rs = search args
|
911
916
|
if rs and rs.first and dn = rs.first.dn
|
912
917
|
password = args[:password]
|
@@ -914,7 +919,7 @@ class Net::LDAP
|
|
914
919
|
result = rs if bind(:method => :simple, :username => dn,
|
915
920
|
:password => password)
|
916
921
|
end
|
917
|
-
|
922
|
+
end
|
918
923
|
result
|
919
924
|
end
|
920
925
|
|
@@ -1041,6 +1046,44 @@ class Net::LDAP
|
|
1041
1046
|
end
|
1042
1047
|
end
|
1043
1048
|
|
1049
|
+
# Password Modify
|
1050
|
+
#
|
1051
|
+
# Change existing password:
|
1052
|
+
#
|
1053
|
+
# dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
|
1054
|
+
# auth = {
|
1055
|
+
# method: :simple,
|
1056
|
+
# username: dn,
|
1057
|
+
# password: 'passworD1'
|
1058
|
+
# }
|
1059
|
+
# ldap.password_modify(dn: dn,
|
1060
|
+
# auth: auth,
|
1061
|
+
# old_password: 'passworD1',
|
1062
|
+
# new_password: 'passworD2')
|
1063
|
+
#
|
1064
|
+
# Or get the LDAP server to generate a password for you:
|
1065
|
+
#
|
1066
|
+
# dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
|
1067
|
+
# auth = {
|
1068
|
+
# method: :simple,
|
1069
|
+
# username: dn,
|
1070
|
+
# password: 'passworD1'
|
1071
|
+
# }
|
1072
|
+
# ldap.password_modify(dn: dn,
|
1073
|
+
# auth: auth,
|
1074
|
+
# old_password: 'passworD1')
|
1075
|
+
#
|
1076
|
+
# ldap.get_operation_result.extended_response[0][0] #=> 'VtcgGf/G'
|
1077
|
+
#
|
1078
|
+
def password_modify(args)
|
1079
|
+
instrument "modify_password.net_ldap", args do |payload|
|
1080
|
+
@result = use_connection(args) do |conn|
|
1081
|
+
conn.password_modify(args)
|
1082
|
+
end
|
1083
|
+
@result.success?
|
1084
|
+
end
|
1085
|
+
end
|
1086
|
+
|
1044
1087
|
# Add a value to an attribute. Takes the full DN of the entry to modify,
|
1045
1088
|
# the name (Symbol or String) of the attribute, and the value (String or
|
1046
1089
|
# Array). If the attribute does not exist (and there are no schema
|
@@ -1159,7 +1202,7 @@ class Net::LDAP
|
|
1159
1202
|
:supportedExtension,
|
1160
1203
|
:supportedFeatures,
|
1161
1204
|
:supportedLdapVersion,
|
1162
|
-
:supportedSASLMechanisms
|
1205
|
+
:supportedSASLMechanisms,
|
1163
1206
|
])
|
1164
1207
|
(rs and rs.first) or Net::LDAP::Entry.new
|
1165
1208
|
end
|
@@ -1226,6 +1269,11 @@ class Net::LDAP
|
|
1226
1269
|
inspected
|
1227
1270
|
end
|
1228
1271
|
|
1272
|
+
# Internal: Set @open_connection for testing
|
1273
|
+
def connection=(connection)
|
1274
|
+
@open_connection = connection
|
1275
|
+
end
|
1276
|
+
|
1229
1277
|
private
|
1230
1278
|
|
1231
1279
|
# Yields an open connection if there is one, otherwise establishes a new
|
@@ -1251,18 +1299,35 @@ class Net::LDAP
|
|
1251
1299
|
|
1252
1300
|
# Establish a new connection to the LDAP server
|
1253
1301
|
def new_connection
|
1254
|
-
Net::LDAP::Connection.new \
|
1302
|
+
connection = Net::LDAP::Connection.new \
|
1255
1303
|
:host => @host,
|
1256
1304
|
:port => @port,
|
1257
1305
|
:hosts => @hosts,
|
1258
1306
|
:encryption => @encryption,
|
1259
1307
|
:instrumentation_service => @instrumentation_service,
|
1260
1308
|
:connect_timeout => @connect_timeout
|
1309
|
+
|
1310
|
+
# Force connect to see if there's a connection error
|
1311
|
+
connection.socket
|
1312
|
+
connection
|
1261
1313
|
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, Net::LDAP::ConnectionRefusedError => e
|
1262
1314
|
@result = {
|
1263
1315
|
:resultCode => 52,
|
1264
|
-
:errorMessage => ResultStrings[ResultCodeUnavailable]
|
1316
|
+
:errorMessage => ResultStrings[ResultCodeUnavailable],
|
1265
1317
|
}
|
1266
1318
|
raise e
|
1267
1319
|
end
|
1320
|
+
|
1321
|
+
# Normalize encryption parameter the constructor accepts, expands a few
|
1322
|
+
# convenience symbols into recognizable hashes
|
1323
|
+
def normalize_encryption(args)
|
1324
|
+
return if args.nil?
|
1325
|
+
return args if args.is_a? Hash
|
1326
|
+
|
1327
|
+
case method = args.to_sym
|
1328
|
+
when :simple_tls, :start_tls
|
1329
|
+
{ :method => method, :tls_options => {} }
|
1330
|
+
end
|
1331
|
+
end
|
1332
|
+
|
1268
1333
|
end # class LDAP
|
@@ -22,12 +22,12 @@ module Net
|
|
22
22
|
user, psw = [auth[:username] || auth[:dn], auth[:password]]
|
23
23
|
raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
|
24
24
|
|
25
|
-
nego = proc
|
25
|
+
nego = proc do |challenge|
|
26
26
|
t2_msg = NTLM::Message.parse(challenge)
|
27
27
|
t3_msg = t2_msg.response({ :user => user, :password => psw },
|
28
28
|
{ :ntlmv2 => true })
|
29
29
|
t3_msg.serialize
|
30
|
-
|
30
|
+
end
|
31
31
|
|
32
32
|
Net::LDAP::AuthAdapter::Sasl.new(@connection).bind \
|
33
33
|
:method => :sasl,
|
@@ -33,7 +33,7 @@ module Net
|
|
33
33
|
message_id = @connection.next_msgid
|
34
34
|
|
35
35
|
n = 0
|
36
|
-
loop
|
36
|
+
loop do
|
37
37
|
sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
|
38
38
|
request = [
|
39
39
|
Net::LDAP::Connection::LdapVersion.to_ber, "".to_ber, sasl
|
@@ -50,7 +50,7 @@ module Net
|
|
50
50
|
raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
|
51
51
|
|
52
52
|
cred = chall.call(pdu.result_server_sasl_creds)
|
53
|
-
|
53
|
+
end
|
54
54
|
|
55
55
|
raise Net::LDAP::SASLChallengeOverflowError, "why are we here?"
|
56
56
|
end
|
data/lib/net/ldap/connection.rb
CHANGED
@@ -9,19 +9,28 @@ class Net::LDAP::Connection #:nodoc:
|
|
9
9
|
LdapVersion = 3
|
10
10
|
MaxSaslChallenges = 10
|
11
11
|
|
12
|
-
|
12
|
+
# Initialize a connection to an LDAP server
|
13
|
+
#
|
14
|
+
# :server
|
15
|
+
# :hosts Array of tuples specifying host, port
|
16
|
+
# :host host
|
17
|
+
# :port port
|
18
|
+
# :socket prepared socket
|
19
|
+
#
|
20
|
+
def initialize(server = {})
|
21
|
+
@server = server
|
13
22
|
@instrumentation_service = server[:instrumentation_service]
|
14
23
|
|
15
|
-
|
16
|
-
|
17
|
-
else
|
18
|
-
server[:hosts] = [[server[:host], server[:port]]] if server[:hosts].nil?
|
19
|
-
open_connection(server)
|
20
|
-
end
|
24
|
+
# Allows tests to parameterize what socket class to use
|
25
|
+
@socket_class = server.fetch(:socket_class, DefaultSocket)
|
21
26
|
|
22
27
|
yield self if block_given?
|
23
28
|
end
|
24
29
|
|
30
|
+
def socket_class=(socket_class)
|
31
|
+
@socket_class = socket_class
|
32
|
+
end
|
33
|
+
|
25
34
|
def prepare_socket(server)
|
26
35
|
socket = server[:socket]
|
27
36
|
encryption = server[:encryption]
|
@@ -35,13 +44,13 @@ class Net::LDAP::Connection #:nodoc:
|
|
35
44
|
encryption = server[:encryption]
|
36
45
|
|
37
46
|
socket_opts = {
|
38
|
-
connect_timeout: server[:connect_timeout] || DefaultConnectTimeout
|
47
|
+
connect_timeout: server[:connect_timeout] || DefaultConnectTimeout,
|
39
48
|
}
|
40
49
|
|
41
50
|
errors = []
|
42
51
|
hosts.each do |host, port|
|
43
52
|
begin
|
44
|
-
prepare_socket(server.merge(socket:
|
53
|
+
prepare_socket(server.merge(socket: @socket_class.new(host, port, socket_opts)))
|
45
54
|
return
|
46
55
|
rescue Net::LDAP::Error, SocketError, SystemCallError,
|
47
56
|
OpenSSL::SSL::SSLError => e
|
@@ -124,7 +133,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
124
133
|
when :start_tls
|
125
134
|
message_id = next_msgid
|
126
135
|
request = [
|
127
|
-
Net::LDAP::StartTlsOid.to_ber_contextspecific(0)
|
136
|
+
Net::LDAP::StartTlsOid.to_ber_contextspecific(0),
|
128
137
|
].to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
|
129
138
|
|
130
139
|
write(request, nil, message_id)
|
@@ -202,7 +211,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
202
211
|
def read(syntax = Net::LDAP::AsnSyntax)
|
203
212
|
ber_object =
|
204
213
|
instrument "read.net_ldap_connection", :syntax => syntax do |payload|
|
205
|
-
|
214
|
+
socket.read_ber(syntax) do |id, content_length|
|
206
215
|
payload[:object_type_id] = id
|
207
216
|
payload[:content_length] = content_length
|
208
217
|
end
|
@@ -232,7 +241,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
232
241
|
def write(request, controls = nil, message_id = next_msgid)
|
233
242
|
instrument "write.net_ldap_connection" do |payload|
|
234
243
|
packet = [message_id.to_ber, request, controls].compact.to_ber_sequence
|
235
|
-
payload[:content_length] =
|
244
|
+
payload[:content_length] = socket.write(packet)
|
236
245
|
end
|
237
246
|
end
|
238
247
|
private :write
|
@@ -274,7 +283,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
274
283
|
sort_control = [
|
275
284
|
Net::LDAP::LDAPControls::SORT_REQUEST.to_ber,
|
276
285
|
false.to_ber,
|
277
|
-
sort_control_values.to_ber_sequence.to_s.to_ber
|
286
|
+
sort_control_values.to_ber_sequence.to_s.to_ber,
|
278
287
|
].to_ber_sequence
|
279
288
|
end
|
280
289
|
|
@@ -387,7 +396,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
387
396
|
time.to_ber,
|
388
397
|
attrs_only.to_ber,
|
389
398
|
filter.to_ber,
|
390
|
-
ber_attrs.to_ber_sequence
|
399
|
+
ber_attrs.to_ber_sequence,
|
391
400
|
].to_ber_appsequence(Net::LDAP::PDU::SearchRequest)
|
392
401
|
|
393
402
|
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
|
@@ -400,7 +409,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
400
409
|
Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
|
401
410
|
# Criticality MUST be false to interoperate with normal LDAPs.
|
402
411
|
false.to_ber,
|
403
|
-
rfc2696_cookie.map
|
412
|
+
rfc2696_cookie.map(&:to_ber).to_ber_sequence.to_s.to_ber,
|
404
413
|
].to_ber_sequence if paged
|
405
414
|
controls << ber_sort if ber_sort
|
406
415
|
controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
|
@@ -494,20 +503,20 @@ class Net::LDAP::Connection #:nodoc:
|
|
494
503
|
MODIFY_OPERATIONS = { #:nodoc:
|
495
504
|
:add => 0,
|
496
505
|
:delete => 1,
|
497
|
-
:replace => 2
|
506
|
+
:replace => 2,
|
498
507
|
}
|
499
508
|
|
500
509
|
def self.modify_ops(operations)
|
501
510
|
ops = []
|
502
511
|
if operations
|
503
|
-
operations.each
|
512
|
+
operations.each do |op, attrib, values|
|
504
513
|
# TODO, fix the following line, which gives a bogus error if the
|
505
514
|
# opcode is invalid.
|
506
515
|
op_ber = MODIFY_OPERATIONS[op.to_sym].to_ber_enumerated
|
507
|
-
values = [
|
508
|
-
values = [
|
509
|
-
ops << [
|
510
|
-
|
516
|
+
values = [values].flatten.map { |v| v.to_ber if v }.to_ber_set
|
517
|
+
values = [attrib.to_s.to_ber, values].to_ber_sequence
|
518
|
+
ops << [op_ber, values].to_ber
|
519
|
+
end
|
511
520
|
end
|
512
521
|
ops
|
513
522
|
end
|
@@ -526,7 +535,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
526
535
|
message_id = next_msgid
|
527
536
|
request = [
|
528
537
|
modify_dn.to_ber,
|
529
|
-
ops.to_ber_sequence
|
538
|
+
ops.to_ber_sequence,
|
530
539
|
].to_ber_appsequence(Net::LDAP::PDU::ModifyRequest)
|
531
540
|
|
532
541
|
write(request, nil, message_id)
|
@@ -539,6 +548,51 @@ class Net::LDAP::Connection #:nodoc:
|
|
539
548
|
pdu
|
540
549
|
end
|
541
550
|
|
551
|
+
##
|
552
|
+
# Password Modify
|
553
|
+
#
|
554
|
+
# http://tools.ietf.org/html/rfc3062
|
555
|
+
#
|
556
|
+
# passwdModifyOID OBJECT IDENTIFIER ::= 1.3.6.1.4.1.4203.1.11.1
|
557
|
+
#
|
558
|
+
# PasswdModifyRequestValue ::= SEQUENCE {
|
559
|
+
# userIdentity [0] OCTET STRING OPTIONAL
|
560
|
+
# oldPasswd [1] OCTET STRING OPTIONAL
|
561
|
+
# newPasswd [2] OCTET STRING OPTIONAL }
|
562
|
+
#
|
563
|
+
# PasswdModifyResponseValue ::= SEQUENCE {
|
564
|
+
# genPasswd [0] OCTET STRING OPTIONAL }
|
565
|
+
#
|
566
|
+
# Encoded request:
|
567
|
+
#
|
568
|
+
# 00\x02\x01\x02w+\x80\x171.3.6.1.4.1.4203.1.11.1\x81\x100\x0E\x81\x05old\x82\x05new
|
569
|
+
#
|
570
|
+
def password_modify(args)
|
571
|
+
dn = args[:dn]
|
572
|
+
raise ArgumentError, 'DN is required' if !dn || dn.empty?
|
573
|
+
|
574
|
+
ext_seq = [Net::LDAP::PasswdModifyOid.to_ber_contextspecific(0)]
|
575
|
+
|
576
|
+
unless args[:old_password].nil?
|
577
|
+
pwd_seq = [args[:old_password].to_ber(0x81)]
|
578
|
+
pwd_seq << args[:new_password].to_ber(0x82) unless args[:new_password].nil?
|
579
|
+
ext_seq << pwd_seq.to_ber_sequence.to_ber(0x81)
|
580
|
+
end
|
581
|
+
|
582
|
+
request = ext_seq.to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
|
583
|
+
|
584
|
+
message_id = next_msgid
|
585
|
+
|
586
|
+
write(request, nil, message_id)
|
587
|
+
pdu = queued_read(message_id)
|
588
|
+
|
589
|
+
if !pdu || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse
|
590
|
+
raise Net::LDAP::ResponseMissingError, "response missing or invalid"
|
591
|
+
end
|
592
|
+
|
593
|
+
pdu
|
594
|
+
end
|
595
|
+
|
542
596
|
#--
|
543
597
|
# TODO: need to support a time limit, in case the server fails to respond.
|
544
598
|
# Unlike other operation-methods in this class, we return a result hash
|
@@ -549,9 +603,9 @@ class Net::LDAP::Connection #:nodoc:
|
|
549
603
|
def add(args)
|
550
604
|
add_dn = args[:dn] or raise Net::LDAP::EmptyDNError, "Unable to add empty DN"
|
551
605
|
add_attrs = []
|
552
|
-
a = args[:attributes] and a.each
|
553
|
-
add_attrs << [
|
554
|
-
|
606
|
+
a = args[:attributes] and a.each do |k, v|
|
607
|
+
add_attrs << [k.to_s.to_ber, Array(v).map(&:to_ber).to_ber_set].to_ber_sequence
|
608
|
+
end
|
555
609
|
|
556
610
|
message_id = next_msgid
|
557
611
|
request = [add_dn.to_ber, add_attrs.to_ber_sequence].to_ber_appsequence(Net::LDAP::PDU::AddRequest)
|
@@ -607,4 +661,33 @@ class Net::LDAP::Connection #:nodoc:
|
|
607
661
|
|
608
662
|
pdu
|
609
663
|
end
|
664
|
+
|
665
|
+
# Internal: Returns a Socket like object used internally to communicate with
|
666
|
+
# LDAP server.
|
667
|
+
#
|
668
|
+
# Typically a TCPSocket, but can be a OpenSSL::SSL::SSLSocket
|
669
|
+
def socket
|
670
|
+
return @conn if defined? @conn
|
671
|
+
|
672
|
+
# First refactoring uses the existing methods open_connection and
|
673
|
+
# prepare_socket to set @conn. Next cleanup would centralize connection
|
674
|
+
# handling here.
|
675
|
+
if @server[:socket]
|
676
|
+
prepare_socket(@server)
|
677
|
+
else
|
678
|
+
@server[:hosts] = [[@server[:host], @server[:port]]] if @server[:hosts].nil?
|
679
|
+
open_connection(@server)
|
680
|
+
end
|
681
|
+
|
682
|
+
@conn
|
683
|
+
end
|
684
|
+
|
685
|
+
private
|
686
|
+
|
687
|
+
# Wrap around Socket.tcp to normalize with other Socket initializers
|
688
|
+
class DefaultSocket
|
689
|
+
def self.new(host, port, socket_opts = {})
|
690
|
+
Socket.tcp(host, port, socket_opts)
|
691
|
+
end
|
692
|
+
end
|
610
693
|
end # class Connection
|