Module: BetterAuth::RequestIP
- Defined in:
- lib/better_auth/request_ip.rb
Constant Summary collapse
- LOCALHOST_IP =
"127.0.0.1"
Class Method Summary collapse
- .client_ip(source, options) ⇒ Object
- .fallback_ip(request, allow_forwarded_headers:) ⇒ Object
- .header_value(request, header) ⇒ Object
- .ip_from_header(value, trusted_proxies: nil, peer_ip: nil, ipv6_subnet: nil) ⇒ Object
- .native_address(address) ⇒ Object
- .normalize_ip(ip, ipv6_subnet: nil) ⇒ Object
- .parse_ip(ip) ⇒ Object
- .parse_trusted_proxy(entry) ⇒ Object
- .peer_ip(request) ⇒ Object
- .rack_header_name(header) ⇒ Object
- .test_or_development? ⇒ Boolean
- .unwrap_request(source) ⇒ Object
- .valid_ip?(ip) ⇒ Boolean
Class Method Details
.client_ip(source, options) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/better_auth/request_ip.rb', line 11 def client_ip(source, ) = .advanced[:ip_address] || {} return nil if [:disable_ip_tracking] request = unwrap_request(source) configured_headers = [:ip_address_headers] if configured_headers Array(configured_headers).each do |header| value = header_value(source, header) next unless value.is_a?(String) ip = ip_from_header( value, trusted_proxies: [:trusted_proxies], peer_ip: peer_ip(request), ipv6_subnet: [:ipv6_subnet] ) return ip if ip end end ip = fallback_ip(request, allow_forwarded_headers: configured_headers.nil?) return normalize_ip(ip, ipv6_subnet: [:ipv6_subnet]) if valid_ip?(ip) LOCALHOST_IP if test_or_development? end |
.fallback_ip(request, allow_forwarded_headers:) ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/better_auth/request_ip.rb', line 73 def fallback_ip(request, allow_forwarded_headers:) return nil unless request remote_ip = request.remote_ip.to_s if request.respond_to?(:remote_ip) return remote_ip if valid_ip?(remote_ip) direct_peer_ip = peer_ip(request) return nil unless valid_ip?(direct_peer_ip) if allow_forwarded_headers && request.respond_to?(:ip) ip = request.ip.to_s return ip if valid_ip?(ip) end direct_peer_ip end |
.header_value(request, header) ⇒ Object
65 66 67 68 69 70 71 |
# File 'lib/better_auth/request_ip.rb', line 65 def header_value(request, header) return request.get_header(rack_header_name(header)) if request.respond_to?(:get_header) return request.headers[header.to_s.downcase] if request.respond_to?(:headers) return request[header.to_s.downcase] || request[header.to_s] || request[header.to_sym] if request.is_a?(Hash) nil end |
.ip_from_header(value, trusted_proxies: nil, peer_ip: nil, ipv6_subnet: nil) ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/better_auth/request_ip.rb', line 38 def ip_from_header(value, trusted_proxies: nil, peer_ip: nil, ipv6_subnet: nil) forwarded_ips = value.split(",").map(&:strip).reject(&:empty?) return nil if forwarded_ips.empty? proxy_entries = Array(trusted_proxies).map(&:to_s).reject(&:empty?) if proxy_entries.any? proxies = proxy_entries.map { |entry| parse_trusted_proxy(entry) } return nil if proxies.any?(&:nil?) peer_address = parse_ip(peer_ip) return nil unless peer_address && proxies.any? { |proxy| proxy.include?(native_address(peer_address)) } forwarded_ips.reverse_each do |ip| address = parse_ip(ip) return nil unless address next if proxies.any? { |proxy| proxy.include?(native_address(address)) } return normalize_ip(ip, ipv6_subnet: ipv6_subnet) end return nil end return nil unless forwarded_ips.one? && valid_ip?(forwarded_ips.first) normalize_ip(forwarded_ips.first, ipv6_subnet: ipv6_subnet) end |
.native_address(address) ⇒ Object
158 159 160 161 162 163 164 |
# File 'lib/better_auth/request_ip.rb', line 158 def native_address(address) if address.respond_to?(:ipv4_mapped?) && address.ipv4_mapped? address.native else address end end |
.normalize_ip(ip, ipv6_subnet: nil) ⇒ Object
166 167 168 169 170 171 172 |
# File 'lib/better_auth/request_ip.rb', line 166 def normalize_ip(ip, ipv6_subnet: nil) address = IPAddr.new(ip) return address.native.to_s if address.respond_to?(:ipv4_mapped?) && address.ipv4_mapped? return address.to_s if address.ipv4? address.mask((ipv6_subnet || 64).to_i).to_s end |
.parse_ip(ip) ⇒ Object
132 133 134 135 136 |
# File 'lib/better_auth/request_ip.rb', line 132 def parse_ip(ip) IPAddr.new(ip) rescue IPAddr::InvalidAddressError, IPAddr::AddressFamilyError nil end |
.parse_trusted_proxy(entry) ⇒ Object
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/better_auth/request_ip.rb', line 138 def parse_trusted_proxy(entry) value = entry.to_s return nil if value.empty? || value.match?(/\s/) if value.include?("/") address, prefix = value.split("/", -1) return nil unless value.count("/") == 1 && prefix.match?(/\A\d+\z/) parsed_address = parse_ip(address) return nil unless parsed_address max_prefix = parsed_address.ipv4? ? 32 : 128 return nil if prefix.to_i > max_prefix end IPAddr.new(value) rescue IPAddr::InvalidAddressError, IPAddr::AddressFamilyError nil end |
.peer_ip(request) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/better_auth/request_ip.rb', line 90 def peer_ip(request) if request.respond_to?(:get_header) value = request.get_header("REMOTE_ADDR") return value.to_s unless value.nil? end if request.respond_to?(:env) value = request.env["REMOTE_ADDR"] return value.to_s unless value.nil? end if request.is_a?(Hash) value = request["REMOTE_ADDR"] || request[:REMOTE_ADDR] || request[:remote_addr] return value.to_s unless value.nil? end nil end |
.rack_header_name(header) ⇒ Object
122 123 124 |
# File 'lib/better_auth/request_ip.rb', line 122 def rack_header_name(header) "HTTP_#{header.to_s.upcase.tr("-", "_")}" end |
.test_or_development? ⇒ Boolean
174 175 176 177 178 |
# File 'lib/better_auth/request_ip.rb', line 174 def test_or_development? ["test", "development"].include?(ENV["RACK_ENV"]) || ["test", "development"].include?(ENV["RAILS_ENV"]) || ["test", "development"].include?(ENV["APP_ENV"]) end |
.unwrap_request(source) ⇒ Object
107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/better_auth/request_ip.rb', line 107 def unwrap_request(source) current = source seen = {} while current&.respond_to?(:request) break if seen[current.object_id] seen[current.object_id] = true nested = current.request break unless nested && !nested.equal?(current) current = nested end current end |
.valid_ip?(ip) ⇒ Boolean
126 127 128 129 130 |
# File 'lib/better_auth/request_ip.rb', line 126 def valid_ip?(ip) return false if ip.to_s.empty? || ip.to_s.match?(/\s/) !parse_ip(ip).nil? end |