rubysspi 0.0.1-i386-mswin32

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.
data/README.txt ADDED
@@ -0,0 +1,93 @@
1
+ = Introduction
2
+
3
+ This library provides bindings to the Win32 SSPI libraries, which implement various security protocols for Windows. The library was primarily developed to give Negotiate/NTLM proxy authentication abilities to Net::HTTP, similar to support found in Internet Explorer or Firefox.
4
+
5
+ The libary is NOT an implementation of the NTLM protocol, and does not give the ability to authenticate as any given user. It is able to authenticate with a proxy server as the current user.
6
+
7
+ This project can be found on rubyforge at:
8
+
9
+ http://rubyforge.org/projects/rubysspi
10
+
11
+ = Using with open-uri
12
+
13
+ To use the library with open-uri, make sure to set the environment variable +http_proxy+ to your proxy server. This must be a hostname and port in URL form. E.g.:
14
+
15
+ http://proxy.corp.com:8080
16
+
17
+ The library will grab your current username and domain from the environment variables +USERNAME+ and +USERDOMAIN+. This should be set for you by Windows already.
18
+
19
+ The library implements a patch on top of Net::HTTP, which means open-uri gets it too. At the top of your script, make sure to require the patch after +open-uri+:
20
+
21
+ require +open-uri+
22
+ require +rubysspi/proxy_auth+
23
+
24
+ open("http://www.google.com") { |f| puts(f.gets(nil)) }
25
+
26
+ Note that this patch does NOT work with the +http_proxy_user+ and +http_proxy_password+ environment variables. The library will ONLY authenticate as the current user.
27
+
28
+ = Using with Net::HTTP
29
+
30
+ Net::HTTP will not use the proxy server supplied in the environment variable automatically, so you have to supply the proxy address yourself. Otherwise, it's exactly the same:
31
+
32
+ require 'net/http'
33
+ require 'rubysspi/proxy_auth+
34
+
35
+ Net::HTTP::Proxy("proxy.corp.com", 8080).start("www.google.com") do |http|
36
+ resp = http.request_get "/"
37
+ puts resp.body
38
+ end
39
+
40
+ = Using rubysspi directly
41
+
42
+ As stated, the library is geared primarily towards supporting Negotiate/NTLM authentication with proxy servers. In this vein, you can manually authenticate a given HTTP connection with a single call:
43
+
44
+ require 'rubysspi'
45
+
46
+ Net::HTTP.Proxy(proxy.host, proxy.port).start("www.google.com") do |http|
47
+ resp = SSPI::NegotiateAuth.proxy_auth_get http, "/"
48
+ end
49
+
50
+ The +resp+ variable will contain the response from Google, with any proxy authorization necessary taken care of automatically. Note that if the +http+ connection is not closed, any subsequent requests will NOT require authentication.
51
+
52
+ If the above method is used, it is recommended that you do NOT require the 'rubysspi/proxy_auth' library, as the interaction between the two will fail.
53
+
54
+ The library can be used directly to generate tokens appropriate for the current user, too.
55
+
56
+ To get started, first create an instance of the SSPI::NegotiateAuth class:
57
+
58
+ require 'rubysspi'
59
+
60
+ n = SSPI::NegotiateAuth.new
61
+
62
+ Next, get the first token by calling get_initial_token:
63
+
64
+ token = n.get_initial_token
65
+
66
+ This token returned will be Base64 encoded and can be directly placed in an HTTP header. This token can be easily decoded, however, and is usually an NTLM Type 1 message.
67
+
68
+ After getting a response from the server (usually an NTLM Type 2 message), pass it into the complete_authentication:
69
+
70
+ token = n.complete_authentication(server_token)
71
+
72
+ Note that server_token can be Base64 encoded or not, and if it starts with "Negotiate", that phrase will be stripped off. This allows the response from a Proxy-Authentication header to be passed into the method directly. The token can be decoded externally and passed in, too.
73
+
74
+ The token returned (usually an NTLM Type 3) message can then be sent to the server and the connection should be authenticated.
75
+
76
+ = Thanks & References
77
+
78
+ Many references were used in decoding both NTLM messages and integrating with the SSPI library. Among them are:
79
+
80
+ * Managed SSPI Sample - A .NET implementation of a simple client/server using SSPI. A complex undertaking but provides a great resource for playing with the API.
81
+ * http://msdn.microsoft.com/library/?url=/library/en-us/dndotnet/html/remsspi.asp?frame=true
82
+ * John Lam's RubyCLR - http://www.rubyclr.com
83
+ * Originally, I used RubyCLR to call into the Managed SSPI sample which really helped me decode what the SSPI interface did and how it worked. I did not end up using that implementation but it was great for research.
84
+ * The NTLM Authentication Protocol - The definitive explanation for the NTLM protocol (outside MS internal documents, I presume).
85
+ * http://davenport.sourceforge.net/ntlm.html
86
+ * Ruby/NTLM - A pure Ruby implementation of the NTLM protocol. Again, not used in this project but invaluable for decoding NTLM messages and figuring out what SSPI was returning.
87
+ * http://rubyforge.org/projects/rubyntlm/
88
+ * Seamonkey/Mozilla NTLM implementation - The only source for an implementation in an actual browser. How they figured out how to use SSPI themselves is beyond me.
89
+ * http://lxr.mozilla.org/seamonkey/source/mailnews/base/util/nsMsgProtocol.cpp#899
90
+
91
+ And of course, thanks to my Lord and Savior, Jesus Christ. In the name of the Father, the Son, and the Holy Spirit.
92
+
93
+
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ Gem::manage_gems
3
+ require 'rake/gempackagetask'
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = "rubysspi"
7
+ s.summary = "A library which adds implements Ruby bindings to the Win32 SSPI library. Also includes a module to add Negotate authentication support to Net::HTTP."
8
+ s.version = "0.0.1"
9
+ s.author = "Justin Bailey"
10
+ s.email = "jgbailey @nospan@ gmail.com"
11
+ s.homepage = "http://rubyforge.org/projects/rubysspi/"
12
+ s.rubyforge_project = "http://rubyforge.org/projects/rubysspi/"
13
+ s.description = <<EOS
14
+ This gem provides bindings to the Win32 SSPI libraries, primarily to support Negotiate (i.e. SPNEGO, NTLM)
15
+ authentication with a proxy server. Enough support is implemented to provide the necessary support for
16
+ the authentication.
17
+
18
+ A module is also provided which overrides Net::HTTP and adds support for Negotiate authentication to it.
19
+
20
+ This implies that open-uri automatically gets support for it, as long as the http_proxy environment variable
21
+ is set.
22
+ EOS
23
+
24
+ s.platform = Gem::Platform::CURRENT
25
+ s.files = FileList["lib/**/*", "test/*", "*.txt", "Rakefile"].to_a
26
+
27
+ s.require_path = "lib"
28
+ s.autorequire = "rubysspi"
29
+
30
+ s.has_rdoc = true
31
+ s.extra_rdoc_files = ["README.txt"]
32
+ s.rdoc_options << '--title' << 'Ruby SSPI -- Win32 SSPI Bindings for Ruby' <<
33
+ '--main' << 'README.txt' <<
34
+ '--line-numbers'
35
+ end
36
+
37
+ Rake::GemPackageTask.new(spec) do |pkg|
38
+ pkg.need_tar = true
39
+ end
data/lib/rubysspi.rb ADDED
@@ -0,0 +1,303 @@
1
+ require 'dl/win32'
2
+ require 'base64'
3
+
4
+ # Implements bindings to Win32 SSPI functions, focused on authentication to a proxy server over HTTP.
5
+ module SSPI
6
+ # Specifies how credential structure requested will be used. Only SECPKG_CRED_OUTBOUND is used
7
+ # here.
8
+ SECPKG_CRED_INBOUND = 0x00000001
9
+ SECPKG_CRED_OUTBOUND = 0x00000002
10
+ SECPKG_CRED_BOTH = 0x00000003
11
+
12
+ # Format of token. NETWORK format is used here.
13
+ SECURITY_NATIVE_DREP = 0x00000010
14
+ SECURITY_NETWORK_DREP = 0x00000000
15
+
16
+ # InitializeSecurityContext Requirement flags
17
+ ISC_REQ_REPLAY_DETECT = 0x00000004
18
+ ISC_REQ_SEQUENCE_DETECT = 0x00000008
19
+ ISC_REQ_CONFIDENTIALITY = 0x00000010
20
+ ISC_REQ_USE_SESSION_KEY = 0x00000020
21
+ ISC_REQ_PROMPT_FOR_CREDS = 0x00000040
22
+ ISC_REQ_CONNECTION = 0x00000800
23
+
24
+ # Win32 API Functions. Uses dl/win32 to bind methods to constants contained in class.
25
+ module API
26
+ # Can be called with AcquireCredentialsHandle.call()
27
+ AcquireCredentialsHandle = Win32API.new("secur32", "AcquireCredentialsHandle", ['ppLpppppp'], 'L')
28
+ # Can be called with AcquireCredentialsHandle.call()
29
+ InitializeSecurityContext = Win32API.new("secur32", "InitializeSecurityContext", [ 'pppLLLpLpppp'], 'L')
30
+ # Can be called with AcquireCredentialsHandle.call()
31
+ DeleteSecurityContext = Win32API.new("secur32", "DeleteSecurityContext", ['P'], 'L')
32
+ # Can be called with AcquireCredentialsHandle.call()
33
+ FreeCredentialsHandle = Win32API.new("secur32", "FreeCredentialsHandle", ['P'], 'L')
34
+ end
35
+
36
+ # SecHandle struct
37
+ class SecurityHandle
38
+ def upper
39
+ @struct.unpack("LL")[1]
40
+ end
41
+
42
+ def lower
43
+ @struct.unpack("LL")[0]
44
+ end
45
+
46
+ def to_p
47
+ @struct ||= "\0" * 8
48
+ end
49
+ end
50
+
51
+ # Some familiar aliases for the SecHandle structure
52
+ CredHandle = CtxtHandle = SecurityHandle
53
+
54
+ # TimeStamp struct
55
+ class TimeStamp
56
+ attr_reader :struct
57
+
58
+ def to_p
59
+ @struct ||= "\0" * 8
60
+ end
61
+ end
62
+
63
+ # Creates binary representaiton of a SecBufferDesc structure,
64
+ # including the SecBuffer contained inside.
65
+ class SecurityBuffer
66
+
67
+ SECBUFFER_TOKEN = 2 # Security token
68
+
69
+ TOKENBUFSIZE = 12288
70
+ SECBUFFER_VERSION = 0
71
+
72
+ def initialize(buffer = nil)
73
+ @buffer = buffer || "\0" * TOKENBUFSIZE
74
+ @bufferSize = @buffer.length
75
+ @type = SECBUFFER_TOKEN
76
+ end
77
+
78
+ def bufferSize
79
+ unpack
80
+ @bufferSize
81
+ end
82
+
83
+ def bufferType
84
+ unpack
85
+ @type
86
+ end
87
+
88
+ def token
89
+ unpack
90
+ @buffer
91
+ end
92
+
93
+ def to_p
94
+ # Assumption is that when to_p is called we are going to get a packed structure. Therefore,
95
+ # set @unpacked back to nil so we know to unpack when accessors are next accessed.
96
+ @unpacked = nil
97
+ # Assignment of inner structure to variable is very important here. Without it,
98
+ # will not be able to unpack changes to the structure. Alternative, nested unpacks,
99
+ # does not work (i.e. @struct.unpack("LLP12")[2].unpack("LLP12") results in "no associated pointer")
100
+ @sec_buffer ||= [@bufferSize, @type, @buffer].pack("LLP")
101
+ @struct ||= [SECBUFFER_VERSION, 1, @sec_buffer].pack("LLP")
102
+ end
103
+
104
+ private
105
+
106
+ # Unpacks the SecurityBufferDesc structure into member variables. We
107
+ # only want to do this once per struct, so the struct is deleted
108
+ # after unpacking.
109
+ def unpack
110
+ if ! @unpacked && @sec_buffer && @struct
111
+ @bufferSize, @type = @sec_buffer.unpack("LL")
112
+ @buffer = @sec_buffer.unpack("LLP#{@bufferSize}")[2]
113
+ @struct = nil
114
+ @sec_buffer = nil
115
+ @unpacked = true
116
+ end
117
+ end
118
+ end
119
+
120
+ # SEC_WINNT_AUTH_IDENTITY structure
121
+ class Identity
122
+ SEC_WINNT_AUTH_IDENTITY_ANSI = 0x1
123
+
124
+ attr_accessor :user, :domain, :password
125
+
126
+ def initialize(user = nil, domain = nil, password = nil)
127
+ @user = user
128
+ @domain = domain
129
+ @password = password
130
+ @flags = SEC_WINNT_AUTH_IDENTITY_ANSI
131
+ end
132
+
133
+ def to_p
134
+ [@user, @user ? @user.length : 0,
135
+ @domain, @domain ? @domain.length : 0,
136
+ @password, @password ? @password.length : 0,
137
+ @flags].pack("PLPLPLL")
138
+ end
139
+ end
140
+
141
+ # Takes a return result from an SSPI function and interprets the value.
142
+ class SSPIResult
143
+ # Good results
144
+ SEC_E_OK = 0x00000000
145
+ SEC_I_CONTINUE_NEEDED = 0x00090312
146
+
147
+ # These are generally returned by InitializeSecurityContext
148
+ SEC_E_INSUFFICIENT_MEMORY = 0x80090300
149
+ SEC_E_INTERNAL_ERROR = 0x80090304
150
+ SEC_E_INVALID_HANDLE = 0x80090301
151
+ SEC_E_INVALID_TOKEN = 0x80090308
152
+ SEC_E_LOGON_DENIED = 0x8009030C
153
+ SEC_E_NO_AUTHENTICATING_AUTHORITY = 0x80090311
154
+ SEC_E_NO_CREDENTIALS = 0x8009030E
155
+ SEC_E_TARGET_UNKNOWN = 0x80090303
156
+ SEC_E_UNSUPPORTED_FUNCTION = 0x80090302
157
+ SEC_E_WRONG_PRINCIPAL = 0x80090322
158
+
159
+ # These are generally returned by AcquireCredentialsHandle
160
+ SEC_E_NOT_OWNER = 0x80090306
161
+ SEC_E_SECPKG_NOT_FOUND = 0x80090305
162
+ SEC_E_UNKNOWN_CREDENTIALS = 0x8009030D
163
+
164
+ @@map = {}
165
+ constants.each { |v| @@map[self.const_get(v.to_s)] = v }
166
+
167
+ attr_reader :value
168
+
169
+ def initialize(value)
170
+ # convert to unsigned long
171
+ value = [value].pack("L").unpack("L").first
172
+ raise "#{value.to_s(16)} is not a recognized result" unless @@map.has_key? value
173
+ @value = value
174
+ end
175
+
176
+ def to_s
177
+ @@map[@value].to_s
178
+ end
179
+
180
+ def ok?
181
+ @value == SEC_I_CONTINUE_NEEDED || @value == SEC_E_OK
182
+ end
183
+
184
+ def ==(other)
185
+ if other.is_a?(SSPIResult)
186
+ @value == other.value
187
+ elsif other.is_a?(Fixnum)
188
+ @value == @@map[other]
189
+ else
190
+ false
191
+ end
192
+ end
193
+ end
194
+
195
+ # Handles "Negotiate" type authentication. Geared towards authenticating with a proxy server over HTTP
196
+ class NegotiateAuth
197
+ attr_accessor :credentials, :context, :contextAttributes, :user, :domain
198
+ REQUEST_FLAGS = ISC_REQ_CONFIDENTIALITY | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONNECTION
199
+
200
+ # Given a connection and a request path, performs authentication as the current user and returns
201
+ # the response from a GET request. The connnection should be a Net::HTTP object, and it should
202
+ # have been constructed using the Net::HTTP.Proxy method, but anything that responds to "get" will work.
203
+ # If a user and domain are given, will authenticate as the given user.
204
+ # Returns the response received from the get method (usually Net::HTTPResponse)
205
+ def NegotiateAuth.proxy_auth_get(http, path, user = nil, domain = nil)
206
+ raise "http must respond to :get" unless http.respond_to?(:get)
207
+ nego_auth = self.new user, domain
208
+
209
+ resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.get_initial_token }
210
+ if resp["Proxy-Authenticate"]
211
+ resp = http.get path, { "Proxy-Authorization" => "Negotiate " + nego_auth.complete_authentication(resp["Proxy-Authenticate"].split(" ").last.strip) }
212
+ end
213
+
214
+ resp
215
+ end
216
+
217
+ # Creates a new instance ready for authentication as the given user in the given domain.
218
+ # Defaults to current user and domain as defined by ENV["USERDOMAIN"] and ENV["USERNAME"] if
219
+ # no arguments are supplied.
220
+ def initialize(user = nil, domain = nil)
221
+ if user.nil? && domain.nil? && ENV["USERNAME"].nil? && ENV["USERDOMAIN"].nil?
222
+ raise "A username or domain must be supplied since they cannot be retrieved from the environment"
223
+ end
224
+
225
+ @user = user || ENV["USERNAME"]
226
+ @domain = domain || ENV["USERDOMAIN"]
227
+ end
228
+
229
+ # Gets the initial Negotiate token. Returns it as a base64 encoded string suitable for use in HTTP. Can
230
+ # be easily decoded, however.
231
+ def get_initial_token
232
+ raise "This object is no longer usable because its resources have been freed." if @cleaned_up
233
+ get_credentials
234
+
235
+ outputBuffer = SecurityBuffer.new
236
+ @context = CtxtHandle.new
237
+ @contextAttributes = "\0" * 4
238
+
239
+ result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, nil, nil,
240
+ REQUEST_FLAGS,0, SECURITY_NETWORK_DREP, nil, 0, @context.to_p, outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p))
241
+
242
+ if result.ok? then
243
+ return encode_token(outputBuffer.token)
244
+ else
245
+ raise "Error: #{result.to_s}"
246
+ end
247
+ end
248
+
249
+ # Takes a Base64 encoded token and gets the next token in the
250
+ # Negotiate authentication chain. The token can include the "Negotiate" header and it will be stripped.
251
+ # Does not indicate if SEC_I_CONTINUE or SEC_E_OK was returned.
252
+ # Token returned is Base64 encoded.
253
+ def complete_authentication(token)
254
+ raise "This object is no longer usable because its resources have been freed." if @cleaned_up
255
+
256
+ # If the Negotiate prefix is passed in, assume we are seeing "Negotiate <token>" and get the token.
257
+ if token.include? "Negotiate"
258
+ token = token.split(" ").last.strip
259
+ end
260
+ outputBuffer = SecurityBuffer.new
261
+
262
+ result = SSPIResult.new(API::InitializeSecurityContext.call(@credentials.to_p, @context.to_p, nil,
263
+ REQUEST_FLAGS, 0, SECURITY_NETWORK_DREP, SecurityBuffer.new(Base64.decode64(token)).to_p, 0,
264
+ @context.to_p,
265
+ outputBuffer.to_p, @contextAttributes, TimeStamp.new.to_p))
266
+
267
+ if result.ok? then
268
+ return encode_token(outputBuffer.token)
269
+ else
270
+ raise "Error: #{result.to_s}"
271
+ end
272
+ ensure
273
+ # need to make sure we don't clean up if we've already cleaned up.
274
+ clean_up unless @cleaned_up
275
+ end
276
+
277
+ private
278
+
279
+ def clean_up
280
+ # free structures allocated
281
+ @cleaned_up = true
282
+ API::FreeCredentialsHandle.call(@credentials.to_p)
283
+ API::DeleteSecurityContext.call(@context.to_p)
284
+ @context = nil
285
+ @credentials = nil
286
+ @contextAttributes = nil
287
+ end
288
+
289
+ # Gets credentials based on user, domain or both. If both are nil, an error occurs
290
+ def get_credentials
291
+ @credentials = CredHandle.new
292
+ ts = TimeStamp.new
293
+ @identity = Identity.new @user, @domain
294
+ result = SSPIResult.new(API::AcquireCredentialsHandle.call(nil, "Negotiate", SECPKG_CRED_OUTBOUND, nil, @identity.to_p, nil, nil, @credentials.to_p, ts.to_p))
295
+ raise "Error acquire credentials: #{result}" unless result.ok?
296
+ end
297
+
298
+ def encode_token(t)
299
+ # encode64 will add newlines every 60 characters so we need to remove those.
300
+ Base64.encode64(t).gsub(/\n/, "")
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,56 @@
1
+ require 'net/http'
2
+ require 'rubysspi'
3
+
4
+
5
+ module Net
6
+ # Replaces Net::HTTP.request to understand Negotate HTTP proxy authorization. Uses native Win32
7
+ # libraries to authentic as the current user (as defined by the ENV["USERNAME"] and ENV["USERDOMAIN"]
8
+ # environment variables.
9
+ class HTTP
10
+ def request(req, body = nil, &block) # :yield: +response+
11
+ unless started?
12
+ start {
13
+ req['connection'] ||= 'close'
14
+ return request(req, body, &block)
15
+ }
16
+ end
17
+ if proxy_user()
18
+ unless use_ssl?
19
+ req.proxy_basic_auth proxy_user(), proxy_pass()
20
+ end
21
+ end
22
+
23
+ req.set_body_internal body
24
+ begin_transport req
25
+ req.exec @socket, @curr_http_version, edit_path(req.path)
26
+ begin
27
+ res = HTTPResponse.read_new(@socket)
28
+ end while res.kind_of?(HTTPContinue)
29
+ # If proxy was specified, and the server is demanding authentication, negotatiate with it
30
+ if proxy? && res.code.to_i == 407 && res["Proxy-Authenticate"].include?("Negotiate")
31
+ n = SSPI::NegotiateAuth.new
32
+ res.reading_body(@socket, req.response_body_permitted?) { }
33
+ req["Proxy-Authorization"] = "Negotiate #{n.get_initial_token}"
34
+ req.exec @socket, @curr_http_version, edit_path(req.path)
35
+ begin
36
+ res = HTTPResponse.read_new(@socket)
37
+ end while res.kind_of?(HTTPContinue)
38
+
39
+ if res["Proxy-Authenticate"]
40
+ res.reading_body(@socket, req.response_body_permitted?) { }
41
+ req["Proxy-Authorization"] = "Negotiate #{n.complete_authentication res["Proxy-Authenticate"]}"
42
+ req.exec @socket, @curr_http_version, edit_path(req.path)
43
+ begin
44
+ res = HTTPResponse.read_new(@socket)
45
+ end while res.kind_of?(HTTPContinue)
46
+ end
47
+ end
48
+ res.reading_body(@socket, req.response_body_permitted?) {
49
+ yield res if block_given?
50
+ }
51
+ end_transport req, res
52
+
53
+ res
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,54 @@
1
+ require 'test/unit'
2
+ require 'net/http'
3
+ require 'pathname'
4
+ $: << (File.dirname(__FILE__) << "/../lib")
5
+ require 'rubysspi'
6
+
7
+ class NTLMTest < Test::Unit::TestCase
8
+
9
+ def setup
10
+ assert ENV["http_proxy"], "http_proxy must be set before running tests."
11
+ end
12
+
13
+ def test_auth
14
+ assert ENV["http_proxy"], "http_proxy environment variable must be set."
15
+ proxy = URI.parse(ENV["http_proxy"])
16
+ assert proxy.host && proxy.port, "Could not parse http_proxy (#{ENV["http_proxy"]}). http_proxy should be a URL with a port (e.g. http://proxy.corp.com:8080)."
17
+
18
+ Net::HTTP.Proxy(proxy.host, proxy.port).start("www.google.com") do |http|
19
+ nego_auth = SSPI::NegotiateAuth.new
20
+ sr = http.request_get "/", { "Proxy-Authorization" => "Negotiate " + nego_auth.get_initial_token }
21
+ resp = http.get "/", { "Proxy-Authorization" => "Negotiate " + nego_auth.complete_authentication(sr["Proxy-Authenticate"].split(" ").last.strip) }
22
+ assert resp.code.to_i == 200, "Resposne code not as expected: #{resp.inspect}"
23
+ resp = http.get "/foobar.html"
24
+ assert resp.code.to_i == 404, "Response code not as expected: #{resp.inspect}"
25
+ end
26
+ end
27
+
28
+ def test_proxy_auth_get
29
+ assert ENV["http_proxy"], "http_proxy environment variable must be set."
30
+ proxy = URI.parse(ENV["http_proxy"])
31
+ assert proxy.host && proxy.port, "Could not parse http_proxy (#{ENV["http_proxy"]}). http_proxy should be a URL with a port (e.g. http://proxy.corp.com:8080)."
32
+
33
+ Net::HTTP.Proxy(proxy.host, proxy.port).start("www.google.com") do |http|
34
+ resp = SSPI::NegotiateAuth.proxy_auth_get http, "/"
35
+ assert resp.code.to_i == 200, "Response code not as expected: #{resp.inspect}"
36
+ end
37
+ end
38
+
39
+ def test_one_time_use_only
40
+ assert ENV["http_proxy"], "http_proxy environment variable must be set."
41
+ proxy = URI.parse(ENV["http_proxy"])
42
+ assert proxy.host && proxy.port, "Could not parse http_proxy (#{ENV["http_proxy"]}). http_proxy should be a URL with a port (e.g. http://proxy.corp.com:8080)."
43
+
44
+ Net::HTTP.Proxy(proxy.host, proxy.port).start("www.google.com") do |http|
45
+ nego_auth = SSPI::NegotiateAuth.new
46
+ sr = http.request_get "/", { "Proxy-Authorization" => "Negotiate " + nego_auth.get_initial_token }
47
+ resp = http.get "/", { "Proxy-Authorization" => "Negotiate " + nego_auth.complete_authentication(sr["Proxy-Authenticate"].split(" ").last.strip) }
48
+ assert resp.code.to_i == 200, "Response code not as expected: #{resp.inspect}"
49
+ assert_raises(RuntimeError, "Should not be able to call complete_authentication again") do
50
+ nego_auth.complete_authentication "foo"
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+ require 'test/unit'
2
+ require 'net/http'
3
+ require 'pathname'
4
+ $: << (File.dirname(__FILE__) << "/../lib")
5
+ require 'rubysspi/proxy_auth'
6
+
7
+ class NTLMTest < Test::Unit::TestCase
8
+ def setup
9
+ assert ENV["http_proxy"], "http_proxy must be set before running tests."
10
+ end
11
+
12
+ def test_net_http
13
+
14
+ assert ENV["http_proxy"], "http_proxy environment variable must be set."
15
+ proxy = URI.parse(ENV["http_proxy"])
16
+ assert proxy.host && proxy.port, "Could not parse http_proxy (#{ENV["http_proxy"]}). http_proxy should be a URL with a port (e.g. http://proxy.corp.com:8080)."
17
+
18
+ Net::HTTP.Proxy(proxy.host, proxy.port).start("www.google.com") do |http|
19
+ resp = http.get("/")
20
+ assert resp.code.to_i == 200, "Did not get response from Google as expected."
21
+ end
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: rubysspi
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.0.1
7
+ date: 2006-07-14 00:00:00 -07:00
8
+ summary: A library which adds implements Ruby bindings to the Win32 SSPI library. Also includes a module to add Negotate authentication support to Net::HTTP.
9
+ require_paths:
10
+ - lib
11
+ email: jgbailey @nospan@ gmail.com
12
+ homepage: http://rubyforge.org/projects/rubysspi/
13
+ rubyforge_project: http://rubyforge.org/projects/rubysspi/
14
+ description: This gem provides bindings to the Win32 SSPI libraries, primarily to support Negotiate (i.e. SPNEGO, NTLM) authentication with a proxy server. Enough support is implemented to provide the necessary support for the authentication. A module is also provided which overrides Net::HTTP and adds support for Negotiate authentication to it. This implies that open-uri automatically gets support for it, as long as the http_proxy environment variable is set.
15
+ autorequire: rubysspi
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: i386-mswin32
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Justin Bailey
31
+ files:
32
+ - lib/rubysspi
33
+ - lib/rubysspi.rb
34
+ - lib/rubysspi/proxy_auth.rb
35
+ - test/ruby_sspi_test.rb
36
+ - test/test_net_http.rb
37
+ - README.txt
38
+ - Rakefile
39
+ test_files: []
40
+
41
+ rdoc_options:
42
+ - --title
43
+ - Ruby SSPI -- Win32 SSPI Bindings for Ruby
44
+ - --main
45
+ - README.txt
46
+ - --line-numbers
47
+ extra_rdoc_files:
48
+ - README.txt
49
+ executables: []
50
+
51
+ extensions: []
52
+
53
+ requirements: []
54
+
55
+ dependencies: []
56
+