Class: Rack::Access

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/contrib/access.rb

Overview

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' ]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ Access

Returns a new instance of Access.



25
26
27
28
29
# File 'lib/rack/contrib/access.rb', line 25

def initialize(app, options = {})
  @app = app
  mapping = options.empty? ? {"/" => ["127.0.0.1"]} : options
  @mapping = remap(mapping)
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



23
24
25
# File 'lib/rack/contrib/access.rb', line 23

def options
  @options
end

Instance Method Details

#call(env) ⇒ Object



52
53
54
55
56
57
58
# File 'lib/rack/contrib/access.rb', line 52

def call(env)
  request = Request.new(env)
  ipmasks = ipmasks_for_path(env)
  return forbidden! unless ip_authorized?(request, ipmasks)
  status, headers, body = @app.call(env)
  [status, headers, body]
end

#forbidden!Object



74
75
76
# File 'lib/rack/contrib/access.rb', line 74

def forbidden!
  [403, { 'content-type' => 'text/html', 'content-length' => '0' }, []]
end

#ip_authorized?(request, ipmasks) ⇒ Boolean

Returns:

  • (Boolean)


78
79
80
81
82
83
84
# File 'lib/rack/contrib/access.rb', line 78

def ip_authorized?(request, ipmasks)
  return true if ipmasks.nil?

  ipmasks.any? do |ip_mask|
    ip_mask.include?(IPAddr.new(request.ip))
  end
end

#ipmasks_for_path(env) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/rack/contrib/access.rb', line 60

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

#remap(mapping) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/rack/contrib/access.rb', line 31

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('/', '/+')}(.*)", Regexp::NOENCODING)

    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