Class: Clickwrap::Import::Legacy

Inherits:
Object
  • Object
show all
Defined in:
lib/clickwrap/import/legacy.rb

Overview

Clickwrap.import_legacy! — bring an accepted_terms_at column, a bespoke audit table, or any other pre-Clickwrap record into the event log without inventing anything that was never recorded.

=========================================================================== The governing rule: HISTORICAL WEAKNESS STAYS VISIBLE RATHER THAN BEING LAUNDERED INTO MODERN CERTAINTY.

An import is the single easiest place in this gem to manufacture evidence by accident. Every field a modern capture fills in is sitting right there with an obvious plausible value: the current Terms text, today's document digest, the submit-button label from the current view, the assertion sentence from the current policy, an IP address from the user's last session. Writing any of them here would produce a row that is indistinguishable from a real capture and is, in the parts that matter, a fabrication.

So this importer NEVER synthesizes:

* a presentation manifest — nobody signed one, and there is no offer to
reproduce;
* an assertion — we do not know the sentence the old system offered;
* submit-button text — we do not know what the control said;
* an IP address or browser user-agent — these were not observed by us,
and a later session's address is a different fact about a different
request;
* document bytes or a digest — unless the caller can point at a version
that is actually published here.

Every key the caller lists in unknown: is recorded explicitly as unknown in a structured field on the event, AND said in plain words in the assertion text of each statement — which is inside the digested canonical body, so the admission travels with the evidence rather than beside it.

The event also keeps occurred_at (when it happened, according to the old record) strictly separate from recorded_at_by_server (when we wrote it down). That gap is a fact about the evidence and it stays visible.

Defined Under Namespace

Classes: Result

Constant Summary collapse

PERMITTED_CHANNELS =

The capture channels an import may claim. imported_provider means the record came from another system; system means this application wrote it without a human at a keyboard. Neither is web_browser, because no browser was involved and a receipt that said otherwise would be wrong.

%w[imported_provider system].freeze
CONVENTIONAL_UNKNOWN_KEYS =

Naming an unknown as unknown is the whole point, so the vocabulary is open: a host may list any key it wants. These are the ones the README and the FinePrint importer use, kept here so a typo in a migration script is at least visibly a typo next to its neighbours.

%w[
  exact_document_bytes
  document_version
  presentation
  presentation_manifest
  assertion
  submit_button_text
  protected_action
  request_evidence
  ip_address
  browser_user_agent
  capture_channel
  authentication_context
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(policy:, actor:, occurred_at:, because:, known: {}, unknown: [], dry_run: false, subject: nil, tenant: nil, statements: nil, capture_channel: "imported_provider", source: nil, counts_as_current: true) ⇒ Legacy

Returns a new instance of Legacy.



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/clickwrap/import/legacy.rb', line 83

def initialize(policy:, actor:, occurred_at:, because:, known: {}, unknown: [],
               dry_run: false, subject: nil, tenant: nil, statements: nil,
               capture_channel: "imported_provider", source: nil,
               counts_as_current: true)
  @policy = policy
  @actor = actor
  @occurred_at = coerce_time(occurred_at)
  @because = because.to_s
  @known = normalize_known(known)
  @unknown = normalize_unknown(unknown)
  @dry_run = dry_run
  @subject = subject
  @tenant = tenant
  @statement_keys = statements&.map(&:to_s)
  @capture_channel = capture_channel.to_s
  @source = source&.to_s
  @counts_as_current = counts_as_current == true
end

Instance Attribute Details

#actorObject (readonly)

Returns the value of attribute actor.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def actor
  @actor
end

#becauseObject (readonly)

Returns the value of attribute because.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def because
  @because
end

#capture_channelObject (readonly)

Returns the value of attribute capture_channel.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def capture_channel
  @capture_channel
end

#counts_as_currentObject (readonly)

Returns the value of attribute counts_as_current.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def counts_as_current
  @counts_as_current
end

#dry_runObject (readonly)

Returns the value of attribute dry_run.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def dry_run
  @dry_run
end

#knownObject (readonly)

Returns the value of attribute known.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def known
  @known
end

#occurred_atObject (readonly)

Returns the value of attribute occurred_at.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def occurred_at
  @occurred_at
end

#policyObject (readonly)

Returns the value of attribute policy.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def policy
  @policy
end

#sourceObject (readonly)

Returns the value of attribute source.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def source
  @source
end

#subjectObject (readonly)

Returns the value of attribute subject.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def subject
  @subject
end

#tenantObject (readonly)

Returns the value of attribute tenant.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def tenant
  @tenant
end

#unknownObject (readonly)

Returns the value of attribute unknown.



102
103
104
# File 'lib/clickwrap/import/legacy.rb', line 102

def unknown
  @unknown
end

Instance Method Details

#idempotency_keyObject

The derived key. Two runs of the same migration script over the same legacy row produce the same key, so re-running an import is a no-op rather than a second history for the same person.

It covers what the legacy record actually said: who, which policy, which subject and tenant, when it happened, and every known: value. Change any of those and it is a different import, which is correct — a different claim deserves a different event rather than silently colliding with the first one.



127
128
129
# File 'lib/clickwrap/import/legacy.rb', line 127

def idempotency_key
  @idempotency_key ||= "imported_legacy:#{Digest.hex(CanonicalJson.generate(identity_body))}"
end

#import!Object Also known as: call



105
106
107
108
109
110
111
112
113
114
# File 'lib/clickwrap/import/legacy.rb', line 105

def import!
  validate!

  existing = Event.find_by(policy_key: policy.key, idempotency_key: idempotency_key)
  return already_imported(existing) if existing

  return planned if dry_run

  write!
end