Skip to content

Commit 3db83b4

Browse files
committed
Merge branch 'master' of git://github.com/rails/rails
2 parents 0b9c956 + dc88847 commit 3db83b4

File tree

136 files changed

+2878
-1300
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+2878
-1300
lines changed

actionmailer/CHANGELOG

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
*2.3.1 [RC2] (March 5, 2009)*
1+
*2.3.2 [Final] (March 15, 2009)*
22

33
* Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones]
44

5-
6-
*2.3.0 [RC1] (February 1st, 2009)*
7-
85
* Fixed RFC-2045 quoted-printable bug #1421 [squadette]
96

107
* Fixed that no body charset would be set when there are attachments present #740 [Paweł Kondzior]

actionmailer/Rakefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ spec = Gem::Specification.new do |s|
5555
s.rubyforge_project = "actionmailer"
5656
s.homepage = "http://www.rubyonrails.org"
5757

58-
s.add_dependency('actionpack', '= 2.3.1' + PKG_BUILD)
58+
s.add_dependency('actionpack', '= 2.3.2' + PKG_BUILD)
5959

6060
s.has_rdoc = true
6161
s.requirements << 'none'

actionmailer/lib/action_mailer/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module ActionMailer
22
module VERSION #:nodoc:
33
MAJOR = 2
44
MINOR = 3
5-
TINY = 1
5+
TINY = 2
66

77
STRING = [MAJOR, MINOR, TINY].join('.')
88
end

actionpack/CHANGELOG

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*2.3.1 [RC2] (March 5, 2009)*
1+
*2.3.2 [Final] (March 15, 2009)*
22

33
* Fixed that redirection would just log the options, not the final url (which lead to "Redirected to #<Post:0x23150b8>") [DHH]
44

@@ -14,9 +14,6 @@
1414

1515
* Added localized rescue template when I18n.locale is set (ex: public/404.da.html) #1835 [José Valim]
1616

17-
18-
*2.3.0 [RC1] (February 1st, 2009)*
19-
2017
* Make the form_for and fields_for helpers support the new Active Record nested update options. #1202 [Eloy Duran]
2118

2219
<% form_for @person do |person_form| %>

actionpack/Rakefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ spec = Gem::Specification.new do |s|
8080
s.has_rdoc = true
8181
s.requirements << 'none'
8282

83-
s.add_dependency('activesupport', '= 2.3.1' + PKG_BUILD)
83+
s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD)
8484

8585
s.require_path = 'lib'
8686
s.autorequire = 'action_controller'

actionpack/lib/action_controller/http_authentication.rb

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,11 @@ module HttpAuthentication
6868
#
6969
# Simple Digest example:
7070
#
71+
# require 'digest/md5'
7172
# class PostsController < ApplicationController
72-
# USERS = {"dhh" => "secret"}
73+
# REALM = "SuperSecret"
74+
# USERS = {"dhh" => "secret", #plain text password
75+
# "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password
7376
#
7477
# before_filter :authenticate, :except => [:index]
7578
#
@@ -83,14 +86,18 @@ module HttpAuthentication
8386
#
8487
# private
8588
# def authenticate
86-
# authenticate_or_request_with_http_digest(realm) do |username|
89+
# authenticate_or_request_with_http_digest(REALM) do |username|
8790
# USERS[username]
8891
# end
8992
# end
9093
# end
9194
#
92-
# NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password so the framework can appropriately
93-
# hash it to check the user's credentials. Returning +nil+ will cause authentication to fail.
95+
# NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately
96+
# hash to check the user's credentials. Returning +nil+ will cause authentication to fail.
97+
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
98+
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
99+
# authenticate as the user at this +realm+, but would not have the user's password to try using at
100+
# other sites.
94101
#
95102
# On shared hosts, Apache sometimes doesn't pass authentication headers to
96103
# FCGI instances. If your environment matches this description and you cannot
@@ -177,26 +184,37 @@ def authorization(request)
177184
end
178185

179186
# Raises error unless the request credentials response value matches the expected value.
187+
# First try the password as a ha1 digest password. If this fails, then try it as a plain
188+
# text password.
180189
def validate_digest_response(request, realm, &password_procedure)
181190
credentials = decode_credentials_header(request)
182191
valid_nonce = validate_nonce(request, credentials[:nonce])
183192

184-
if valid_nonce && realm == credentials[:realm] && opaque(request.session.session_id) == credentials[:opaque]
193+
if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque]
185194
password = password_procedure.call(credentials[:username])
186-
expected = expected_response(request.env['REQUEST_METHOD'], credentials[:uri], credentials, password)
187-
expected == credentials[:response]
195+
196+
[true, false].any? do |password_is_ha1|
197+
expected = expected_response(request.env['REQUEST_METHOD'], request.env['REQUEST_URI'], credentials, password, password_is_ha1)
198+
expected == credentials[:response]
199+
end
188200
end
189201
end
190202

191203
# Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
192-
def expected_response(http_method, uri, credentials, password)
193-
ha1 = ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
204+
# Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
205+
# of a plain-text password.
206+
def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
207+
ha1 = password_is_ha1 ? password : ha1(credentials, password)
194208
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
195209
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':'))
196210
end
197211

198-
def encode_credentials(http_method, credentials, password)
199-
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password)
212+
def ha1(credentials, password)
213+
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
214+
end
215+
216+
def encode_credentials(http_method, credentials, password, password_is_ha1)
217+
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
200218
"Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ')
201219
end
202220

@@ -213,8 +231,7 @@ def decode_credentials(header)
213231
end
214232

215233
def authentication_header(controller, realm)
216-
session_id = controller.request.session.session_id
217-
controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce(session_id)}", opaque="#{opaque(session_id)}")
234+
controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
218235
end
219236

220237
def authentication_request(controller, realm, message = nil)
@@ -252,23 +269,36 @@ def authentication_request(controller, realm, message = nil)
252269
# POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
253270
# of this document.
254271
#
255-
# The nonce is opaque to the client.
256-
def nonce(session_id, time = Time.now)
272+
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
273+
# key from the Rails session secret generated upon creation of project. Ensures
274+
# the time cannot be modifed by client.
275+
def nonce(time = Time.now)
257276
t = time.to_i
258-
hashed = [t, session_id]
277+
hashed = [t, secret_key]
259278
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
260279
Base64.encode64("#{t}:#{digest}").gsub("\n", '')
261280
end
262281

263-
def validate_nonce(request, value)
282+
# Might want a shorter timeout depending on whether the request
283+
# is a PUT or POST, and if client is browser or web service.
284+
# Can be much shorter if the Stale directive is implemented. This would
285+
# allow a user to use new nonce without prompting user again for their
286+
# username and password.
287+
def validate_nonce(request, value, seconds_to_timeout=5*60)
264288
t = Base64.decode64(value).split(":").first.to_i
265-
nonce(request.session.session_id, t) == value && (t - Time.now.to_i).abs <= 10 * 60
289+
nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
266290
end
267291

268-
# Opaque based on digest of session_id
269-
def opaque(session_id)
270-
Base64.encode64(::Digest::MD5::hexdigest(session_id)).gsub("\n", '')
292+
# Opaque based on random generation - but changing each request?
293+
def opaque()
294+
::Digest::MD5.hexdigest(secret_key)
271295
end
296+
297+
# Set in /initializers/session_store.rb, and loaded even if sessions are not in use.
298+
def secret_key
299+
ActionController::Base.session_options[:secret]
300+
end
301+
272302
end
273303
end
274304
end

actionpack/lib/action_controller/integration.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
module ActionController
66
module Integration #:nodoc:
77
# An integration Session instance represents a set of requests and responses
8-
# performed sequentially by some virtual user. Becase you can instantiate
8+
# performed sequentially by some virtual user. Because you can instantiate
99
# multiple sessions and run them side-by-side, you can also mimic (to some
1010
# limited extent) multiple simultaneous users interacting with your system.
1111
#

actionpack/lib/action_controller/polymorphic_routes.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,18 @@ def build_named_route_call(records, namespace, inflection, options = {})
163163
if parent.is_a?(Symbol) || parent.is_a?(String)
164164
string << "#{parent}_"
165165
else
166-
string << "#{RecordIdentifier.__send__("singular_class_name", parent)}_"
166+
string << "#{RecordIdentifier.__send__("plural_class_name", parent)}".singularize
167+
string << "_"
167168
end
168169
end
169170
end
170171

171172
if record.is_a?(Symbol) || record.is_a?(String)
172173
route << "#{record}_"
173174
else
174-
route << "#{RecordIdentifier.__send__("#{inflection}_class_name", record)}_"
175+
route << "#{RecordIdentifier.__send__("plural_class_name", record)}"
176+
route = route.singularize if inflection == :singular
177+
route << "_"
175178
end
176179

177180
action_prefix(options) + namespace + route + routing_type(options).to_s

actionpack/lib/action_controller/resources.rb

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ def map_member_actions(map, resource)
630630
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
631631
action_path ||= Base.resources_path_names[action] || action
632632

633-
map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m)
633+
map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true })
634634
end
635635
end
636636
end
@@ -641,9 +641,9 @@ def map_member_actions(map, resource)
641641
map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
642642
end
643643

644-
def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil)
644+
def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} )
645645
if resource.has_action?(action)
646-
action_options = action_options_for(action, resource, method)
646+
action_options = action_options_for(action, resource, method, resource_options)
647647
formatted_route_path = "#{route_path}.:format"
648648

649649
if route_name && @set.named_routes[route_name.to_sym].nil?
@@ -660,22 +660,18 @@ def add_conditions_for(conditions, method)
660660
end
661661
end
662662

663-
def action_options_for(action, resource, method = nil)
663+
def action_options_for(action, resource, method = nil, resource_options = {})
664664
default_options = { :action => action.to_s }
665665
require_id = !resource.kind_of?(SingletonResource)
666+
force_id = resource_options[:force_id] && !resource.kind_of?(SingletonResource)
666667

667668
case default_options[:action]
668669
when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
669670
when "create"; default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements)
670671
when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
671672
when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
672673
when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
673-
else
674-
if method.nil? || resource.member_methods.nil? || resource.member_methods[method.to_sym].nil?
675-
default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements)
676-
else
677-
resource.member_methods[method.to_sym].include?(action) ? default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(require_id)) : default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements)
678-
end
674+
else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(force_id))
679675
end
680676
end
681677
end

actionpack/lib/action_controller/vendor/rack-1.0/rack.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ def self.version
2323

2424
# Return the Rack release as a dotted string.
2525
def self.release
26-
"0.4"
26+
"1.0 bundled"
2727
end
2828

2929
autoload :Builder, "rack/builder"
3030
autoload :Cascade, "rack/cascade"
31+
autoload :Chunked, "rack/chunked"
3132
autoload :CommonLogger, "rack/commonlogger"
3233
autoload :ConditionalGet, "rack/conditionalget"
3334
autoload :ContentLength, "rack/content_length"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require 'rack/utils'
2+
3+
module Rack
4+
5+
# Middleware that applies chunked transfer encoding to response bodies
6+
# when the response does not include a Content-Length header.
7+
class Chunked
8+
include Rack::Utils
9+
10+
def initialize(app)
11+
@app = app
12+
end
13+
14+
def call(env)
15+
status, headers, body = @app.call(env)
16+
headers = HeaderHash.new(headers)
17+
18+
if env['HTTP_VERSION'] == 'HTTP/1.0' ||
19+
STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
20+
headers['Content-Length'] ||
21+
headers['Transfer-Encoding']
22+
[status, headers.to_hash, body]
23+
else
24+
dup.chunk(status, headers, body)
25+
end
26+
end
27+
28+
def chunk(status, headers, body)
29+
@body = body
30+
headers.delete('Content-Length')
31+
headers['Transfer-Encoding'] = 'chunked'
32+
[status, headers.to_hash, self]
33+
end
34+
35+
def each
36+
term = "\r\n"
37+
@body.each do |chunk|
38+
size = bytesize(chunk)
39+
next if size == 0
40+
yield [size.to_s(16), term, chunk, term].join
41+
end
42+
yield ["0", term, "", term].join
43+
end
44+
45+
def close
46+
@body.close if @body.respond_to?(:close)
47+
end
48+
end
49+
end

actionpack/lib/action_controller/vendor/rack-1.0/rack/content_length.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,23 @@
33
module Rack
44
# Sets the Content-Length header on responses with fixed-length bodies.
55
class ContentLength
6+
include Rack::Utils
7+
68
def initialize(app)
79
@app = app
810
end
911

1012
def call(env)
1113
status, headers, body = @app.call(env)
12-
headers = Utils::HeaderHash.new(headers)
14+
headers = HeaderHash.new(headers)
1315

14-
if !Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
16+
if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
1517
!headers['Content-Length'] &&
1618
!headers['Transfer-Encoding'] &&
1719
(body.respond_to?(:to_ary) || body.respond_to?(:to_str))
1820

1921
body = [body] if body.respond_to?(:to_str) # rack 0.4 compat
20-
length = body.to_ary.inject(0) { |len, part| len + part.length }
22+
length = body.to_ary.inject(0) { |len, part| len + bytesize(part) }
2123
headers['Content-Length'] = length.to_s
2224
end
2325

actionpack/lib/action_controller/vendor/rack-1.0/rack/deflater.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ def call(env)
3636
mtime = headers.key?("Last-Modified") ?
3737
Time.httpdate(headers["Last-Modified"]) : Time.now
3838
body = self.class.gzip(body, mtime)
39-
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
39+
size = Rack::Utils.bytesize(body)
4040
headers = headers.merge("Content-Encoding" => "gzip", "Content-Length" => size.to_s)
4141
[status, headers, [body]]
4242
when "deflate"
4343
body = self.class.deflate(body)
44-
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
44+
size = Rack::Utils.bytesize(body)
4545
headers = headers.merge("Content-Encoding" => "deflate", "Content-Length" => size.to_s)
4646
[status, headers, [body]]
4747
when "identity"

actionpack/lib/action_controller/vendor/rack-1.0/rack/directory.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def check_forbidden
7070
return unless @path_info.include? ".."
7171

7272
body = "Forbidden\n"
73-
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
73+
size = Rack::Utils.bytesize(body)
7474
return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]]
7575
end
7676

@@ -122,7 +122,7 @@ def list_path
122122

123123
def entity_not_found
124124
body = "Entity not found: #{@path_info}\n"
125-
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
125+
size = Rack::Utils.bytesize(body)
126126
return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]]
127127
end
128128

actionpack/lib/action_controller/vendor/rack-1.0/rack/file.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def serving
6060
body = self
6161
else
6262
body = [F.read(@path)]
63-
size = body.first.size
63+
size = Utils.bytesize(body.first)
6464
end
6565

6666
[200, {

0 commit comments

Comments
 (0)