Class: Clickwrap::RequestEvidenceExtractor
- Inherits:
-
Object
- Object
- Clickwrap::RequestEvidenceExtractor
- Defined in:
- lib/clickwrap/request_evidence_extractor.rb
Overview
Resolves the optional request evidence for one capture: the IP address the server observed, the browser user-agent the client supplied, and whatever a resolver was authorized to estimate from that address.
Four properties of this class are load-bearing.
It runs SYNCHRONOUSLY, before the evidence and domain transaction opens. Not in a job, not after commit, not in a rescue that fills the gap in later. Evidence that arrives after the action it was supposed to accompany is a different claim from evidence that accompanied it, and a receipt cannot tell the two apart once they are in the same columns. Resolving before the transaction opens also keeps a provider lookup from holding locks on evidence rows while it waits on somebody else's network.
It WRITES NOTHING. It returns a value object holding the attribute hash for
one clickwrap_request_evidence row, and the caller writes that row inside
the transaction that carries the protected action. Required evidence and the
action commit together or not at all.
It reads ONLY from the HTTP request, through the host's configured readers. No parameter, no hidden form field, and no client-supplied header can select a policy, a resolver, a field, a precision, or a retention rule. A browser may answer a policy; it may never author one.
It RECORDS WHAT IT COULD NOT GET. Missing, refused, failed, and answered are four different states, and every one of them ends up in a column with a reason attached. Nothing here silently substitutes a blank, a zero, or the word "Unknown" for an answer nobody gave.
Nothing is collected unless the COMPILED policy says so by name. Application defaults are merged into that policy at boot, not here at capture time, so a configuration change necessarily produces a different policy revision and a policy can explicitly narrow an application default.
Defined Under Namespace
Classes: Resolved
Constant Summary collapse
- NO_HTTP_REQUEST =
Reason strings are permanent evidence vocabulary. They are added to, never renamed or repurposed: a receipt written today is read by code that may be years newer, and a reason that changed meaning underneath it would make old evidence say something it never said.
"no_http_request"- CHANNEL_CARRIES_NO_HTTP_REQUEST =
"capture_channel_carries_no_http_request"- NO_IP_ADDRESS_ON_HTTP_REQUEST =
"no_ip_address_on_http_request"- FORWARDED_CHAIN_REFUSED =
"ip_address_reader_returned_a_forwarded_chain"- NO_BROWSER_USER_AGENT_ON_HTTP_REQUEST =
"no_browser_user_agent_on_http_request"- RESOLVER_RETURNED_NO_RESULT =
"resolver_returned_no_result"- RESOLVER_CANNOT_SUPPLY_AUTHORIZED_FIELDS =
"resolver_cannot_supply_authorized_fields"- PROVIDER_SUPPLIED_NO_AUTHORIZED_FIELD =
"provider_supplied_no_authorized_field"- RAILS_REQUEST_REMOTE_IP_READER_NAME =
What produced the stored address. Rails'
request.remote_ipis the conventional reader and the one Clickwrap ships with; anything else is the host's own, and the receipt says so rather than implying Rails' spoof checks and trusted-proxy handling were involved when they were not. "rails_request_remote_ip"- HOST_CONFIGURED_READER_NAME =
"host_configured_reader"- CHANNELS_WITHOUT_AN_HTTP_REQUEST =
Channels that structurally carry no HTTP request. A background job has no browser and never had one; that is a fact about the capture, not a failure to collect something, and the reason string says which it was.
%w[background_job imported_provider system].freeze
- MAXIMUM_UNAVAILABLE_REASON_LENGTH =
Reasons are stored in a string column. A pathological error class name must not turn a recorded unavailability into a failed INSERT that rolls back the protected action.
200- TEXT_COLUMNS_BY_AUTHORIZED_FIELD =
Exactly which columns each authorized field unlocks. This table IS the minimization guarantee, which is why it is a table rather than a run of conditionals: a reviewer can see at a glance that authorizing
countryunlocks a country code and name and nothing else, and adding a column here is a visible decision to store more. Coordinates are absent on purpose — they are a coupled pair and are handled separately below. { "country" => { ip_geolocation_country_code: :country_code, ip_geolocation_country_name: :country_name }, "region" => { ip_geolocation_region_name: :region_name, ip_geolocation_region_code: :region_code }, "city" => { ip_geolocation_city_name: :city_name }, "postal_code" => { ip_geolocation_postal_code: :postal_code }, "timezone" => { ip_geolocation_timezone: :timezone }, "continent" => { ip_geolocation_continent_code: :continent_code }, "metro_code" => { ip_geolocation_metro_code: :metro_code } }.freeze
Class Method Summary collapse
-
.default_ip_address_reader_source_location ⇒ Object
The source location of a freshly built Configuration's default IP-address reader.
Instance Method Summary collapse
- #extract ⇒ Object
-
#initialize(policy:, http_request: nil, capture_channel: nil) ⇒ RequestEvidenceExtractor
constructor
policy:accepts a compiled Clickwrap::Policy or the RequestEvidencePolicy it carries, so a test can hand this class an allowlist directly.
Constructor Details
#initialize(policy:, http_request: nil, capture_channel: nil) ⇒ RequestEvidenceExtractor
policy: accepts a compiled Clickwrap::Policy or the RequestEvidencePolicy
it carries, so a test can hand this class an allowlist directly.
http_request: is nil for captures that genuinely have no request, and
that absence is recorded rather than papered over.
131 132 133 134 135 |
# File 'lib/clickwrap/request_evidence_extractor.rb', line 131 def initialize(policy:, http_request: nil, capture_channel: nil) @policy = policy.respond_to?(:request_evidence) ? policy.request_evidence : policy @http_request = http_request @capture_channel = capture_channel&.to_s end |
Class Method Details
.default_ip_address_reader_source_location ⇒ Object
The source location of a freshly built Configuration's default IP-address
reader. See #ip_address_reader_name for why this is a comparison
against a fresh object rather than against a constant.
115 116 117 118 119 120 121 122 123 124 |
# File 'lib/clickwrap/request_evidence_extractor.rb', line 115 def default_ip_address_reader_source_location return @default_ip_address_reader_source_location if defined?(@default_ip_address_reader_source_location) @default_ip_address_reader_source_location = begin Configuration.new.read_ip_address_from_http_request_with.source_location rescue StandardError nil end end |
Instance Method Details
#extract ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/clickwrap/request_evidence_extractor.rb', line 137 def extract return Resolved.none(authorized_fields: ) unless records_anything? attributes = { authorized_fields: } .merge(ip_address_attributes) .merge(browser_user_agent_attributes) .merge(ip_geolocation_attributes) Resolved.new(attributes: attributes.freeze, authorized_fields: , records_anything: true) end |