Class: Clickwrap::IpGeolocation::TrackdownResolver
- Defined in:
- lib/clickwrap/ip_geolocation/trackdown_resolver.rb
Overview
The optional official adapter for the trackdown gem.
bundle add trackdown --version ">= 0.4"
Clickwrap.configure do |config|
config.ip_geolocation_resolver = Clickwrap::IpGeolocation::TrackdownResolver.new
end
trackdown is NOT a dependency of this gem and must never become one. It
is required lazily, inside the constructor, so that a host who never names
this class never loads it — and so that a host who does name it without
installing it gets one sentence telling them what to do, at the line in
their initializer that asked for it, instead of a NameError somewhere in
the middle of a capture.
Two rules shape the mapping below, and both exist because this adapter sits between a general-purpose geolocation gem and an evidence record that has to still be readable and honest in several years.
ONE: every field is read through respond_to?. Trackdown's result object
has gained fields across releases and will gain more; a NoMethodError
during a capture would roll back the protected action for a field the
policy may not even have authorized. Reading defensively means a newer
Trackdown supplying an accuracy radius is picked up here with no change,
and an older one simply reports nil.
TWO: to_h is never persisted. Trackdown's to_h includes country_info
— the whole ISO3166 country record — and a general gem is right to offer
it. Copying it into evidence would store data no policy authorized and no
receipt could explain. Clickwrap copies named fields, one at a time, and
the extractor then keeps only the subset the server-owned policy allowed.
Pinned sources for the mapping (released tag, not a moving branch):
result object -> https://github.com/rameerez/trackdown/blob/v0.4.0/lib/trackdown/location_result.rb
Cloudflare -> https://github.com/rameerez/trackdown/blob/v0.4.0/lib/trackdown/providers/cloudflare_provider.rb
MaxMind -> https://github.com/rameerez/trackdown/blob/v0.4.0/lib/trackdown/providers/maxmind_provider.rb
Trackdown 0.4.0 closed the provenance gap scoped in https://github.com/rameerez/trackdown/issues/8. This adapter requires that release rather than silently manufacturing the missing facts itself.
Constant Summary collapse
- PROVIDER_NAME =
"trackdown"- MINIMUM_TRACKDOWN_VERSION =
Gem::Version.new("0.4.0")
- PLACEHOLDER_VALUES =
Trackdown returns the string "Unknown" for a country or city it could not determine, and Cloudflare's own "no country" code is "XX" (pinned provider source above). Neither is a place. Written into a receipt they would be indistinguishable from a country a provider actually reported, so they are mapped back to nil and the extractor records "the provider supplied no authorized field" instead. This is the single most important line in this file.
["unknown", "n/a", "xx", "-"].freeze
- FIELD_READERS =
What Trackdown could supply at all, by Clickwrap field name. Read dynamically from the installed result object where possible, so a Trackdown release that adds accuracy radius starts reporting the capability without an edit here.
{ country: %i[country_code country_name], region: %i[region region_name region_code], city: %i[city city_name], postal_code: %i[postal_code], latitude_and_longitude: %i[latitude longitude], timezone: %i[timezone time_zone], continent: %i[continent continent_code], metro_code: %i[metro_code], accuracy_radius_in_kilometers: %i[accuracy_radius accuracy_radius_in_kilometers] }.freeze
- PINNED_CAPABILITIES =
What Trackdown 0.4 can supply across its providers, used only when the result class cannot be inspected. Cloudflare does not supply an accuracy radius, while MaxMind does; with Trackdown's default :auto provider the adapter can therefore supply it even though any one lookup may not.
%i[ country region city postal_code latitude_and_longitude timezone continent metro_code accuracy_radius_in_kilometers ].freeze
Instance Attribute Summary collapse
-
#capabilities ⇒ Object
readonly
Returns the value of attribute capabilities.
Instance Method Summary collapse
-
#initialize(provider_source: nil, source_verified_by_host: nil) ⇒ TrackdownResolver
constructor
Trust is per request in Trackdown 0.4.
- #resolve(ip_address, http_request: nil) ⇒ Object
Constructor Details
#initialize(provider_source: nil, source_verified_by_host: nil) ⇒ TrackdownResolver
Trust is per request in Trackdown 0.4. A host registers its verifier with Trackdown, Trackdown runs it against the same request that supplied the CDN headers, and this adapter copies the result's explicit trust state. A constructor-wide boolean would overclaim every request after one deployment assertion, so the old experimental option is refused by name.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/clickwrap/ip_geolocation/trackdown_resolver.rb', line 91 def initialize(provider_source: nil, source_verified_by_host: nil) super() require "trackdown" unless defined?(::Trackdown) ensure_supported_trackdown_version! unless source_verified_by_host.nil? raise ConfigurationError, "TrackdownResolver no longer accepts `source_verified_by_host:`. Trackdown " \ "0.4 verifies source trust per request. Configure " \ "`Trackdown.configuration.verify_request_came_through_trusted_cloudflare_path_with` " \ "or the matching CloudFront helper; the resolver will record the result's " \ "`source_was_verified_by_host?` value." end @provider_source_fallback = (provider_source || configured_provider_source).to_s @capabilities = detect_capabilities.freeze freeze rescue ::LoadError => error raise ConfigurationError, "Clickwrap::IpGeolocation::TrackdownResolver needs the `trackdown` gem, which is " \ "not installed. Run `bundle add trackdown --version \">= 0.4\"` and configure " \ "it (it needs either a " \ "MaxMind database or Cloudflare visitor-location headers), or set " \ "`config.ip_geolocation_resolver = nil` and turn off the IP-geolocation fields " \ "your policies enable. The underlying load error was: #{error.}" end |
Instance Attribute Details
#capabilities ⇒ Object (readonly)
Returns the value of attribute capabilities.
84 85 86 |
# File 'lib/clickwrap/ip_geolocation/trackdown_resolver.rb', line 84 def capabilities @capabilities end |
Instance Method Details
#resolve(ip_address, http_request: nil) ⇒ Object
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/clickwrap/ip_geolocation/trackdown_resolver.rb', line 119 def resolve(ip_address, http_request: nil) address = ip_address.to_s.strip return unavailable("no_ip_address_to_resolve") if address.empty? result = ::Trackdown.locate(address, request: http_request) return unavailable("provider_returned_no_result") if result.nil? if result.respond_to?(:unavailable?) && result.unavailable? return unavailable( text(result, :unavailable_reason) || "provider_returned_unavailable", result: ) end location = build_location(result) # Trackdown answers with a result object full of "Unknown" when no # provider is configured, when its database has no row for the address, # and when a Cloudflare country header says "XX". Once the placeholders # are mapped away that is an empty answer, and an empty answer is an # unavailable result with a reason on it — not a location whose every # field happens to be blank. return unavailable("provider_supplied_no_location_fields", result:) unless location.any_data_field? location rescue StandardError => error # Trackdown raises for ordinary conditions — a private or loopback # address in development, a missing MaxMind database, a lookup timeout. # None of those should abort a capture by itself: the policy decides # whether unavailable IP geolocation is fatal. The reason carries the # error CLASS and never the message, because a provider message can # quote the IP address and this string is written to a column that a # redacted receipt is allowed to show. unavailable("trackdown_raised_#{error.class}") end |