Permalink
Fetching contributors…
Cannot retrieve contributors at this time
86 lines (70 sloc) 2.17 KB
require "ipaddr"
module Rack
##
# Rack middleware for limiting access based on IP address
#
#
# === Options:
#
# path => ipmasks ipmasks: Array of remote addresses which are allowed to access
#
# === Examples:
#
# use Rack::Access, '/backend' => [ '127.0.0.1', '192.168.1.0/24' ]
#
#
class Access
attr_reader :options
def initialize(app, options = {})
@app = app
mapping = options.empty? ? {"/" => ["127.0.0.1"]} : options
@mapping = remap(mapping)
end
def remap(mapping)
mapping.map { |location, ipmasks|
if location =~ %r{\Ahttps?://(.*?)(/.*)}
host, location = $1, $2
else
host = nil
end
unless location[0] == ?/
raise ArgumentError, "paths need to start with /"
end
location = location.chomp('/')
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n')
ipmasks.collect! do |ipmask|
ipmask.is_a?(IPAddr) ? ipmask : IPAddr.new(ipmask)
end
[host, location, match, ipmasks]
}.sort_by { |(h, l, m, a)| [h ? -h.size : (-1.0 / 0.0), -l.size] } # Longest path first
end
def call(env)
@original_request = Request.new(env)
ipmasks = ipmasks_for_path(env)
return forbidden! unless ip_authorized?(ipmasks)
status, headers, body = @app.call(env)
[status, headers, body]
end
def ipmasks_for_path(env)
path = env["PATH_INFO"].to_s
hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
@mapping.each do |host, location, match, ipmasks|
next unless (hHost == host || sName == host \
|| (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
next unless path =~ match && rest = $1
next unless rest.empty? || rest[0] == ?/
return ipmasks
end
nil
end
def forbidden!
[403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, []]
end
def ip_authorized?(ipmasks)
return true if ipmasks.nil?
ipmasks.any? do |ip_mask|
ip_mask.include?(IPAddr.new(@original_request.ip))
end
end
end
end