Class: Clickwrap::Capture
- Inherits:
-
Object
- Object
- Clickwrap::Capture
- Defined in:
- lib/clickwrap/capture.rb,
lib/clickwrap/capture/event_builder.rb,
lib/clickwrap/capture/presentation_verifier.rb
Overview
Turns a verified submission into an evidence event, and — when the caller asks for it — commits that event in the same database transaction as the action it authorizes.
That last part is the whole point of the gem, so it is worth being exact about what is promised.
For a same-database protected action, capture_and! joins the caller's
transaction. If the evidence write fails, the block's work rolls back with
it. If the block raises, the event rolls back with it. Neither side can
commit alone, and there is no rescue-and-continue path anywhere in here: an
account that exists without the evidence that authorized it is precisely the
failure this class exists to make impossible.
For anything crossing a system boundary — a payment provider, an identity
service, a remote signature — none of that applies, and pretending otherwise
would be worse than useless. Use Clickwrap.authorize_external_action! and
its outbox instead. This class will not claim atomicity it cannot deliver.
Defined Under Namespace
Classes: EventBuilder, PresentationVerifier
Instance Attribute Summary collapse
-
#actor ⇒ Object
readonly
Returns the value of attribute actor.
-
#capture_channel ⇒ Object
readonly
Returns the value of attribute capture_channel.
-
#http_request ⇒ Object
readonly
Returns the value of attribute http_request.
-
#policy ⇒ Object
readonly
Returns the value of attribute policy.
-
#subject ⇒ Object
readonly
Returns the value of attribute subject.
-
#submission ⇒ Object
readonly
Returns the value of attribute submission.
-
#tenant ⇒ Object
readonly
Returns the value of attribute tenant.
Instance Method Summary collapse
-
#capture! ⇒ Object
Records evidence with no protected action attached.
-
#capture_and!(&block) ⇒ Object
Records evidence and runs the protected action inside the same transaction.
-
#create_represented_party!(&block) ⇒ Object
Creates a new record of the type named by
represented_party:and binds the block's persisted result to the evidence before either can commit. -
#initialize(policy:, actor: nil, subject: nil, tenant: nil, http_request: nil, submission: nil, answers: nil, locale: nil, capture_channel: nil, acting_for: nil, authentication_context: nil, attribution_method: nil, idempotency_key: nil, prospective_actor: nil, registration_flow_id: nil, represented_party_creation_flow_id: nil, consume_one_time_authorizations: true, record_protected_outcome: true, reason: nil, event_type: "capture", root_event_id: nil, predecessor_event_id: nil, statement_action_overrides: {}) ⇒ Capture
constructor
A new instance of Capture.
-
#register!(&block) ⇒ Object
Signup.
Constructor Details
#initialize(policy:, actor: nil, subject: nil, tenant: nil, http_request: nil, submission: nil, answers: nil, locale: nil, capture_channel: nil, acting_for: nil, authentication_context: nil, attribution_method: nil, idempotency_key: nil, prospective_actor: nil, registration_flow_id: nil, represented_party_creation_flow_id: nil, consume_one_time_authorizations: true, record_protected_outcome: true, reason: nil, event_type: "capture", root_event_id: nil, predecessor_event_id: nil, statement_action_overrides: {}) ⇒ Capture
Returns a new instance of Capture.
35 36 37 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 64 65 66 67 68 69 70 |
# File 'lib/clickwrap/capture.rb', line 35 def initialize(policy:, actor: nil, subject: nil, tenant: nil, http_request: nil, submission: nil, answers: nil, locale: nil, capture_channel: nil, acting_for: nil, authentication_context: nil, attribution_method: nil, idempotency_key: nil, prospective_actor: nil, registration_flow_id: nil, represented_party_creation_flow_id: nil, consume_one_time_authorizations: true, record_protected_outcome: true, reason: nil, event_type: "capture", root_event_id: nil, predecessor_event_id: nil, statement_action_overrides: {}) @policy = policy @actor = actor @prospective_actor = prospective_actor @subject = subject @tenant = tenant @http_request = http_request @submission = submission @explicit_answers = answers @locale = locale @explicit_capture_channel = capture_channel&.to_s @capture_channel = @explicit_capture_channel @acting_for = acting_for @authentication_context = authentication_context @attribution_method = attribution_method @explicit_idempotency_key = idempotency_key @registration_flow_id = registration_flow_id @represented_party_creation_flow_id = represented_party_creation_flow_id @creating_represented_party = false @consume_one_time_authorizations = @record_protected_outcome = record_protected_outcome == true @reason = reason @event_type = event_type.to_s @root_event_id = root_event_id @predecessor_event_id = predecessor_event_id @statement_action_overrides = statement_action_overrides.to_h.transform_keys(&:to_s) end |
Instance Attribute Details
#actor ⇒ Object (readonly)
Returns the value of attribute actor.
72 73 74 |
# File 'lib/clickwrap/capture.rb', line 72 def actor @actor end |
#capture_channel ⇒ Object (readonly)
Returns the value of attribute capture_channel.
72 73 74 |
# File 'lib/clickwrap/capture.rb', line 72 def capture_channel @capture_channel end |
#http_request ⇒ Object (readonly)
Returns the value of attribute http_request.
72 73 74 |
# File 'lib/clickwrap/capture.rb', line 72 def http_request @http_request end |
#policy ⇒ Object (readonly)
Returns the value of attribute policy.
72 73 74 |
# File 'lib/clickwrap/capture.rb', line 72 def policy @policy end |
#subject ⇒ Object (readonly)
Returns the value of attribute subject.
72 73 74 |
# File 'lib/clickwrap/capture.rb', line 72 def subject @subject end |
#submission ⇒ Object (readonly)
Returns the value of attribute submission.
72 73 74 |
# File 'lib/clickwrap/capture.rb', line 72 def submission @submission end |
#tenant ⇒ Object (readonly)
Returns the value of attribute tenant.
72 73 74 |
# File 'lib/clickwrap/capture.rb', line 72 def tenant @tenant end |
Instance Method Details
#capture! ⇒ Object
Records evidence with no protected action attached.
75 76 77 |
# File 'lib/clickwrap/capture.rb', line 75 def capture! perform(protected_action: false) { |_pending| nil } end |
#capture_and!(&block) ⇒ Object
Records evidence and runs the protected action inside the same
transaction. The block receives a read-only PendingReceipt whose stable
event_id the domain row can reference; export and verification are
unavailable on it until commit, because until commit there is nothing to
export.
84 85 86 87 88 |
# File 'lib/clickwrap/capture.rb', line 84 def capture_and!(&block) raise ArgumentError, "capture_and! needs a block containing the protected action" unless block perform(protected_action: true, &block) end |
#create_represented_party!(&block) ⇒ Object
Creates a new record of the type named by represented_party: and binds
the block's persisted result to the evidence before either can commit.
Presentation records honestly
that membership authority was not yet verifiable; after the block saves
the record and creates its authority relationship, the adapter rereads
that relationship inside this same transaction and the finalized event
is rebound to the persisted represented party.
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 154 155 |
# File 'lib/clickwrap/capture.rb', line 122 def create_represented_party!(&block) unless block raise ArgumentError, "create_represented_party! needs a block that persists the represented party" end unless @acting_for.respond_to?(:new_record?) && @acting_for.new_record? raise RepresentedPartyCreationFailed, "create_represented_party! needs the exact new `represented_party:` record " \ "that was used to render the presentation." end @prospective_represented_party_class = @acting_for.class @creating_represented_party = true perform(protected_action: true) do |pending| result = block.call(pending) unless result.respond_to?(:persisted?) && result.persisted? raise RepresentedPartyCreationFailed, "The represented-party creation block must return the persisted represented party. " \ "Save it and its authority relationship inside the block, then return that record." end unless result.instance_of?(@prospective_represented_party_class) raise RepresentedPartyCreationFailed, "The represented-party creation block returned #{result.class.name}, but the " \ "presentation was bound to #{@verified_manifest.represented_party_type}. Return a " \ "persisted record of the presented type." end @acting_for = result result end end |
#register!(&block) ⇒ Object
Signup. At first render there is no persisted actor, so the presentation
bound itself to a short-lived registration flow instead of to a fictional
authenticated user. Here the account is created and its stable reference
bound to the evidence, both inside one transaction, and the receipt records
account_registration attribution rather than claiming a session that did
not exist.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/clickwrap/capture.rb', line 96 def register!(&block) raise ArgumentError, "register! needs a block that persists the account" unless block @attribution_method = "account_registration" perform(protected_action: true) do |pending| result = block.call(pending) unless @prospective_actor&.persisted? raise RegistrationFailed, "The registration block did not persist the prospective actor. Use `save!`, or " \ "raise when validation fails, so Clickwrap can roll the evidence back with it." end rebind_actor_after_registration!(pending) result end end |