eac-rack 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +18 -0
- data/KNOWN-ISSUES +21 -0
- data/README +399 -0
- data/bin/rackup +4 -0
- data/contrib/rack_logo.svg +111 -0
- data/example/lobster.ru +4 -0
- data/example/protectedlobster.rb +14 -0
- data/example/protectedlobster.ru +8 -0
- data/lib/rack.rb +92 -0
- data/lib/rack/adapter/camping.rb +22 -0
- data/lib/rack/auth/abstract/handler.rb +37 -0
- data/lib/rack/auth/abstract/request.rb +37 -0
- data/lib/rack/auth/basic.rb +58 -0
- data/lib/rack/auth/digest/md5.rb +124 -0
- data/lib/rack/auth/digest/nonce.rb +51 -0
- data/lib/rack/auth/digest/params.rb +55 -0
- data/lib/rack/auth/digest/request.rb +40 -0
- data/lib/rack/builder.rb +80 -0
- data/lib/rack/cascade.rb +41 -0
- data/lib/rack/chunked.rb +49 -0
- data/lib/rack/commonlogger.rb +49 -0
- data/lib/rack/conditionalget.rb +47 -0
- data/lib/rack/config.rb +15 -0
- data/lib/rack/content_length.rb +29 -0
- data/lib/rack/content_type.rb +23 -0
- data/lib/rack/deflater.rb +96 -0
- data/lib/rack/directory.rb +157 -0
- data/lib/rack/etag.rb +23 -0
- data/lib/rack/file.rb +90 -0
- data/lib/rack/handler.rb +88 -0
- data/lib/rack/handler/cgi.rb +61 -0
- data/lib/rack/handler/evented_mongrel.rb +8 -0
- data/lib/rack/handler/fastcgi.rb +89 -0
- data/lib/rack/handler/lsws.rb +63 -0
- data/lib/rack/handler/mongrel.rb +90 -0
- data/lib/rack/handler/scgi.rb +62 -0
- data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
- data/lib/rack/handler/thin.rb +18 -0
- data/lib/rack/handler/webrick.rb +69 -0
- data/lib/rack/head.rb +19 -0
- data/lib/rack/lint.rb +575 -0
- data/lib/rack/lobster.rb +65 -0
- data/lib/rack/lock.rb +16 -0
- data/lib/rack/logger.rb +20 -0
- data/lib/rack/methodoverride.rb +27 -0
- data/lib/rack/mime.rb +206 -0
- data/lib/rack/mock.rb +189 -0
- data/lib/rack/nulllogger.rb +18 -0
- data/lib/rack/recursive.rb +57 -0
- data/lib/rack/reloader.rb +109 -0
- data/lib/rack/request.rb +271 -0
- data/lib/rack/response.rb +149 -0
- data/lib/rack/rewindable_input.rb +100 -0
- data/lib/rack/runtime.rb +27 -0
- data/lib/rack/sendfile.rb +142 -0
- data/lib/rack/server.rb +212 -0
- data/lib/rack/session/abstract/id.rb +140 -0
- data/lib/rack/session/cookie.rb +90 -0
- data/lib/rack/session/memcache.rb +119 -0
- data/lib/rack/session/pool.rb +100 -0
- data/lib/rack/showexceptions.rb +349 -0
- data/lib/rack/showstatus.rb +106 -0
- data/lib/rack/static.rb +38 -0
- data/lib/rack/urlmap.rb +56 -0
- data/lib/rack/utils.rb +614 -0
- data/rack.gemspec +38 -0
- data/test/spec_rack_auth_basic.rb +73 -0
- data/test/spec_rack_auth_digest.rb +226 -0
- data/test/spec_rack_builder.rb +84 -0
- data/test/spec_rack_camping.rb +51 -0
- data/test/spec_rack_cascade.rb +48 -0
- data/test/spec_rack_cgi.rb +89 -0
- data/test/spec_rack_chunked.rb +62 -0
- data/test/spec_rack_commonlogger.rb +61 -0
- data/test/spec_rack_conditionalget.rb +41 -0
- data/test/spec_rack_config.rb +24 -0
- data/test/spec_rack_content_length.rb +43 -0
- data/test/spec_rack_content_type.rb +30 -0
- data/test/spec_rack_deflater.rb +127 -0
- data/test/spec_rack_directory.rb +61 -0
- data/test/spec_rack_etag.rb +17 -0
- data/test/spec_rack_fastcgi.rb +89 -0
- data/test/spec_rack_file.rb +75 -0
- data/test/spec_rack_handler.rb +43 -0
- data/test/spec_rack_head.rb +30 -0
- data/test/spec_rack_lint.rb +528 -0
- data/test/spec_rack_lobster.rb +45 -0
- data/test/spec_rack_lock.rb +38 -0
- data/test/spec_rack_logger.rb +21 -0
- data/test/spec_rack_methodoverride.rb +60 -0
- data/test/spec_rack_mock.rb +243 -0
- data/test/spec_rack_mongrel.rb +189 -0
- data/test/spec_rack_nulllogger.rb +13 -0
- data/test/spec_rack_recursive.rb +77 -0
- data/test/spec_rack_request.rb +545 -0
- data/test/spec_rack_response.rb +221 -0
- data/test/spec_rack_rewindable_input.rb +118 -0
- data/test/spec_rack_runtime.rb +35 -0
- data/test/spec_rack_sendfile.rb +86 -0
- data/test/spec_rack_session_cookie.rb +73 -0
- data/test/spec_rack_session_memcache.rb +273 -0
- data/test/spec_rack_session_pool.rb +172 -0
- data/test/spec_rack_showexceptions.rb +21 -0
- data/test/spec_rack_showstatus.rb +72 -0
- data/test/spec_rack_static.rb +37 -0
- data/test/spec_rack_thin.rb +91 -0
- data/test/spec_rack_urlmap.rb +215 -0
- data/test/spec_rack_utils.rb +554 -0
- data/test/spec_rack_webrick.rb +130 -0
- data/test/spec_rackup.rb +154 -0
- metadata +311 -0
data/example/lobster.ru
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rack/lobster'
|
3
|
+
|
4
|
+
lobster = Rack::Lobster.new
|
5
|
+
|
6
|
+
protected_lobster = Rack::Auth::Basic.new(lobster) do |username, password|
|
7
|
+
'secret' == password
|
8
|
+
end
|
9
|
+
|
10
|
+
protected_lobster.realm = 'Lobster 2.0'
|
11
|
+
|
12
|
+
pretty_protected_lobster = Rack::ShowStatus.new(Rack::ShowExceptions.new(protected_lobster))
|
13
|
+
|
14
|
+
Rack::Handler::WEBrick.run pretty_protected_lobster, :Port => 9292
|
data/lib/rack.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# Copyright (C) 2007, 2008, 2009, 2010 Christian Neukirchen <purl.org/net/chneukirchen>
|
2
|
+
#
|
3
|
+
# Rack is freely distributable under the terms of an MIT-style license.
|
4
|
+
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
|
6
|
+
# The Rack main module, serving as a namespace for all core Rack
|
7
|
+
# modules and classes.
|
8
|
+
#
|
9
|
+
# All modules meant for use in your application are <tt>autoload</tt>ed here,
|
10
|
+
# so it should be enough just to <tt>require rack.rb</tt> in your code.
|
11
|
+
|
12
|
+
module Rack
|
13
|
+
# The Rack protocol version number implemented.
|
14
|
+
VERSION = [1,1]
|
15
|
+
|
16
|
+
# Return the Rack protocol version as a dotted string.
|
17
|
+
def self.version
|
18
|
+
VERSION.join(".")
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return the Rack release as a dotted string.
|
22
|
+
def self.release
|
23
|
+
"1.1"
|
24
|
+
end
|
25
|
+
|
26
|
+
autoload :Builder, "rack/builder"
|
27
|
+
autoload :Cascade, "rack/cascade"
|
28
|
+
autoload :Chunked, "rack/chunked"
|
29
|
+
autoload :CommonLogger, "rack/commonlogger"
|
30
|
+
autoload :ConditionalGet, "rack/conditionalget"
|
31
|
+
autoload :Config, "rack/config"
|
32
|
+
autoload :ContentLength, "rack/content_length"
|
33
|
+
autoload :ContentType, "rack/content_type"
|
34
|
+
autoload :ETag, "rack/etag"
|
35
|
+
autoload :File, "rack/file"
|
36
|
+
autoload :Deflater, "rack/deflater"
|
37
|
+
autoload :Directory, "rack/directory"
|
38
|
+
autoload :ForwardRequest, "rack/recursive"
|
39
|
+
autoload :Handler, "rack/handler"
|
40
|
+
autoload :Head, "rack/head"
|
41
|
+
autoload :Lint, "rack/lint"
|
42
|
+
autoload :Lock, "rack/lock"
|
43
|
+
autoload :Logger, "rack/logger"
|
44
|
+
autoload :MethodOverride, "rack/methodoverride"
|
45
|
+
autoload :Mime, "rack/mime"
|
46
|
+
autoload :NullLogger, "rack/nulllogger"
|
47
|
+
autoload :Recursive, "rack/recursive"
|
48
|
+
autoload :Reloader, "rack/reloader"
|
49
|
+
autoload :Runtime, "rack/runtime"
|
50
|
+
autoload :Sendfile, "rack/sendfile"
|
51
|
+
autoload :Server, "rack/server"
|
52
|
+
autoload :ShowExceptions, "rack/showexceptions"
|
53
|
+
autoload :ShowStatus, "rack/showstatus"
|
54
|
+
autoload :Static, "rack/static"
|
55
|
+
autoload :URLMap, "rack/urlmap"
|
56
|
+
autoload :Utils, "rack/utils"
|
57
|
+
|
58
|
+
autoload :MockRequest, "rack/mock"
|
59
|
+
autoload :MockResponse, "rack/mock"
|
60
|
+
|
61
|
+
autoload :Request, "rack/request"
|
62
|
+
autoload :Response, "rack/response"
|
63
|
+
|
64
|
+
module Auth
|
65
|
+
autoload :Basic, "rack/auth/basic"
|
66
|
+
autoload :AbstractRequest, "rack/auth/abstract/request"
|
67
|
+
autoload :AbstractHandler, "rack/auth/abstract/handler"
|
68
|
+
module Digest
|
69
|
+
autoload :MD5, "rack/auth/digest/md5"
|
70
|
+
autoload :Nonce, "rack/auth/digest/nonce"
|
71
|
+
autoload :Params, "rack/auth/digest/params"
|
72
|
+
autoload :Request, "rack/auth/digest/request"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module Session
|
77
|
+
autoload :Cookie, "rack/session/cookie"
|
78
|
+
autoload :Pool, "rack/session/pool"
|
79
|
+
autoload :Memcache, "rack/session/memcache"
|
80
|
+
end
|
81
|
+
|
82
|
+
# *Adapters* connect Rack with third party web frameworks.
|
83
|
+
#
|
84
|
+
# Rack includes an adapter for Camping, see README for other
|
85
|
+
# frameworks supporting Rack in their code bases.
|
86
|
+
#
|
87
|
+
# Refer to the submodules for framework-specific calling details.
|
88
|
+
|
89
|
+
module Adapter
|
90
|
+
autoload :Camping, "rack/adapter/camping"
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Rack
|
2
|
+
module Adapter
|
3
|
+
class Camping
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
env["PATH_INFO"] ||= ""
|
10
|
+
env["SCRIPT_NAME"] ||= ""
|
11
|
+
controller = @app.run(env['rack.input'], env)
|
12
|
+
h = controller.headers
|
13
|
+
h.each_pair do |k,v|
|
14
|
+
if v.kind_of? URI
|
15
|
+
h[k] = v.to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
[controller.status, controller.headers, [controller.body.to_s]]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rack
|
2
|
+
module Auth
|
3
|
+
# Rack::Auth::AbstractHandler implements common authentication functionality.
|
4
|
+
#
|
5
|
+
# +realm+ should be set for all handlers.
|
6
|
+
|
7
|
+
class AbstractHandler
|
8
|
+
|
9
|
+
attr_accessor :realm
|
10
|
+
|
11
|
+
def initialize(app, realm=nil, &authenticator)
|
12
|
+
@app, @realm, @authenticator = app, realm, authenticator
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def unauthorized(www_authenticate = challenge)
|
19
|
+
return [ 401,
|
20
|
+
{ 'Content-Type' => 'text/plain',
|
21
|
+
'Content-Length' => '0',
|
22
|
+
'WWW-Authenticate' => www_authenticate.to_s },
|
23
|
+
[]
|
24
|
+
]
|
25
|
+
end
|
26
|
+
|
27
|
+
def bad_request
|
28
|
+
return [ 400,
|
29
|
+
{ 'Content-Type' => 'text/plain',
|
30
|
+
'Content-Length' => '0' },
|
31
|
+
[]
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Rack
|
2
|
+
module Auth
|
3
|
+
class AbstractRequest
|
4
|
+
|
5
|
+
def initialize(env)
|
6
|
+
@env = env
|
7
|
+
end
|
8
|
+
|
9
|
+
def provided?
|
10
|
+
!authorization_key.nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
def parts
|
14
|
+
@parts ||= @env[authorization_key].split(' ', 2)
|
15
|
+
end
|
16
|
+
|
17
|
+
def scheme
|
18
|
+
@scheme ||= parts.first.downcase.to_sym
|
19
|
+
end
|
20
|
+
|
21
|
+
def params
|
22
|
+
@params ||= parts.last
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
AUTHORIZATION_KEYS = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION']
|
29
|
+
|
30
|
+
def authorization_key
|
31
|
+
@authorization_key ||= AUTHORIZATION_KEYS.detect { |key| @env.has_key?(key) }
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rack/auth/abstract/handler'
|
2
|
+
require 'rack/auth/abstract/request'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
module Auth
|
6
|
+
# Rack::Auth::Basic implements HTTP Basic Authentication, as per RFC 2617.
|
7
|
+
#
|
8
|
+
# Initialize with the Rack application that you want protecting,
|
9
|
+
# and a block that checks if a username and password pair are valid.
|
10
|
+
#
|
11
|
+
# See also: <tt>example/protectedlobster.rb</tt>
|
12
|
+
|
13
|
+
class Basic < AbstractHandler
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
auth = Basic::Request.new(env)
|
17
|
+
|
18
|
+
return unauthorized unless auth.provided?
|
19
|
+
|
20
|
+
return bad_request unless auth.basic?
|
21
|
+
|
22
|
+
if valid?(auth)
|
23
|
+
env['REMOTE_USER'] = auth.username
|
24
|
+
|
25
|
+
return @app.call(env)
|
26
|
+
end
|
27
|
+
|
28
|
+
unauthorized
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def challenge
|
35
|
+
'Basic realm="%s"' % realm
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid?(auth)
|
39
|
+
@authenticator.call(*auth.credentials)
|
40
|
+
end
|
41
|
+
|
42
|
+
class Request < Auth::AbstractRequest
|
43
|
+
def basic?
|
44
|
+
:basic == scheme
|
45
|
+
end
|
46
|
+
|
47
|
+
def credentials
|
48
|
+
@credentials ||= params.unpack("m*").first.split(/:/, 2)
|
49
|
+
end
|
50
|
+
|
51
|
+
def username
|
52
|
+
credentials.first
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'rack/auth/abstract/handler'
|
2
|
+
require 'rack/auth/digest/request'
|
3
|
+
require 'rack/auth/digest/params'
|
4
|
+
require 'rack/auth/digest/nonce'
|
5
|
+
require 'digest/md5'
|
6
|
+
|
7
|
+
module Rack
|
8
|
+
module Auth
|
9
|
+
module Digest
|
10
|
+
# Rack::Auth::Digest::MD5 implements the MD5 algorithm version of
|
11
|
+
# HTTP Digest Authentication, as per RFC 2617.
|
12
|
+
#
|
13
|
+
# Initialize with the [Rack] application that you want protecting,
|
14
|
+
# and a block that looks up a plaintext password for a given username.
|
15
|
+
#
|
16
|
+
# +opaque+ needs to be set to a constant base64/hexadecimal string.
|
17
|
+
#
|
18
|
+
class MD5 < AbstractHandler
|
19
|
+
|
20
|
+
attr_accessor :opaque
|
21
|
+
|
22
|
+
attr_writer :passwords_hashed
|
23
|
+
|
24
|
+
def initialize(*args)
|
25
|
+
super
|
26
|
+
@passwords_hashed = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def passwords_hashed?
|
30
|
+
!!@passwords_hashed
|
31
|
+
end
|
32
|
+
|
33
|
+
def call(env)
|
34
|
+
auth = Request.new(env)
|
35
|
+
|
36
|
+
unless auth.provided?
|
37
|
+
return unauthorized
|
38
|
+
end
|
39
|
+
|
40
|
+
if !auth.digest? || !auth.correct_uri? || !valid_qop?(auth)
|
41
|
+
return bad_request
|
42
|
+
end
|
43
|
+
|
44
|
+
if valid?(auth)
|
45
|
+
if auth.nonce.stale?
|
46
|
+
return unauthorized(challenge(:stale => true))
|
47
|
+
else
|
48
|
+
env['REMOTE_USER'] = auth.username
|
49
|
+
|
50
|
+
return @app.call(env)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
unauthorized
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
QOP = 'auth'.freeze
|
61
|
+
|
62
|
+
def params(hash = {})
|
63
|
+
Params.new do |params|
|
64
|
+
params['realm'] = realm
|
65
|
+
params['nonce'] = Nonce.new.to_s
|
66
|
+
params['opaque'] = H(opaque)
|
67
|
+
params['qop'] = QOP
|
68
|
+
|
69
|
+
hash.each { |k, v| params[k] = v }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def challenge(hash = {})
|
74
|
+
"Digest #{params(hash)}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def valid?(auth)
|
78
|
+
valid_opaque?(auth) && valid_nonce?(auth) && valid_digest?(auth)
|
79
|
+
end
|
80
|
+
|
81
|
+
def valid_qop?(auth)
|
82
|
+
QOP == auth.qop
|
83
|
+
end
|
84
|
+
|
85
|
+
def valid_opaque?(auth)
|
86
|
+
H(opaque) == auth.opaque
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid_nonce?(auth)
|
90
|
+
auth.nonce.valid?
|
91
|
+
end
|
92
|
+
|
93
|
+
def valid_digest?(auth)
|
94
|
+
digest(auth, @authenticator.call(auth.username)) == auth.response
|
95
|
+
end
|
96
|
+
|
97
|
+
def md5(data)
|
98
|
+
::Digest::MD5.hexdigest(data)
|
99
|
+
end
|
100
|
+
|
101
|
+
alias :H :md5
|
102
|
+
|
103
|
+
def KD(secret, data)
|
104
|
+
H([secret, data] * ':')
|
105
|
+
end
|
106
|
+
|
107
|
+
def A1(auth, password)
|
108
|
+
[ auth.username, auth.realm, password ] * ':'
|
109
|
+
end
|
110
|
+
|
111
|
+
def A2(auth)
|
112
|
+
[ auth.method, auth.uri ] * ':'
|
113
|
+
end
|
114
|
+
|
115
|
+
def digest(auth, password)
|
116
|
+
password_hash = passwords_hashed? ? password : H(A1(auth, password))
|
117
|
+
|
118
|
+
KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, QOP, H(A2(auth)) ] * ':')
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module Auth
|
5
|
+
module Digest
|
6
|
+
# Rack::Auth::Digest::Nonce is the default nonce generator for the
|
7
|
+
# Rack::Auth::Digest::MD5 authentication handler.
|
8
|
+
#
|
9
|
+
# +private_key+ needs to set to a constant string.
|
10
|
+
#
|
11
|
+
# +time_limit+ can be optionally set to an integer (number of seconds),
|
12
|
+
# to limit the validity of the generated nonces.
|
13
|
+
|
14
|
+
class Nonce
|
15
|
+
|
16
|
+
class << self
|
17
|
+
attr_accessor :private_key, :time_limit
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse(string)
|
21
|
+
new(*string.unpack("m*").first.split(' ', 2))
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(timestamp = Time.now, given_digest = nil)
|
25
|
+
@timestamp, @given_digest = timestamp.to_i, given_digest
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
[([ @timestamp, digest ] * ' ')].pack("m*").strip
|
30
|
+
end
|
31
|
+
|
32
|
+
def digest
|
33
|
+
::Digest::MD5.hexdigest([ @timestamp, self.class.private_key ] * ':')
|
34
|
+
end
|
35
|
+
|
36
|
+
def valid?
|
37
|
+
digest == @given_digest
|
38
|
+
end
|
39
|
+
|
40
|
+
def stale?
|
41
|
+
!self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit
|
42
|
+
end
|
43
|
+
|
44
|
+
def fresh?
|
45
|
+
!stale?
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|