snaury-thin-auth-ntlm 0.0.2 → 0.0.3
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/VERSION.yml +4 -0
- data/lib/thin/ntlm/connection.rb +85 -49
- data/thin-auth-ntlm.gemspec +48 -0
- metadata +8 -5
data/VERSION.yml
ADDED
data/lib/thin/ntlm/connection.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
require 'weakref'
|
1
2
|
require 'win32/sspi/server'
|
2
3
|
|
3
4
|
module Thin
|
4
|
-
class
|
5
|
+
class NTLMWrapper
|
5
6
|
AUTHORIZATION_MESSAGE = <<END
|
6
7
|
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
|
7
8
|
<html><head>
|
@@ -24,34 +25,38 @@ END
|
|
24
25
|
NTLM_REQUEST_PACKAGE = 'NTLM'.freeze
|
25
26
|
NTLM_ALLOWED_PACKAGE = 'NTLM|Negotiate'.freeze
|
26
27
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
super
|
28
|
+
def initialize(app, connection)
|
29
|
+
@app = app
|
30
|
+
@connection = WeakRef.new(connection)
|
31
31
|
end
|
32
32
|
|
33
|
-
def
|
33
|
+
def deferred?(env)
|
34
|
+
@app.respond_to?(:deferred?) && @app.deferred?(env)
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(env)
|
34
38
|
# check if browser wants to reauthenticate
|
35
|
-
if @authenticated_as && http_authorization
|
39
|
+
if @authenticated_as && http_authorization(env)
|
36
40
|
@authenticated_as = nil
|
37
41
|
end
|
42
|
+
|
38
43
|
# require authentication
|
39
44
|
unless @authenticated_as
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
return post_process(result) unless @authenticated_as
|
45
|
+
@connection.ntlm_start
|
46
|
+
@authentication_stage ||= 1
|
47
|
+
result = process(env)
|
48
|
+
return result unless @authenticated_as
|
49
|
+
@connection.ntlm_stop
|
46
50
|
end
|
47
51
|
|
48
|
-
|
49
|
-
|
50
|
-
|
52
|
+
# pass thru
|
53
|
+
env[REMOTE_USER] = @authenticated_as
|
54
|
+
@app.call(env)
|
51
55
|
end
|
52
56
|
|
53
|
-
|
54
|
-
|
57
|
+
# Returns stripped HTTP-Authorization header, nil if none or empty
|
58
|
+
def http_authorization(env)
|
59
|
+
auth = env[HTTP_AUTHORIZATION]
|
55
60
|
if auth
|
56
61
|
auth = auth.strip
|
57
62
|
auth = nil if auth.empty?
|
@@ -59,14 +64,23 @@ END
|
|
59
64
|
auth
|
60
65
|
end
|
61
66
|
|
62
|
-
|
63
|
-
|
67
|
+
# Returns token type and value from HTTP-Authorization header
|
68
|
+
def token(env)
|
69
|
+
auth = http_authorization(env)
|
70
|
+
return [nil, nil] unless auth && auth.match(/\A(#{NTLM_ALLOWED_PACKAGE}) (.*)\Z/)
|
71
|
+
[$1, Base64.decode64($2.strip)]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Acquires new OS credentials handle
|
75
|
+
def acquire(package = 'NTLM')
|
76
|
+
cleanup
|
64
77
|
@ntlm = Win32::SSPI::NegotiateServer.new(package)
|
65
78
|
@ntlm.acquire_credentials_handle
|
66
79
|
@ntlm
|
67
80
|
end
|
68
81
|
|
69
|
-
|
82
|
+
# Frees credentials handle, if acquired
|
83
|
+
def cleanup
|
70
84
|
if @ntlm
|
71
85
|
@ntlm.cleanup rescue nil
|
72
86
|
@ntlm = nil
|
@@ -74,55 +88,77 @@ END
|
|
74
88
|
nil
|
75
89
|
end
|
76
90
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
def ntlm_process
|
91
|
+
# Processes current authentication stage
|
92
|
+
# Returns rack response if authentication is incomplete
|
93
|
+
# Sets @authenticated_as to username if authentication successful
|
94
|
+
def process(env)
|
84
95
|
case @authentication_stage
|
85
96
|
when 1 # we are waiting for type1 message
|
86
|
-
package, t1 =
|
87
|
-
return
|
88
|
-
return
|
97
|
+
package, t1 = token(env)
|
98
|
+
return request_auth(NTLM_REQUEST_PACKAGE, false) if t1.nil?
|
99
|
+
return request_auth unless request.persistent?
|
89
100
|
begin
|
90
|
-
|
101
|
+
acquire(package)
|
91
102
|
t2 = @ntlm.accept_security_context(t1)
|
92
103
|
rescue
|
93
|
-
return
|
104
|
+
return request_auth
|
94
105
|
end
|
95
|
-
|
106
|
+
request_auth("#{package} #{t2}", false, 2)
|
96
107
|
when 2 # we are waiting for type3 message
|
97
|
-
package, t3 =
|
98
|
-
return
|
99
|
-
return
|
100
|
-
return
|
108
|
+
package, t3 = token(env)
|
109
|
+
return request_auth(NTLM_REQUEST_PACKAGE, false) if t3.nil?
|
110
|
+
return request_auth unless package == @ntlm.package
|
111
|
+
return request_auth unless request.persistent?
|
101
112
|
begin
|
102
113
|
t2 = @ntlm.accept_security_context(t3)
|
103
114
|
@authenticated_as = @ntlm.get_username_from_context
|
104
|
-
@authentication_stage =
|
115
|
+
@authentication_stage = nil # in case IE wants to reauthenticate
|
105
116
|
rescue
|
106
|
-
return
|
117
|
+
return request_auth
|
107
118
|
end
|
108
|
-
return
|
109
|
-
|
119
|
+
return request_auth unless @authenticated_as
|
120
|
+
cleanup
|
110
121
|
else
|
111
122
|
raise "Invalid value for @authentication_stage=#{@authentication_stage} detected"
|
112
123
|
end
|
113
|
-
rescue Exception
|
114
|
-
handle_error
|
115
|
-
terminate_request
|
116
|
-
nil
|
117
124
|
end
|
118
125
|
|
119
|
-
|
126
|
+
# Returns response with authentication request to the client
|
127
|
+
def request_auth(auth = nil, finished = true, next_stage = 1)
|
120
128
|
@authentication_stage = next_stage
|
121
|
-
@can_persist
|
129
|
+
@connection.can_persist! unless finished
|
122
130
|
head = {}
|
123
131
|
head[WWW_AUTHENTICATE] = auth if auth
|
124
132
|
head[CONTENT_TYPE] = CONTENT_TYPE_AUTH
|
125
|
-
|
133
|
+
[401, head, [AUTHORIZATION_MESSAGE]]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class NTLMConnection < Connection
|
138
|
+
def app=(app)
|
139
|
+
super NTLMWrapper.new(app, self)
|
140
|
+
end
|
141
|
+
|
142
|
+
def unbind
|
143
|
+
@app.cleanup if @app && @app.respond_to?(:cleanup)
|
144
|
+
ensure
|
145
|
+
super
|
146
|
+
end
|
147
|
+
|
148
|
+
# Saves original can_persist? value (NTLM will force persistence)
|
149
|
+
def ntlm_start
|
150
|
+
unless @ntlm_in_progress
|
151
|
+
@ntlm_saved_can_persist = @can_persist
|
152
|
+
@ntlm_in_progress = true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Restores previous can_persist? value
|
157
|
+
def ntlm_stop
|
158
|
+
if @ntlm_in_progress
|
159
|
+
@can_persist = @ntlm_saved_can_persist
|
160
|
+
@ntlm_in_progress = false
|
161
|
+
end
|
126
162
|
end
|
127
163
|
end
|
128
164
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{thin-auth-ntlm}
|
8
|
+
s.version = "0.0.3"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Alexey Borzenkov"]
|
12
|
+
s.date = %q{2009-08-12}
|
13
|
+
s.email = %q{snaury@gmail.com}
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE.txt",
|
16
|
+
"README.txt"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
"LICENSE.txt",
|
20
|
+
"README.txt",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION.yml",
|
23
|
+
"lib/thin-auth-ntlm.rb",
|
24
|
+
"lib/thin/ntlm/backends/tcp_server.rb",
|
25
|
+
"lib/thin/ntlm/connection.rb",
|
26
|
+
"thin-auth-ntlm.gemspec"
|
27
|
+
]
|
28
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
29
|
+
s.require_paths = ["lib"]
|
30
|
+
s.rubygems_version = %q{1.3.4}
|
31
|
+
s.summary = %q{Allows you to force NTLM authentication on Thin TCP servers.}
|
32
|
+
|
33
|
+
if s.respond_to? :specification_version then
|
34
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
35
|
+
s.specification_version = 3
|
36
|
+
|
37
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
38
|
+
s.add_runtime_dependency(%q<thin>, [">= 1.0.0"])
|
39
|
+
s.add_runtime_dependency(%q<rubysspi-server>, [">= 0.0.1"])
|
40
|
+
else
|
41
|
+
s.add_dependency(%q<thin>, [">= 1.0.0"])
|
42
|
+
s.add_dependency(%q<rubysspi-server>, [">= 0.0.1"])
|
43
|
+
end
|
44
|
+
else
|
45
|
+
s.add_dependency(%q<thin>, [">= 1.0.0"])
|
46
|
+
s.add_dependency(%q<rubysspi-server>, [">= 0.0.1"])
|
47
|
+
end
|
48
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: snaury-thin-auth-ntlm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexey Borzenkov
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-12 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -45,11 +45,14 @@ files:
|
|
45
45
|
- LICENSE.txt
|
46
46
|
- README.txt
|
47
47
|
- Rakefile
|
48
|
+
- VERSION.yml
|
48
49
|
- lib/thin-auth-ntlm.rb
|
49
50
|
- lib/thin/ntlm/backends/tcp_server.rb
|
50
51
|
- lib/thin/ntlm/connection.rb
|
51
|
-
|
52
|
+
- thin-auth-ntlm.gemspec
|
53
|
+
has_rdoc: false
|
52
54
|
homepage:
|
55
|
+
licenses:
|
53
56
|
post_install_message:
|
54
57
|
rdoc_options:
|
55
58
|
- --charset=UTF-8
|
@@ -70,9 +73,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
73
|
requirements: []
|
71
74
|
|
72
75
|
rubyforge_project:
|
73
|
-
rubygems_version: 1.
|
76
|
+
rubygems_version: 1.3.5
|
74
77
|
signing_key:
|
75
|
-
specification_version:
|
78
|
+
specification_version: 3
|
76
79
|
summary: Allows you to force NTLM authentication on Thin TCP servers.
|
77
80
|
test_files: []
|
78
81
|
|