Skip to content

Commit

Permalink
Merge branch 'master' of github.com:lifo/docrails
Browse files Browse the repository at this point in the history
  • Loading branch information
oscardelben committed Apr 25, 2012
2 parents ee4e712 + 4a3ce15 commit 4179141
Show file tree
Hide file tree
Showing 95 changed files with 873 additions and 667 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Expand Up @@ -29,7 +29,7 @@ end
gem 'uglifier', '>= 1.0.3', :require => false

gem 'rake', '>= 0.8.7'
gem 'mocha', '>= 0.9.8'
gem 'mocha', '>= 0.11.2'

group :doc do
# The current sdoc cannot generate GitHub links due
Expand Down
2 changes: 1 addition & 1 deletion actionmailer/test/i18n_with_controller_test.rb
Expand Up @@ -24,7 +24,7 @@ def send_mail
class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new
Routes.draw do
match ':controller(/:action(/:id))'
get ':controller(/:action(/:id))'
end

def app
Expand Down
4 changes: 2 additions & 2 deletions actionmailer/test/url_test.rb
Expand Up @@ -57,8 +57,8 @@ def test_signed_up_with_url
UrlTestMailer.delivery_method = :test

AppRoutes.draw do
match ':controller(/:action(/:id))'
match '/welcome' => "foo#bar", :as => "welcome"
get ':controller(/:action(/:id))'
get '/welcome' => "foo#bar", :as => "welcome"
end

expected = new_mail
Expand Down
2 changes: 2 additions & 0 deletions actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -1,3 +1,5 @@
require 'active_support/core_ext/module/attribute_accessors'

module ActionDispatch
module Http
module MimeNegotiation
Expand Down
47 changes: 26 additions & 21 deletions actionpack/lib/action_dispatch/http/url.rb
Expand Up @@ -23,38 +23,43 @@ def extract_subdomain(host, tld_length = @@tld_length)
end

def url_for(options = {})
if options[:host].blank? && options[:only_path].blank?
raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
end

rewritten_url = ""

unless options[:only_path]
unless options[:protocol] == false
rewritten_url << (options[:protocol] || "http")
rewritten_url << ":" unless rewritten_url.match(%r{:|//})
end
rewritten_url << "//" unless rewritten_url.match("//")
rewritten_url << rewrite_authentication(options)
rewritten_url << host_or_subdomain_and_domain(options)
rewritten_url << ":#{options.delete(:port)}" if options[:port]
end

path = ""
path << options.delete(:script_name).to_s.chomp("/")
path << options.delete(:path).to_s

params = options[:params] || {}
params.reject! {|k,v| v.to_param.nil? }

rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
rewritten_url << "?#{params.to_query}" unless params.empty?
rewritten_url << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
rewritten_url
result = build_host_url(options)

result << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
result << "?#{params.to_query}" unless params.empty?
result << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
result
end

private

def build_host_url(options)
if options[:host].blank? && options[:only_path].blank?
raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
end

result = ""

unless options[:only_path]
unless options[:protocol] == false
result << (options[:protocol] || "http")
result << ":" unless result.match(%r{:|//})
end
result << "//" unless result.match("//")
result << rewrite_authentication(options)
result << host_or_subdomain_and_domain(options)
result << ":#{options.delete(:port)}" if options[:port]
end
result
end

def named_host?(host)
host && IP_HOST_REGEXP !~ host
end
Expand Down
90 changes: 68 additions & 22 deletions actionpack/lib/action_dispatch/middleware/remote_ip.rb
Expand Up @@ -5,11 +5,14 @@ class IpSpoofAttackError < StandardError ; end
# IP addresses that are "trusted proxies" that can be stripped from
# the comma-delimited list in the X-Forwarded-For header. See also:
# http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
# http://en.wikipedia.org/wiki/Private_network#Private_IPv6_addresses.
TRUSTED_PROXIES = %r{
^127\.0\.0\.1$ | # localhost
^::1$ |
^(10 | # private IP 10.x.x.x
172\.(1[6-9]|2[0-9]|3[0-1]) | # private IP in the range 172.16.0.0 .. 172.31.255.255
192\.168 # private IP 192.168.x.x
192\.168 | # private IP 192.168.x.x
fc00:: # private IP fc00
)\.
}x

Expand All @@ -19,13 +22,13 @@ def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
@app = app
@check_ip = check_ip_spoofing
@proxies = case custom_proxies
when Regexp
custom_proxies
when nil
TRUSTED_PROXIES
else
Regexp.union(TRUSTED_PROXIES, custom_proxies)
end
when Regexp
custom_proxies
when nil
TRUSTED_PROXIES
else
Regexp.union(TRUSTED_PROXIES, custom_proxies)
end
end

def call(env)
Expand All @@ -34,6 +37,31 @@ def call(env)
end

class GetIp

# IP v4 and v6 (with compression) validation regexp
# https://gist.github.com/1289635
VALID_IP = %r{
(^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4
(^(
(([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}) | # ip v6 not abbreviated
(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4}) | # ip v6 with double colon in the end
(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4}) | # - ip addresses v6
(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4}) | # - with
(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4}) | # - double colon
(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4}) | # - in the middle
(([0-9A-Fa-f]{1,4}:){6} ((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3} (\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
(([0-9A-Fa-f]{1,4}:){1,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
(([0-9A-Fa-f]{1,4}:){1}:([0-9A-Fa-f]{1,4}:){0,4}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
(([0-9A-Fa-f]{1,4}:){0,2}:([0-9A-Fa-f]{1,4}:){0,3}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
(([0-9A-Fa-f]{1,4}:){0,3}:([0-9A-Fa-f]{1,4}:){0,2}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
(([0-9A-Fa-f]{1,4}:){0,4}:([0-9A-Fa-f]{1,4}:){1}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d) |(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4}) | # ip v6 with compatible to v4
(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the begining
(([0-9A-Fa-f]{1,4}:){1,7}:) # ip v6 without ending
)$)
}x

def initialize(env, middleware)
@env = env
@middleware = middleware
Expand All @@ -44,25 +72,31 @@ def initialize(env, middleware)
# but will be wrong if the user is behind a proxy. Proxies will set
# HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR, so we prioritize those.
# HTTP_X_FORWARDED_FOR may be a comma-delimited list in the case of
# multiple chained proxies. The last address which is not a known proxy
# will be the originating IP.
# multiple chained proxies. The first address which is in this list
# if it's not a known proxy will be the originating IP.
# Format of HTTP_X_FORWARDED_FOR:
# client_ip, proxy_ip1, proxy_ip2...
# http://en.wikipedia.org/wiki/X-Forwarded-For
def calculate_ip
client_ip = @env['HTTP_CLIENT_IP']
forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR')
remote_addrs = ips_from('REMOTE_ADDR')
client_ip = @env['HTTP_CLIENT_IP']
forwarded_ip = ips_from('HTTP_X_FORWARDED_FOR').first
remote_addrs = ips_from('REMOTE_ADDR')

check_ip = client_ip && @middleware.check_ip
if check_ip && !forwarded_ips.include?(client_ip)
if check_ip && forwarded_ip != client_ip
# We don't know which came from the proxy, and which from the user
raise IpSpoofAttackError, "IP spoofing attack?!" \
"HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
"HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
end

not_proxy = client_ip || forwarded_ips.first || remote_addrs.first

# Return first REMOTE_ADDR if there are no other options
not_proxy || ips_from('REMOTE_ADDR', :allow_proxies).first
client_ips = remove_proxies [client_ip, forwarded_ip, remote_addrs].flatten
if client_ips.present?
client_ips.first
else
# If there is no client ip we can return first valid proxy ip from REMOTE_ADDR
remote_addrs.find { |ip| valid_ip? ip }
end
end

def to_s
Expand All @@ -71,12 +105,24 @@ def to_s
@ip = calculate_ip
end

protected
private

def ips_from(header, allow_proxies = false)
ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
allow_proxies ? ips : ips.reject{|ip| ip =~ @middleware.proxies }
def ips_from(header)
@env[header] ? @env[header].strip.split(/[,\s]+/) : []
end

def valid_ip?(ip)
ip =~ VALID_IP
end

def not_a_proxy?(ip)
ip !~ @middleware.proxies
end

def remove_proxies(ips)
ips.select { |ip| valid_ip?(ip) && not_a_proxy?(ip) }
end

end

end
Expand Down
15 changes: 13 additions & 2 deletions actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/enumerable'
require 'active_support/inflector'
Expand Down Expand Up @@ -58,6 +59,16 @@ def initialize(set, scope, path, options)
@options = (@scope[:options] || {}).merge(options)
@path = normalize_path(path)
normalize_options!

via_all = @options.delete(:via) if @options[:via] == :all

if !via_all && request_method_condition.empty?
msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
"If you want to expose your action to GET, use `get` in the router:\n\n" \
" Instead of: match \"controller#action\"\n" \
" Do: get \"controller#action\""
raise msg
end
end

def to_route
Expand Down Expand Up @@ -263,7 +274,7 @@ module Base
# of most Rails applications, this is beneficial.
def root(options = {})
options = { :to => options } if options.is_a?(String)
match '/', { :as => :root }.merge(options)
match '/', { :as => :root, :via => :get }.merge(options)
end

# Matches a url pattern to one or more routes. Any symbols in a pattern
Expand Down Expand Up @@ -416,7 +427,7 @@ def mount(app, options = nil)

options[:as] ||= app_name(app)

match(path, options.merge(:to => app, :anchor => false, :format => false))
match(path, options.merge(:to => app, :anchor => false, :format => false, :via => :all))

define_generate_prefix(app, options[:as])
self
Expand Down

0 comments on commit 4179141

Please sign in to comment.