Class: Clickwrap::RemediationToken
- Inherits:
-
Object
- Object
- Clickwrap::RemediationToken
- Defined in:
- lib/clickwrap/remediation_token.rb
Overview
A short-lived, signed handoff from a host gate to the standalone capture screen. It binds the exact actor, tenant, policy, subject, represented party, subject fingerprint, and local return path. The browser transports this context; it never chooses it.
Defined Under Namespace
Classes: Context
Constant Summary collapse
- PURPOSE =
"clickwrap/remediation"- SCHEMA =
"clickwrap.remediation.v1"
Class Method Summary collapse
- .issue(policy:, actor:, tenant: nil, subject: nil, represented_party: nil, return_to: nil, issued_at: nil) ⇒ Object
- .reset_verifier! ⇒ Object
- .resolve!(token, policy:, actor:) ⇒ Object
Class Method Details
.issue(policy:, actor:, tenant: nil, subject: nil, represented_party: nil, return_to: nil, issued_at: nil) ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/clickwrap/remediation_token.rb', line 16 def issue(policy:, actor:, tenant: nil, subject: nil, represented_party: nil, return_to: nil, issued_at: nil) issued_at ||= Clickwrap.now expires_at = issued_at + Clickwrap.config.remediation_token_valid_for attributes = { "schema" => SCHEMA, "policy" => policy.key, "actor_reference" => Reference.actor(actor), "tenant_reference" => Reference.tenant(tenant), "subject" => record_binding(subject, "subject", expires_at: expires_at), "subject_fingerprint" => SubjectFingerprint.for(policy, subject), "represented_party" => record_binding( represented_party, "represented_party", expires_at: expires_at ), "return_to" => return_to, "issued_at" => Receipt.format_time(issued_at), "expires_at" => Receipt.format_time(expires_at), "nonce" => SecureRandom.uuid }.compact verifier.generate(attributes, purpose: PURPOSE, expires_at: expires_at) end |
.reset_verifier! ⇒ Object
82 |
# File 'lib/clickwrap/remediation_token.rb', line 82 def reset_verifier! = @verifier = nil |
.resolve!(token, policy:, actor:) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/clickwrap/remediation_token.rb', line 40 def resolve!(token, policy:, actor:) attributes = verifier.verified(token.to_s, purpose: PURPOSE) raise RemediationInvalid, "The remediation token is missing, expired, or invalid." unless attributes unless attributes["schema"] == SCHEMA && attributes["policy"] == policy.key raise RemediationInvalid, "The remediation token belongs to a different policy." end unless secure_equal?(attributes["actor_reference"], Reference.actor(actor)) raise RemediationInvalid, "The remediation token belongs to a different actor." end # The tenant is CARRIED, not compared: the gate resolved it server-side # and signed it, and the engine's own routes have no ambient tenant to # compare against — a comparison here permanently 404'd every # remediation issued from a tenant-scoped page. The signature is the # authority; the resolved context hands the tenant back to # presentation and capture exactly like the subject. subject = resolve_record(attributes["subject"], "subject") represented_party = resolve_record(attributes["represented_party"], "represented_party") expected_fingerprint = SubjectFingerprint.for(policy, subject) unless secure_equal?(attributes["subject_fingerprint"], expected_fingerprint) raise RemediationInvalid, "The remediation subject changed after this route was issued. Return to the " \ "blocked action and start again against its current state." end Context.new( policy: policy, actor_reference: attributes["actor_reference"], tenant_reference: attributes["tenant_reference"], subject: subject, represented_party: represented_party, return_to: attributes["return_to"], attributes: attributes.freeze ) rescue ActiveSupport::MessageVerifier::InvalidSignature raise RemediationInvalid, "The remediation token is missing, expired, or invalid." end |