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.



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

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.



21
22
23
# File 'lib/rack/contrib/access.rb', line 21

def options
  @options
end

Instance Method Details

#call(env) ⇒ Object



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

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

#forbidden!Object



72
73
74
# File 'lib/rack/contrib/access.rb', line 72

def forbidden!
  [403, { 'Content-Type' => 'text/html', 'Content-Length' => '0' }, '']
end

#ip_authorized?(ipmasks) ⇒ Boolean

Returns:

  • (Boolean)


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

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

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

#ipmasks_for_path(env) ⇒ Object



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

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



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

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