rubyntlm 0.4.0 → 0.5.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/.rspec +2 -3
- data/README.md +9 -2
- data/Rakefile +8 -0
- data/examples/http.rb +1 -1
- data/lib/net/ntlm.rb +10 -9
- data/lib/net/ntlm/blob.rb +17 -6
- data/lib/net/ntlm/client.rb +61 -0
- data/lib/net/ntlm/client/session.rb +223 -0
- data/lib/net/ntlm/encode_util.rb +2 -2
- data/lib/net/ntlm/field_set.rb +9 -5
- data/lib/net/ntlm/message.rb +23 -9
- data/lib/net/ntlm/message/type1.rb +1 -26
- data/lib/net/ntlm/message/type2.rb +11 -37
- data/lib/net/ntlm/message/type3.rb +77 -14
- data/lib/net/ntlm/version.rb +1 -1
- data/rubyntlm.gemspec +3 -2
- data/spec/lib/net/ntlm/blob_spec.rb +1 -1
- data/spec/lib/net/ntlm/client/session_spec.rb +68 -0
- data/spec/lib/net/ntlm/client_spec.rb +64 -0
- data/spec/lib/net/ntlm/encode_util_spec.rb +3 -3
- data/spec/lib/net/ntlm/field_set_spec.rb +4 -4
- data/spec/lib/net/ntlm/field_spec.rb +5 -5
- data/spec/lib/net/ntlm/message/type1_spec.rb +99 -10
- data/spec/lib/net/ntlm/message/type2_spec.rb +68 -24
- data/spec/lib/net/ntlm/message/type3_spec.rb +207 -2
- data/spec/lib/net/ntlm/security_buffer_spec.rb +8 -8
- data/spec/lib/net/ntlm/string_spec.rb +14 -14
- data/spec/lib/net/ntlm/version_spec.rb +7 -6
- data/spec/lib/net/ntlm_spec.rb +20 -22
- data/spec/spec_helper.rb +1 -2
- data/spec/support/shared/examples/net/ntlm/field_shared.rb +3 -3
- data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +34 -34
- data/spec/support/shared/examples/net/ntlm/int_shared.rb +8 -8
- data/spec/support/shared/examples/net/ntlm/message_shared.rb +3 -3
- metadata +36 -16
data/lib/net/ntlm/encode_util.rb
CHANGED
@@ -24,8 +24,8 @@ module NTLM
|
|
24
24
|
end
|
25
25
|
else # Use native 1.9 string encoding functions
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
# Decode a UTF16 string to a ASCII string
|
28
|
+
# @param [String] str The string to convert
|
29
29
|
def self.decode_utf16le(str)
|
30
30
|
str.force_encoding(Encoding::UTF_16LE)
|
31
31
|
str.encode(Encoding::UTF_8, Encoding::UTF_16LE).force_encoding('UTF-8')
|
data/lib/net/ntlm/field_set.rb
CHANGED
@@ -88,12 +88,12 @@ module Net
|
|
88
88
|
@alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
|
89
89
|
end
|
90
90
|
|
91
|
-
def
|
92
|
-
@alist.
|
91
|
+
def parse(str, offset=0)
|
92
|
+
@alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
|
93
93
|
end
|
94
94
|
|
95
|
-
def
|
96
|
-
@alist.
|
95
|
+
def serialize
|
96
|
+
@alist.map{|n, f| f.serialize }.join
|
97
97
|
end
|
98
98
|
|
99
99
|
def size
|
@@ -119,7 +119,11 @@ module Net
|
|
119
119
|
def disable(name)
|
120
120
|
self[name].active = false
|
121
121
|
end
|
122
|
+
|
123
|
+
def has_disabled_fields?
|
124
|
+
@alist.any? { |name, field| !field.active }
|
125
|
+
end
|
122
126
|
end
|
123
127
|
|
124
128
|
end
|
125
|
-
end
|
129
|
+
end
|
data/lib/net/ntlm/message.rb
CHANGED
@@ -20,9 +20,10 @@ module NTLM
|
|
20
20
|
:LOCAL_CALL => 0x00004000,
|
21
21
|
:ALWAYS_SIGN => 0x00008000,
|
22
22
|
:TARGET_TYPE_DOMAIN => 0x00010000,
|
23
|
-
:TARGET_INFO => 0x00800000,
|
24
23
|
:NTLM2_KEY => 0x00080000,
|
24
|
+
:TARGET_INFO => 0x00800000,
|
25
25
|
:KEY128 => 0x20000000,
|
26
|
+
:KEY_EXCHANGE => 0x40000000,
|
26
27
|
:KEY56 => 0x80000000
|
27
28
|
}.freeze
|
28
29
|
|
@@ -42,14 +43,14 @@ module NTLM
|
|
42
43
|
m = Type0.new
|
43
44
|
m.parse(str)
|
44
45
|
case m.type
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
46
|
+
when 1
|
47
|
+
t = Type1.new.parse(str)
|
48
|
+
when 2
|
49
|
+
t = Type2.new.parse(str)
|
50
|
+
when 3
|
51
|
+
t = Type3.new.parse(str)
|
52
|
+
else
|
53
|
+
raise ArgumentError, "unknown type: #{m.type}"
|
53
54
|
end
|
54
55
|
t
|
55
56
|
end
|
@@ -59,6 +60,19 @@ module NTLM
|
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
63
|
+
# @return [self]
|
64
|
+
def parse(str)
|
65
|
+
super
|
66
|
+
|
67
|
+
while has_disabled_fields? && serialize.size < str.size
|
68
|
+
# enable the next disabled field
|
69
|
+
self.class.names.find { |name| !self[name].active && enable(name) }
|
70
|
+
super
|
71
|
+
end
|
72
|
+
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
62
76
|
def has_flag?(flag)
|
63
77
|
(self[:flag].value & FLAGS[flag]) == FLAGS[flag]
|
64
78
|
end
|
@@ -10,34 +10,9 @@ module Net
|
|
10
10
|
int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] }
|
11
11
|
security_buffer :domain, {:value => ""}
|
12
12
|
security_buffer :workstation, {:value => Socket.gethostname }
|
13
|
-
string :
|
13
|
+
string :os_version, {:size => 8, :value => "", :active => false }
|
14
14
|
|
15
|
-
class << Type1
|
16
|
-
# Parses a Type 1 Message
|
17
|
-
# @param [String] str A string containing Type 1 data
|
18
|
-
# @return [Type1] The parsed Type 1 message
|
19
|
-
def parse(str)
|
20
|
-
t = new
|
21
|
-
t.parse(str)
|
22
|
-
t
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# @!visibility private
|
27
|
-
def parse(str)
|
28
|
-
super(str)
|
29
|
-
enable(:domain) if has_flag?(:DOMAIN_SUPPLIED)
|
30
|
-
enable(:workstation) if has_flag?(:WORKSTATION_SUPPLIED)
|
31
|
-
super(str)
|
32
|
-
if ( (len = data_edge - head_size) > 0)
|
33
|
-
self.padding = "\0" * len
|
34
|
-
super(str)
|
35
|
-
end
|
36
|
-
end
|
37
15
|
end
|
38
|
-
|
39
16
|
end
|
40
17
|
end
|
41
18
|
end
|
42
|
-
|
43
|
-
|
@@ -5,39 +5,14 @@ module Net
|
|
5
5
|
# @private false
|
6
6
|
class Type2 < Message
|
7
7
|
|
8
|
-
string
|
9
|
-
int32LE
|
10
|
-
security_buffer
|
11
|
-
int32LE
|
12
|
-
int64LE
|
13
|
-
int64LE
|
14
|
-
security_buffer
|
15
|
-
string
|
16
|
-
|
17
|
-
class << Type2
|
18
|
-
# Parse a Type 2 packet
|
19
|
-
# @param [String] str A string containing Type 2 data
|
20
|
-
# @return [Type2]
|
21
|
-
def parse(str)
|
22
|
-
t = new
|
23
|
-
t.parse(str)
|
24
|
-
t
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# @!visibility private
|
29
|
-
def parse(str)
|
30
|
-
super(str)
|
31
|
-
if has_flag?(:TARGET_INFO)
|
32
|
-
enable(:context)
|
33
|
-
enable(:target_info)
|
34
|
-
super(str)
|
35
|
-
end
|
36
|
-
if ( (len = data_edge - head_size) > 0)
|
37
|
-
self.padding = "\0" * len
|
38
|
-
super(str)
|
39
|
-
end
|
40
|
-
end
|
8
|
+
string :sign, { :size => 8, :value => SSP_SIGN }
|
9
|
+
int32LE :type, { :value => 2 }
|
10
|
+
security_buffer :target_name, { :size => 0, :value => "" }
|
11
|
+
int32LE :flag, { :value => DEFAULT_FLAGS[:TYPE2] }
|
12
|
+
int64LE :challenge, { :value => 0}
|
13
|
+
int64LE :context, { :value => 0, :active => false }
|
14
|
+
security_buffer :target_info, { :value => "", :active => false }
|
15
|
+
string :os_version, { :size => 8, :value => "", :active => false }
|
41
16
|
|
42
17
|
# Generates a Type 3 response based on the Type 2 Information
|
43
18
|
# @return [Type3]
|
@@ -45,9 +20,9 @@ module Net
|
|
45
20
|
# @option arg [String] :password The user's password
|
46
21
|
# @option arg [String] :domain ('') The domain to authenticate to
|
47
22
|
# @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
|
48
|
-
# @option opt [Boolean] :use_default_target (
|
23
|
+
# @option opt [Boolean] :use_default_target (false) Use the domain supplied by the server in the Type 2 packet
|
49
24
|
# @note An empty :domain option authenticates to the local machine.
|
50
|
-
# @note The :use_default_target has
|
25
|
+
# @note The :use_default_target has precedence over the :domain option
|
51
26
|
def response(arg, opt = {})
|
52
27
|
usr = arg[:user]
|
53
28
|
pwd = arg[:password]
|
@@ -115,9 +90,8 @@ module Net
|
|
115
90
|
:flag => self.flag
|
116
91
|
})
|
117
92
|
end
|
118
|
-
end
|
119
|
-
|
120
93
|
|
94
|
+
end
|
121
95
|
|
122
96
|
end
|
123
97
|
end
|
@@ -13,18 +13,10 @@ module Net
|
|
13
13
|
security_buffer :user, {:value => ""}
|
14
14
|
security_buffer :workstation, {:value => ""}
|
15
15
|
security_buffer :session_key, {:value => "", :active => false }
|
16
|
-
|
16
|
+
int32LE :flag, {:value => 0, :active => false }
|
17
|
+
string :os_version, {:size => 8, :active => false }
|
17
18
|
|
18
19
|
class << Type3
|
19
|
-
# Parse a Type 3 packet
|
20
|
-
# @param [String] str A string containing Type 3 data
|
21
|
-
# @return [Type2]
|
22
|
-
def parse(str)
|
23
|
-
t = new
|
24
|
-
t.parse(str)
|
25
|
-
t
|
26
|
-
end
|
27
|
-
|
28
20
|
# Builds a Type 3 packet
|
29
21
|
# @note All options must be properly encoded with either unicode or oem encoding
|
30
22
|
# @return [Type3]
|
@@ -47,7 +39,7 @@ module Net
|
|
47
39
|
|
48
40
|
if arg[:session_key]
|
49
41
|
t.enable(:session_key)
|
50
|
-
t.session_key = arg[session_key]
|
42
|
+
t.session_key = arg[:session_key]
|
51
43
|
end
|
52
44
|
|
53
45
|
if arg[:flag]
|
@@ -58,11 +50,82 @@ module Net
|
|
58
50
|
t
|
59
51
|
end
|
60
52
|
end
|
61
|
-
end
|
62
53
|
|
54
|
+
# @param server_challenge (see #password?)
|
55
|
+
def blank_password?(server_challenge)
|
56
|
+
password?('', server_challenge)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param password [String]
|
60
|
+
# @param server_challenge [String] The server's {Type2#challenge challenge} from the
|
61
|
+
# {Type2} message for which this object is a response.
|
62
|
+
# @return [true] if +password+ was the password used to generate this
|
63
|
+
# {Type3} message
|
64
|
+
# @return [false] otherwise
|
65
|
+
def password?(password, server_challenge)
|
66
|
+
case ntlm_version
|
67
|
+
when :ntlm2_session
|
68
|
+
ntlm2_session_password?(password, server_challenge)
|
69
|
+
when :ntlmv2
|
70
|
+
ntlmv2_password?(password, server_challenge)
|
71
|
+
else
|
72
|
+
raise
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# @return [Symbol]
|
77
|
+
def ntlm_version
|
78
|
+
if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
|
79
|
+
:ntlm2_session
|
80
|
+
elsif ntlm_response.size == 24
|
81
|
+
:ntlmv1
|
82
|
+
elsif ntlm_response.size > 24
|
83
|
+
:ntlmv2
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def ntlm2_session_password?(password, server_challenge)
|
90
|
+
hash = ntlm_response
|
91
|
+
_lm, empty_hash = NTLM.ntlm2_session(
|
92
|
+
{
|
93
|
+
:ntlm_hash => NTLM.ntlm_hash(password),
|
94
|
+
:challenge => server_challenge,
|
95
|
+
},
|
96
|
+
{
|
97
|
+
:client_challenge => lm_response[0,8]
|
98
|
+
}
|
99
|
+
)
|
100
|
+
hash == empty_hash
|
101
|
+
end
|
102
|
+
|
103
|
+
def ntlmv2_password?(password, server_challenge)
|
104
|
+
|
105
|
+
# The first 16 bytes of the ntlm_response are the HMAC of the blob
|
106
|
+
# that follows it.
|
107
|
+
blob = Blob.new
|
108
|
+
blob.parse(ntlm_response[16..-1])
|
63
109
|
|
110
|
+
empty_hash = NTLM.ntlmv2_response(
|
111
|
+
{
|
112
|
+
# user and domain came from the serialized data here, so
|
113
|
+
# they're already unicode
|
114
|
+
:ntlmv2_hash => NTLM.ntlmv2_hash(user, '', domain, :unicode => true),
|
115
|
+
:challenge => server_challenge,
|
116
|
+
:target_info => blob.target_info
|
117
|
+
},
|
118
|
+
{
|
119
|
+
:client_challenge => blob.challenge,
|
120
|
+
# The blob's timestamp is already in milliseconds since 1601,
|
121
|
+
# so convert it back to epoch time first
|
122
|
+
:timestamp => (blob.timestamp / 10_000_000) - NTLM::TIME_OFFSET,
|
123
|
+
}
|
124
|
+
)
|
125
|
+
|
126
|
+
empty_hash == ntlm_response
|
127
|
+
end
|
128
|
+
end
|
64
129
|
end
|
65
130
|
end
|
66
131
|
end
|
67
|
-
|
68
|
-
|
data/lib/net/ntlm/version.rb
CHANGED
data/rubyntlm.gemspec
CHANGED
@@ -18,10 +18,11 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
20
|
s.required_ruby_version = '>= 1.8.7'
|
21
|
-
|
21
|
+
|
22
22
|
s.license = 'MIT'
|
23
23
|
|
24
|
+
s.add_development_dependency "pry"
|
24
25
|
s.add_development_dependency "rake"
|
25
|
-
s.add_development_dependency "rspec"
|
26
|
+
s.add_development_dependency "rspec", ">= 2.11"
|
26
27
|
s.add_development_dependency "simplecov"
|
27
28
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::Client::Session do
|
4
|
+
let(:t2_challenge) { Net::NTLM::Message.decode64 "TlRMTVNTUAACAAAADAAMADgAAAA1goriAAyk1DmJUnUAAAAAAAAAAFAAUABEAAAABgLwIwAAAA9TAEUAUgBWAEUAUgACAAwAUwBFAFIAVgBFAFIAAQAMAFMARQBSAFYARQBSAAQADABzAGUAcgB2AGUAcgADAAwAcwBlAHIAdgBlAHIABwAIADd7mrNaB9ABAAAAAA==" }
|
5
|
+
let(:inst) { Net::NTLM::Client::Session.new(nil, t2_challenge) }
|
6
|
+
let(:user_session_key) {["3c4918ff0b33e2603e5d7ceaf34bb7d5"].pack("H*")}
|
7
|
+
let(:client_sign_key) {["f7f97a82ec390f9c903dac4f6aceb132"].pack("H*")}
|
8
|
+
let(:client_seal_key) {["6f0d99535033951cbe499cd1914fe9ee"].pack("H*")}
|
9
|
+
let(:server_sign_key) {["f7f97a82ec390f9c903dac4f6aceb132"].pack("H*")}
|
10
|
+
let(:server_seal_key) {["6f0d99535033951cbe499cd1914fe9ee"].pack("H*")}
|
11
|
+
|
12
|
+
describe "#sign_message" do
|
13
|
+
|
14
|
+
it "signs a message and when KEY_EXCHANGE is true" do
|
15
|
+
expect(inst).to receive(:client_sign_key).and_return(client_sign_key)
|
16
|
+
expect(inst).to receive(:client_seal_key).and_return(client_seal_key)
|
17
|
+
expect(inst).to receive(:negotiate_key_exchange?).and_return(true)
|
18
|
+
sm = inst.sign_message("Test Message")
|
19
|
+
str = "01000000b35ccd60c110c52f00000000"
|
20
|
+
expect(sm.unpack("H*")[0]).to eq(str)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#verify_signature" do
|
26
|
+
|
27
|
+
it "verifies a message signature" do
|
28
|
+
expect(inst).to receive(:server_sign_key).and_return(server_sign_key)
|
29
|
+
expect(inst).to receive(:server_seal_key).and_return(server_seal_key)
|
30
|
+
expect(inst).to receive(:negotiate_key_exchange?).and_return(true)
|
31
|
+
sig = "01000000b35ccd60c110c52f00000000"
|
32
|
+
sm = inst.verify_signature([sig].pack("H*"), "Test Message")
|
33
|
+
expect(sm).to be true
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#seal_message" do
|
39
|
+
it "should seal the message" do
|
40
|
+
expect(inst).to receive(:client_seal_key).and_return(client_seal_key)
|
41
|
+
emsg = inst.seal_message("rubyntlm")
|
42
|
+
expect(emsg.unpack("H*")[0]).to eq("d7389b9604f6274f")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#unseal_message" do
|
47
|
+
it "should unseal the message" do
|
48
|
+
expect(inst).to receive(:server_seal_key).and_return(server_seal_key)
|
49
|
+
msg = inst.unseal_message(["d7389b9604f6274f"].pack("H*"))
|
50
|
+
expect(msg).to eq("rubyntlm")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#master_key" do
|
55
|
+
it "returns a random 16-byte key when negotiate_key_exchange? is true" do
|
56
|
+
expect(inst).to receive(:negotiate_key_exchange?).and_return(true)
|
57
|
+
expect(inst).not_to receive(:user_session_key)
|
58
|
+
inst.send :master_key
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns the user_session_key when negotiate_key_exchange? is false" do
|
62
|
+
expect(inst).to receive(:negotiate_key_exchange?).and_return(false)
|
63
|
+
expect(inst).to receive(:user_session_key).and_return(user_session_key)
|
64
|
+
inst.send :master_key
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Net::NTLM::Client do
|
4
|
+
let(:inst) { Net::NTLM::Client.new("test", "test01", :workstation => "testhost") }
|
5
|
+
let(:user_session_key) {["3c4918ff0b33e2603e5d7ceaf34bb7d5"].pack("H*")}
|
6
|
+
|
7
|
+
describe "#init_context" do
|
8
|
+
|
9
|
+
it "returns a default Type1 message" do
|
10
|
+
t1 = inst.init_context
|
11
|
+
expect(t1).to be_instance_of Net::NTLM::Message::Type1
|
12
|
+
expect(t1.domain).to eq("")
|
13
|
+
expect(t1.workstation).to eq("testhost")
|
14
|
+
expect(t1).to have_flag(:UNICODE)
|
15
|
+
expect(t1).to have_flag(:OEM)
|
16
|
+
expect(t1).to have_flag(:SIGN)
|
17
|
+
expect(t1).to have_flag(:SEAL)
|
18
|
+
expect(t1).to have_flag(:REQUEST_TARGET)
|
19
|
+
expect(t1).to have_flag(:NTLM)
|
20
|
+
expect(t1).to have_flag(:ALWAYS_SIGN)
|
21
|
+
expect(t1).to have_flag(:NTLM2_KEY)
|
22
|
+
expect(t1).to have_flag(:KEY128)
|
23
|
+
expect(t1).to have_flag(:KEY_EXCHANGE)
|
24
|
+
expect(t1).to have_flag(:KEY56)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "clears session variable on new init_context" do
|
28
|
+
inst.instance_variable_set :@session, "BADSESSION"
|
29
|
+
expect(inst.session).to eq("BADSESSION")
|
30
|
+
inst.init_context
|
31
|
+
expect(inst.session).to be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns a Type1 message with custom flags" do
|
35
|
+
flags = Net::NTLM::FLAGS[:UNICODE] | Net::NTLM::FLAGS[:REQUEST_TARGET] | Net::NTLM::FLAGS[:NTLM]
|
36
|
+
inst = Net::NTLM::Client.new("test", "test01", :workstation => "testhost", :flags => flags)
|
37
|
+
t1 = inst.init_context
|
38
|
+
expect(t1).to be_instance_of Net::NTLM::Message::Type1
|
39
|
+
expect(t1.domain).to eq("")
|
40
|
+
expect(t1.workstation).to eq("testhost")
|
41
|
+
expect(t1).to have_flag(:UNICODE)
|
42
|
+
expect(t1).not_to have_flag(:OEM)
|
43
|
+
expect(t1).not_to have_flag(:SIGN)
|
44
|
+
expect(t1).not_to have_flag(:SEAL)
|
45
|
+
expect(t1).to have_flag(:REQUEST_TARGET)
|
46
|
+
expect(t1).to have_flag(:NTLM)
|
47
|
+
expect(t1).not_to have_flag(:ALWAYS_SIGN)
|
48
|
+
expect(t1).not_to have_flag(:NTLM2_KEY)
|
49
|
+
expect(t1).not_to have_flag(:KEY128)
|
50
|
+
expect(t1).not_to have_flag(:KEY_EXCHANGE)
|
51
|
+
expect(t1).not_to have_flag(:KEY56)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "calls authenticate! when we receive a Challenge Message" do
|
55
|
+
t2_challenge = "TlRMTVNTUAACAAAADAAMADgAAAA1goriAAyk1DmJUnUAAAAAAAAAAFAAUABEAAAABgLwIwAAAA9TAEUAUgBWAEUAUgACAAwAUwBFAFIAVgBFAFIAAQAMAFMARQBSAFYARQBSAAQADABzAGUAcgB2AGUAcgADAAwAcwBlAHIAdgBlAHIABwAIADd7mrNaB9ABAAAAAA=="
|
56
|
+
session = double("session")
|
57
|
+
expect(session).to receive(:authenticate!)
|
58
|
+
expect(Net::NTLM::Client::Session).to receive(:new).with(inst, instance_of(Net::NTLM::Message::Type2)).and_return(session)
|
59
|
+
inst.init_context t2_challenge
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|