egalite 0.0.1
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/.gitignore +17 -0
- data/README.md +91 -0
- data/auth/basic.rb +32 -0
- data/blank.rb +53 -0
- data/egalite.rb +742 -0
- data/errorconsole.rb +77 -0
- data/examples/simple/example.rb +39 -0
- data/examples/simple/pages/test.html +15 -0
- data/examples/simple_db/example_db.rb +103 -0
- data/examples/simple_db/pages/edit.html +6 -0
- data/helper.rb +251 -0
- data/keitai/keitai.rb +107 -0
- data/keitai/ketai.rb +11 -0
- data/keitai/rack/ketai/carrier/abstract.rb +131 -0
- data/keitai/rack/ketai/carrier/au.rb +78 -0
- data/keitai/rack/ketai/carrier/docomo.rb +80 -0
- data/keitai/rack/ketai/carrier/emoji/ausjisstrtoemojiid.rb +1391 -0
- data/keitai/rack/ketai/carrier/emoji/docomosjisstrtoemojiid.rb +759 -0
- data/keitai/rack/ketai/carrier/emoji/emojidata.rb +836 -0
- data/keitai/rack/ketai/carrier/emoji/softbankutf8strtoemojiid.rb +1119 -0
- data/keitai/rack/ketai/carrier/emoji/softbankwebcodetoutf8str.rb +499 -0
- data/keitai/rack/ketai/carrier/iphone.rb +8 -0
- data/keitai/rack/ketai/carrier/softbank.rb +82 -0
- data/keitai/rack/ketai/carrier.rb +17 -0
- data/keitai/rack/ketai/middleware.rb +24 -0
- data/m17n.rb +193 -0
- data/rack/auth/abstract/handler.rb +37 -0
- data/rack/auth/abstract/request.rb +37 -0
- data/rack/auth/basic.rb +58 -0
- data/rack/auth/digest/md5.rb +124 -0
- data/rack/auth/digest/nonce.rb +51 -0
- data/rack/auth/digest/params.rb +55 -0
- data/rack/auth/digest/request.rb +40 -0
- data/rack/builder.rb +80 -0
- data/rack/cascade.rb +41 -0
- data/rack/chunked.rb +49 -0
- data/rack/commonlogger.rb +49 -0
- data/rack/conditionalget.rb +47 -0
- data/rack/config.rb +15 -0
- data/rack/content_length.rb +29 -0
- data/rack/content_type.rb +23 -0
- data/rack/deflater.rb +96 -0
- data/rack/directory.rb +157 -0
- data/rack/etag.rb +32 -0
- data/rack/file.rb +92 -0
- data/rack/handler/cgi.rb +62 -0
- data/rack/handler/evented_mongrel.rb +8 -0
- data/rack/handler/fastcgi.rb +89 -0
- data/rack/handler/lsws.rb +63 -0
- data/rack/handler/mongrel.rb +90 -0
- data/rack/handler/scgi.rb +59 -0
- data/rack/handler/swiftiplied_mongrel.rb +8 -0
- data/rack/handler/thin.rb +18 -0
- data/rack/handler/webrick.rb +73 -0
- data/rack/handler.rb +88 -0
- data/rack/head.rb +19 -0
- data/rack/lint.rb +567 -0
- data/rack/lobster.rb +65 -0
- data/rack/lock.rb +16 -0
- data/rack/logger.rb +20 -0
- data/rack/methodoverride.rb +27 -0
- data/rack/mime.rb +208 -0
- data/rack/mock.rb +190 -0
- data/rack/nulllogger.rb +18 -0
- data/rack/recursive.rb +61 -0
- data/rack/reloader.rb +109 -0
- data/rack/request.rb +273 -0
- data/rack/response.rb +150 -0
- data/rack/rewindable_input.rb +103 -0
- data/rack/runtime.rb +27 -0
- data/rack/sendfile.rb +144 -0
- data/rack/server.rb +271 -0
- data/rack/session/abstract/id.rb +140 -0
- data/rack/session/cookie.rb +90 -0
- data/rack/session/memcache.rb +119 -0
- data/rack/session/pool.rb +100 -0
- data/rack/showexceptions.rb +349 -0
- data/rack/showstatus.rb +106 -0
- data/rack/static.rb +38 -0
- data/rack/urlmap.rb +55 -0
- data/rack/utils.rb +662 -0
- data/rack.rb +81 -0
- data/route.rb +231 -0
- data/sendmail.rb +222 -0
- data/sequel_helper.rb +20 -0
- data/session.rb +132 -0
- data/stringify_hash.rb +63 -0
- data/support.rb +35 -0
- data/template.rb +287 -0
- data/test/french.html +13 -0
- data/test/french_msg.html +3 -0
- data/test/m17n.txt +30 -0
- data/test/mobile.html +15 -0
- data/test/setup.rb +8 -0
- data/test/static/test.txt +1 -0
- data/test/template.html +58 -0
- data/test/template_inner.html +1 -0
- data/test/template_innerparam.html +1 -0
- data/test/test_auth.rb +43 -0
- data/test/test_blank.rb +44 -0
- data/test/test_csrf.rb +87 -0
- data/test/test_errorconsole.rb +91 -0
- data/test/test_handler.rb +155 -0
- data/test/test_helper.rb +296 -0
- data/test/test_keitai.rb +107 -0
- data/test/test_m17n.rb +129 -0
- data/test/test_route.rb +192 -0
- data/test/test_sendmail.rb +146 -0
- data/test/test_session.rb +83 -0
- data/test/test_stringify_hash.rb +67 -0
- data/test/test_template.rb +114 -0
- data/test.bat +2 -0
- metadata +240 -0
data/m17n.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
|
2
|
+
module Egalite
|
3
|
+
module M17N
|
4
|
+
|
5
|
+
|
6
|
+
module Filters
|
7
|
+
def before_filter
|
8
|
+
# check hostname first to determine which language to serve.
|
9
|
+
first = req.host.split(/\./).first
|
10
|
+
@lang = Translation.lang(first)
|
11
|
+
if not @lang and req.accept_language and Translation.allow_content_negotiation
|
12
|
+
# fallback to Accept-Language HTTP header for language to serve.
|
13
|
+
langs = req.accept_language.split(/,/)
|
14
|
+
@lang = langs.map { |s| Translation.lang(s.split(/;/).first) }.compact.first
|
15
|
+
end
|
16
|
+
@lang ||= Translation.lang(Translation.user_default_lang)
|
17
|
+
@lang ||= Translation.lang('ja')
|
18
|
+
|
19
|
+
super
|
20
|
+
end
|
21
|
+
def filter_on_html_load(html,path)
|
22
|
+
html = @lang.translate_html(path, html) if @lang
|
23
|
+
super(html,path)
|
24
|
+
end
|
25
|
+
def after_filter_return_value(response)
|
26
|
+
if @lang
|
27
|
+
response = @lang.translate_msg(req.controller_class, req.action_method, response)
|
28
|
+
end
|
29
|
+
super(response)
|
30
|
+
end
|
31
|
+
def _(str, values = [])
|
32
|
+
if @lang
|
33
|
+
str = @lang.translate_string(req.controller_class, req.action_method, str, values)
|
34
|
+
else
|
35
|
+
str = str.dup
|
36
|
+
values.each_with_index { |s2,i| str.gsub!(/\{#{i}\}/, s2) }
|
37
|
+
end
|
38
|
+
str
|
39
|
+
end
|
40
|
+
end
|
41
|
+
class Controller < Egalite::Controller
|
42
|
+
include Filters
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
class Translation
|
47
|
+
class <<self
|
48
|
+
attr_accessor :langs, :user_default_lang, :allow_content_negotiation
|
49
|
+
end
|
50
|
+
def self.load(path)
|
51
|
+
@@langs = {}
|
52
|
+
|
53
|
+
s = open(path) { |f| f.read }
|
54
|
+
|
55
|
+
langs = nil
|
56
|
+
system_default = nil
|
57
|
+
|
58
|
+
[:languages, :system_default, :english_name, :native_name, :aliases].each { |optname|
|
59
|
+
s.gsub!(/\{\{#{optname}\s*(.+?)\s*\}\}\s*\n+/i) {
|
60
|
+
values = $1.split(/\s*,\s*/)
|
61
|
+
case optname
|
62
|
+
when :languages
|
63
|
+
langs = values
|
64
|
+
values.each { |lang|
|
65
|
+
@@langs[lang] = Translation.new(lang)
|
66
|
+
@@langs[lang].data = {}
|
67
|
+
}
|
68
|
+
when :system_default
|
69
|
+
lang = values.shift
|
70
|
+
@@langs[lang] = Translation.new(lang)
|
71
|
+
@@langs[lang].data = nil
|
72
|
+
system_default = lang
|
73
|
+
when :aliases
|
74
|
+
lang = values.shift
|
75
|
+
@@langs[lang].send("#{optname}=", values)
|
76
|
+
else
|
77
|
+
lang = values.shift
|
78
|
+
@@langs[lang].send("#{optname}=", values.first)
|
79
|
+
end
|
80
|
+
''
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
s.split(/###+\s*\n+/).each { |part|
|
85
|
+
next if part =~ /\A\s*\Z/
|
86
|
+
lines = part.split(/\n+/)
|
87
|
+
key = lines.shift
|
88
|
+
(type, path) = key.split(/\s+/,2)
|
89
|
+
raise "Egalite::M17N::Translation.load: type should be 'html', 'msg' or 'img' but it was '#{type}'" unless %w[msg html img].include?(type)
|
90
|
+
lines.each { |line|
|
91
|
+
if type == 'img'
|
92
|
+
langs.each { |lang|
|
93
|
+
next unless @@langs[lang].data
|
94
|
+
img = line.sub(/\.(jpg|jpeg|gif|png)/i,"_#{lang}.\\1")
|
95
|
+
@@langs[lang].data[:img] ||= {}
|
96
|
+
@@langs[lang].data[:img][path] ||= {}
|
97
|
+
@@langs[lang].data[:img][path][line] = img
|
98
|
+
}
|
99
|
+
else
|
100
|
+
a = line.split(/\s*\t+\s*/)
|
101
|
+
k = nil
|
102
|
+
a.each_with_index { |s,i|
|
103
|
+
unless @@langs[langs[i]].data
|
104
|
+
k = s
|
105
|
+
else
|
106
|
+
@@langs[langs[i]].data[type.to_sym] ||= {}
|
107
|
+
@@langs[langs[i]].data[type.to_sym][path] ||= {}
|
108
|
+
@@langs[langs[i]].data[type.to_sym][path][k] = s
|
109
|
+
end
|
110
|
+
}
|
111
|
+
end
|
112
|
+
}
|
113
|
+
}
|
114
|
+
@@langs
|
115
|
+
end
|
116
|
+
def self.lang(s)
|
117
|
+
return nil unless s
|
118
|
+
a = @@langs.find { |k,v| v.fullmatch?(s) }
|
119
|
+
a ||= @@langs.find { |k,v| v.partialmatch?(s) }
|
120
|
+
a ? a.last : nil
|
121
|
+
end
|
122
|
+
private
|
123
|
+
def method_path(c,a)
|
124
|
+
c.class.name.to_s + '#' + a.to_s
|
125
|
+
end
|
126
|
+
def t_string(list, s)
|
127
|
+
list[s] ? list[s] : s
|
128
|
+
end
|
129
|
+
def t_hash(list, h)
|
130
|
+
if h.is_a?(EgaliteResponse)
|
131
|
+
h.param = t_hash(list, h.param)
|
132
|
+
return h
|
133
|
+
end
|
134
|
+
return h unless h.is_a?(Hash)
|
135
|
+
h2 = {}
|
136
|
+
h.each { |k,v|
|
137
|
+
h2[k] = case v
|
138
|
+
when String: t_string(list,v)
|
139
|
+
when Array: v.map { |x| t_hash(list,x) }
|
140
|
+
when Hash: t_hash(list, v)
|
141
|
+
else v
|
142
|
+
end
|
143
|
+
}
|
144
|
+
h2
|
145
|
+
end
|
146
|
+
|
147
|
+
public
|
148
|
+
|
149
|
+
attr_accessor :english_name, :native_name, :aliases, :data
|
150
|
+
attr_reader :langcode
|
151
|
+
|
152
|
+
def initialize(langcode)
|
153
|
+
@langcode = langcode
|
154
|
+
@aliases = []
|
155
|
+
end
|
156
|
+
def fullmatch?(lang)
|
157
|
+
lang = lang.to_s.downcase
|
158
|
+
@langcode == lang or @aliases.include?(lang)
|
159
|
+
end
|
160
|
+
def partialmatch?(lang)
|
161
|
+
fullmatch?(lang.to_s.split(/-/).first)
|
162
|
+
end
|
163
|
+
def translate_html(path, html)
|
164
|
+
return html unless @data
|
165
|
+
list = @data[:html][path]
|
166
|
+
return html unless list
|
167
|
+
s = html.dup
|
168
|
+
list.sort { |a,b| b[0].length <=> a[0].length }.each { |k,v| s.gsub!(k, v)}
|
169
|
+
if @data[:img] and @data[:img][path]
|
170
|
+
@data[:img][path].each { |k,v| s.gsub!(k, v) }
|
171
|
+
end
|
172
|
+
s
|
173
|
+
end
|
174
|
+
def translate_msg(controller, action, msg)
|
175
|
+
return msg unless @data
|
176
|
+
list = @data[:msg][method_path(controller,action)]
|
177
|
+
return msg unless list
|
178
|
+
t_hash(list, msg)
|
179
|
+
end
|
180
|
+
def translate_string(controller, action, string, placeholders = [])
|
181
|
+
if @data
|
182
|
+
list = @data[:msg][method_path(controller,action)]
|
183
|
+
if list
|
184
|
+
string = t_string(list, string)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
string = string.dup
|
188
|
+
placeholders.each_with_index { |s2,i| string.gsub!(/\{#{i}\}/, s2) }
|
189
|
+
string
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
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
|
data/rack/auth/basic.rb
ADDED
@@ -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
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Rack
|
2
|
+
module Auth
|
3
|
+
module Digest
|
4
|
+
class Params < Hash
|
5
|
+
|
6
|
+
def self.parse(str)
|
7
|
+
split_header_value(str).inject(new) do |header, param|
|
8
|
+
k, v = param.split('=', 2)
|
9
|
+
header[k] = dequote(v)
|
10
|
+
header
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.dequote(str) # From WEBrick::HTTPUtils
|
15
|
+
ret = (/\A"(.*)"\Z/ =~ str) ? $1 : str.dup
|
16
|
+
ret.gsub!(/\\(.)/, "\\1")
|
17
|
+
ret
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.split_header_value(str)
|
21
|
+
str.scan( /(\w+\=(?:"[^\"]+"|[^,]+))/n ).collect{ |v| v[0] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
super
|
26
|
+
|
27
|
+
yield self if block_given?
|
28
|
+
end
|
29
|
+
|
30
|
+
def [](k)
|
31
|
+
super k.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def []=(k, v)
|
35
|
+
super k.to_s, v.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
UNQUOTED = ['nc', 'stale']
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
inject([]) do |parts, (k, v)|
|
42
|
+
parts << "#{k}=" + (UNQUOTED.include?(k) ? v.to_s : quote(v))
|
43
|
+
parts
|
44
|
+
end.join(', ')
|
45
|
+
end
|
46
|
+
|
47
|
+
def quote(str) # From WEBrick::HTTPUtils
|
48
|
+
'"' << str.gsub(/[\\\"]/o, "\\\1") << '"'
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rack/auth/abstract/request'
|
2
|
+
require 'rack/auth/digest/params'
|
3
|
+
require 'rack/auth/digest/nonce'
|
4
|
+
|
5
|
+
module Rack
|
6
|
+
module Auth
|
7
|
+
module Digest
|
8
|
+
class Request < Auth::AbstractRequest
|
9
|
+
|
10
|
+
def method
|
11
|
+
@env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD']
|
12
|
+
end
|
13
|
+
|
14
|
+
def digest?
|
15
|
+
:digest == scheme
|
16
|
+
end
|
17
|
+
|
18
|
+
def correct_uri?
|
19
|
+
(@env['SCRIPT_NAME'].to_s + @env['PATH_INFO'].to_s) == uri
|
20
|
+
end
|
21
|
+
|
22
|
+
def nonce
|
23
|
+
@nonce ||= Nonce.parse(params['nonce'])
|
24
|
+
end
|
25
|
+
|
26
|
+
def params
|
27
|
+
@params ||= Params.parse(parts.last)
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(sym)
|
31
|
+
if params.has_key? key = sym.to_s
|
32
|
+
return params[key]
|
33
|
+
end
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/rack/builder.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module Rack
|
2
|
+
# Rack::Builder implements a small DSL to iteratively construct Rack
|
3
|
+
# applications.
|
4
|
+
#
|
5
|
+
# Example:
|
6
|
+
#
|
7
|
+
# app = Rack::Builder.new {
|
8
|
+
# use Rack::CommonLogger
|
9
|
+
# use Rack::ShowExceptions
|
10
|
+
# map "/lobster" do
|
11
|
+
# use Rack::Lint
|
12
|
+
# run Rack::Lobster.new
|
13
|
+
# end
|
14
|
+
# }
|
15
|
+
#
|
16
|
+
# Or
|
17
|
+
#
|
18
|
+
# app = Rack::Builder.app do
|
19
|
+
# use Rack::CommonLogger
|
20
|
+
# lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'OK'] }
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# +use+ adds a middleware to the stack, +run+ dispatches to an application.
|
24
|
+
# You can use +map+ to construct a Rack::URLMap in a convenient way.
|
25
|
+
|
26
|
+
class Builder
|
27
|
+
def self.parse_file(config, opts = Server::Options.new)
|
28
|
+
options = {}
|
29
|
+
if config =~ /\.ru$/
|
30
|
+
cfgfile = ::File.read(config)
|
31
|
+
if cfgfile[/^#\\(.*)/] && opts
|
32
|
+
options = opts.parse! $1.split(/\s+/)
|
33
|
+
end
|
34
|
+
cfgfile.sub!(/^__END__\n.*/, '')
|
35
|
+
app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app",
|
36
|
+
TOPLEVEL_BINDING, config
|
37
|
+
else
|
38
|
+
require config
|
39
|
+
app = Object.const_get(::File.basename(config, '.rb').capitalize)
|
40
|
+
end
|
41
|
+
return app, options
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(&block)
|
45
|
+
@ins = []
|
46
|
+
instance_eval(&block) if block_given?
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.app(&block)
|
50
|
+
self.new(&block).to_app
|
51
|
+
end
|
52
|
+
|
53
|
+
def use(middleware, *args, &block)
|
54
|
+
@ins << lambda { |app| middleware.new(app, *args, &block) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def run(app)
|
58
|
+
@ins << app #lambda { |nothing| app }
|
59
|
+
end
|
60
|
+
|
61
|
+
def map(path, &block)
|
62
|
+
if @ins.last.kind_of? Hash
|
63
|
+
@ins.last[path] = self.class.new(&block).to_app
|
64
|
+
else
|
65
|
+
@ins << {}
|
66
|
+
map(path, &block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_app
|
71
|
+
@ins[-1] = Rack::URLMap.new(@ins.last) if Hash === @ins.last
|
72
|
+
inner_app = @ins.last
|
73
|
+
@ins[0...-1].reverse.inject(inner_app) { |a, e| e.call(a) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def call(env)
|
77
|
+
to_app.call(env)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|