thin-auth-ntlm 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2009 Alexey Borzenkov
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.txt ADDED
@@ -0,0 +1,11 @@
1
+ = NTLM Authentication for Thin
2
+
3
+ Allows you to force NTLM authentication on Thin TCP servers.
4
+
5
+ = Using thin-auth-ntlm
6
+
7
+ Just start your thin server using NTLMTcpServer backend:
8
+
9
+ thin -r thin-auth-ntlm -b Thin::Backends::NTLMTcpServer start
10
+
11
+ Remote username will be available as request.remote_user.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "thin-auth-ntlm"
5
+ gemspec.summary = "Allows you to force NTLM authentication on Thin TCP servers."
6
+ gemspec.author = "Alexey Borzenkov"
7
+ gemspec.email = "snaury@gmail.com"
8
+
9
+ gemspec.add_dependency('thin', '>= 1.0.0')
10
+ gemspec.add_dependency('rubysspi-server', '>= 0.0.1')
11
+ end
12
+ rescue LoadError
13
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
14
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 0
4
+ :patch: 4
@@ -0,0 +1,9 @@
1
+ require 'thin'
2
+
3
+ module Thin
4
+ autoload :NTLMConnection, 'thin/ntlm/connection'
5
+
6
+ module Backends
7
+ autoload :NTLMTcpServer, 'thin/ntlm/backends/tcp_server'
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module Thin
2
+ module Backends
3
+ class NTLMTcpServer < TcpServer
4
+ def initialize(host, port, options)
5
+ super(host, port)
6
+ end
7
+
8
+ def connect
9
+ @signature = EventMachine.start_server(@host, @port, NTLMConnection, &method(:initialize_connection))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,180 @@
1
+ require 'weakref'
2
+ require 'win32/sspi/server'
3
+
4
+ module Thin
5
+ class NTLMWrapper
6
+ AUTHORIZATION_MESSAGE = <<END
7
+ <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
8
+ <html><head>
9
+ <title>401 NTLM Authorization Required</title>
10
+ </head><body>
11
+ <h1>NTLM Authorization Required</h1>
12
+ <p>This server could not verify that you
13
+ are authorized to access the document
14
+ requested. Either you supplied the wrong
15
+ credentials (e.g., bad password), or your
16
+ browser doesn't understand how to supply
17
+ the credentials required.</p>
18
+ </body></html>
19
+ END
20
+ REMOTE_USER = 'REMOTE_USER'.freeze
21
+ HTTP_AUTHORIZATION = 'HTTP_AUTHORIZATION'.freeze
22
+ WWW_AUTHENTICATE = 'WWW-Authenticate'.freeze
23
+ CONTENT_TYPE = 'Content-Type'.freeze
24
+ CONTENT_TYPE_AUTH = 'text/html; charset=iso-8859-1'.freeze
25
+ NTLM_REQUEST_PACKAGE = 'NTLM'.freeze
26
+ NTLM_ALLOWED_PACKAGE = 'NTLM|Negotiate'.freeze
27
+
28
+ def initialize(app, connection)
29
+ @app = app
30
+ @connection = WeakRef.new(connection)
31
+ end
32
+
33
+ def deferred?(env)
34
+ @app.respond_to?(:deferred?) && @app.deferred?(env)
35
+ end
36
+
37
+ def persistent?
38
+ @connection.request.persistent?
39
+ end
40
+
41
+ def can_persist!
42
+ @connection.can_persist!
43
+ end
44
+
45
+ def ntlm_start
46
+ @connection.ntlm_start
47
+ end
48
+
49
+ def ntlm_stop
50
+ @connection.ntlm_stop
51
+ end
52
+
53
+ def call(env)
54
+ # check if browser wants to reauthenticate
55
+ if @authenticated_as && http_authorization(env)
56
+ @authenticated_as = nil
57
+ end
58
+
59
+ # require authentication
60
+ unless @authenticated_as
61
+ ntlm_start
62
+ @authentication_stage ||= 1
63
+ result = process(env)
64
+ return result unless @authenticated_as
65
+ ntlm_stop
66
+ end
67
+
68
+ # pass thru
69
+ env[REMOTE_USER] = @authenticated_as
70
+ @app.call(env)
71
+ end
72
+
73
+ # Returns stripped HTTP-Authorization header, nil if none or empty
74
+ def http_authorization(env)
75
+ auth = env[HTTP_AUTHORIZATION]
76
+ if auth
77
+ auth = auth.strip
78
+ auth = nil if auth.empty?
79
+ end
80
+ auth
81
+ end
82
+
83
+ # Returns token type and value from HTTP-Authorization header
84
+ def token(env)
85
+ auth = http_authorization(env)
86
+ return [nil, nil] unless auth && auth.match(/\A(#{NTLM_ALLOWED_PACKAGE}) (.*)\Z/)
87
+ [$1, Base64.decode64($2.strip)]
88
+ end
89
+
90
+ # Acquires new OS credentials handle
91
+ def acquire(package = 'NTLM')
92
+ cleanup
93
+ @ntlm = Win32::SSPI::NegotiateServer.new(package)
94
+ @ntlm.acquire_credentials_handle
95
+ @ntlm
96
+ end
97
+
98
+ # Frees credentials handle, if acquired
99
+ def cleanup
100
+ if @ntlm
101
+ @ntlm.cleanup rescue nil
102
+ @ntlm = nil
103
+ end
104
+ nil
105
+ end
106
+
107
+ # Processes current authentication stage
108
+ # Returns rack response if authentication is incomplete
109
+ # Sets @authenticated_as to username if authentication successful
110
+ def process(env)
111
+ case @authentication_stage
112
+ when 1 # we are waiting for type1 message
113
+ package, t1 = token(env)
114
+ return request_auth(NTLM_REQUEST_PACKAGE, false) if t1.nil?
115
+ return request_auth unless persistent?
116
+ begin
117
+ acquire(package)
118
+ t2 = @ntlm.accept_security_context(t1)
119
+ rescue
120
+ return request_auth
121
+ end
122
+ request_auth("#{package} #{t2}", false, 2)
123
+ when 2 # we are waiting for type3 message
124
+ package, t3 = token(env)
125
+ return request_auth(NTLM_REQUEST_PACKAGE, false) if t3.nil?
126
+ return request_auth unless package == @ntlm.package
127
+ return request_auth unless persistent?
128
+ begin
129
+ t2 = @ntlm.accept_security_context(t3)
130
+ @authenticated_as = @ntlm.get_username_from_context
131
+ @authentication_stage = nil # in case IE wants to reauthenticate
132
+ rescue
133
+ return request_auth
134
+ end
135
+ return request_auth unless @authenticated_as
136
+ cleanup
137
+ else
138
+ raise "Invalid value for @authentication_stage=#{@authentication_stage} detected"
139
+ end
140
+ end
141
+
142
+ # Returns response with authentication request to the client
143
+ def request_auth(auth = nil, finished = true, next_stage = 1)
144
+ @authentication_stage = next_stage
145
+ can_persist! unless finished
146
+ head = {}
147
+ head[WWW_AUTHENTICATE] = auth if auth
148
+ head[CONTENT_TYPE] = CONTENT_TYPE_AUTH
149
+ [401, head, [AUTHORIZATION_MESSAGE]]
150
+ end
151
+ end
152
+
153
+ class NTLMConnection < Connection
154
+ def app=(app)
155
+ super NTLMWrapper.new(app, self)
156
+ end
157
+
158
+ def unbind
159
+ @app.cleanup if @app && @app.respond_to?(:cleanup)
160
+ ensure
161
+ super
162
+ end
163
+
164
+ # Saves original can_persist? value (NTLM will force persistence)
165
+ def ntlm_start
166
+ unless @ntlm_in_progress
167
+ @ntlm_saved_can_persist = @can_persist
168
+ @ntlm_in_progress = true
169
+ end
170
+ end
171
+
172
+ # Restores previous can_persist? value
173
+ def ntlm_stop
174
+ if @ntlm_in_progress
175
+ @can_persist = @ntlm_saved_can_persist
176
+ @ntlm_in_progress = false
177
+ end
178
+ end
179
+ end
180
+ 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.4"
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-13}
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 ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thin-auth-ntlm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Alexey Borzenkov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-13 00:00:00 +04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thin
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rubysspi-server
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.1
34
+ version:
35
+ description:
36
+ email: snaury@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE.txt
43
+ - README.txt
44
+ files:
45
+ - LICENSE.txt
46
+ - README.txt
47
+ - Rakefile
48
+ - VERSION.yml
49
+ - lib/thin-auth-ntlm.rb
50
+ - lib/thin/ntlm/backends/tcp_server.rb
51
+ - lib/thin/ntlm/connection.rb
52
+ - thin-auth-ntlm.gemspec
53
+ has_rdoc: true
54
+ homepage:
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --charset=UTF-8
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.5
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Allows you to force NTLM authentication on Thin TCP servers.
81
+ test_files: []
82
+