Class: Clickwrap::DSL::PolicyBuilder
- Inherits:
-
Object
- Object
- Clickwrap::DSL::PolicyBuilder
- Defined in:
- lib/clickwrap/dsl/policy_builder.rb
Overview
The block passed to Clickwrap.policy.
Clickwrap.policy :signup do
agree_to :terms
acknowledge :privacy_notice
retain_with :ordinary_agreement_evidence
end
The verbs are deliberate. agree_to :terms and consent_to :marketing
are different sentences because they are different acts with different
lifecycles, and a developer choosing between them is doing the one piece
of thinking this gem cannot do for them.
Constant Summary collapse
- STATEMENT_OPTIONS =
%i[ document statement label link_label choices purpose withdrawal_path valid_for requires subject_fingerprint_with subject_fingerprint_version record_protected_outcome_with protected_outcome_version optional require_an_explicit_choice one_time require_current_version ].freeze
- DEFAULT_RETENTION_SETTING_NAMES =
{ ip_address: :delete_recorded_ip_addresses_after, browser_user_agent: :delete_recorded_browser_user_agents_after, ip_geolocation: :delete_recorded_ip_geolocation_after }.freeze
Instance Method Summary collapse
-
#acknowledge(statement_key, **options) ⇒ Object
Affirmative receipt or awareness of a notice or risk.
-
#agree_to(statement_key, **options) ⇒ Object
Assent to contractual terms.
-
#attest(statement_key, **options) ⇒ Object
An operational fact affirmed by an authorized actor, usually an operator rather than an end user.
-
#authorize(statement_key, **options) ⇒ Object
Narrow permission bound to one protected action.
-
#compile ⇒ Object
--- Compilation ----------------------------------------------------------.
-
#consent_to(statement_key, **options) ⇒ Object
Purpose-specific permission, where the host has decided consent is the right basis for this processing.
-
#declare(statement_key, **options) ⇒ Object
A factual statement made by the actor.
- #do_not_record_browser_user_agent ⇒ Object
-
#do_not_record_ip_address ⇒ Object
A policy-level refusal wins over an application-wide default.
- #do_not_record_ip_geolocation ⇒ Object
-
#initialize(key) ⇒ PolicyBuilder
constructor
A new instance of PolicyBuilder.
-
#only_capture_from(*channels) ⇒ Object
Restrict which capture channels this policy accepts.
-
#only_present_in(*locales) ⇒ Object
Restrict the policy to locales it can actually present.
-
#permit_acting_for(*represented_party_types, using: :host, when_actor_is_at_least: nil, when_actor_has_permission: nil, including_when_this_action_creates_the_represented_party: false, **unknown_options) ⇒ Object
Allow an actor to act for a represented party (an employee signing for an organization, a guardian, a service account).
-
#permit_acting_for_organization(when_actor_is_at_least: nil, when_actor_has_permission: nil, including_when_this_action_creates_the_organization: false, **unknown_options) ⇒ Object
One-line integration with https://github.com/rameerez/organizations.
-
#permit_exemptions(because: nil, **unknown_options) ⇒ Object
Allow explicitly recorded system exemptions for this policy.
-
#persist_presentations_before_submission_for(duration, because: nil, **unknown_options) ⇒ Object
Keep the presentation manifest for renders that were never submitted.
- #record_browser_user_agent(encrypted: nil, delete_after: nil, retain_until: nil, fail_if_unavailable: false, because: nil, legal_basis_reference: nil, **unknown_options) ⇒ Object
-
#record_ip_address(encrypted: nil, delete_after: nil, retain_until: nil, fail_if_unavailable: false, because: nil, legal_basis_reference: nil, **unknown_options) ⇒ Object
--- Request evidence -----------------------------------------------------.
-
#record_ip_geolocation(country: false, region: false, city: false, postal_code: false, latitude_and_longitude: false, timezone: false, continent: false, metro_code: false, accuracy_radius_in_kilometers: false, using: nil, encrypted: nil, delete_after: nil, retain_until: nil, fail_if_unavailable: false, because: nil, legal_basis_reference: nil, data_protection_impact_assessment_reference: nil, **unknown_options) ⇒ Object
Each IP-geolocation data field is named separately, because each one is a separate decision about what to keep about a person's network context.
-
#retain_with(retention_class_key) ⇒ Object
--- Policy-level settings ------------------------------------------------.
-
#review_request_evidence_configuration_on(date) ⇒ Object
A date by which someone should look at this policy's request-evidence configuration again.
-
#tenant_is(scope) ⇒ Object
Declares whether this policy is personal, tenant-bound, or deliberately usable in either context.
Constructor Details
#initialize(key) ⇒ PolicyBuilder
Returns a new instance of PolicyBuilder.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 43 def initialize(key) @key = key.to_s @statements = [] @retention_class_key = nil @request_evidence = {} @ip_geolocation_fields = {} @persist_presentations_for = nil @persist_presentations_because = nil @capture_channels = nil @locales = nil @tenant_scope = "optional" @options = {} @authority_rule = nil @ip_geolocation_resolver_name = nil @review_request_evidence_configuration_on = nil end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *_arguments, **_options) ⇒ Object (private)
453 454 455 456 457 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 453 def method_missing(name, *_arguments, **) raise DefinitionError, "Policy #{@key} calls unknown DSL method `#{name}`. Check the spelling; " \ "Clickwrap never ignores policy declarations." end |
Instance Method Details
#acknowledge(statement_key, **options) ⇒ Object
Affirmative receipt or awareness of a notice or risk. This is not permission, and it is not consent: a privacy notice is information the person is entitled to, not something they agree to.
70 71 72 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 70 def acknowledge(statement_key, **) add_statement(statement_key, "acknowledgment", ) end |
#agree_to(statement_key, **options) ⇒ Object
Assent to contractual terms.
63 64 65 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 63 def agree_to(statement_key, **) add_statement(statement_key, "agreement", ) end |
#attest(statement_key, **options) ⇒ Object
An operational fact affirmed by an authorized actor, usually an operator rather than an end user.
89 90 91 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 89 def attest(statement_key, **) add_statement(statement_key, "attestation", ) end |
#authorize(statement_key, **options) ⇒ Object
Narrow permission bound to one protected action. This is the difference between "the user once accepted something" and "this exact evidence authorized this exact operation".
96 97 98 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 96 def (statement_key, **) add_statement(statement_key, "authorization", ) end |
#compile ⇒ Object
--- Compilation ----------------------------------------------------------
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 293 def compile Policy.new( key: @key, statements: @statements, retention_class_key: @retention_class_key, request_evidence: build_request_evidence_policy, persist_presentations_for: @persist_presentations_for, persist_presentations_because: @persist_presentations_because, capture_channels: @capture_channels, locales: @locales, tenant_scope: @tenant_scope, authority_rule: @authority_rule, options: @options ) end |
#consent_to(statement_key, **options) ⇒ Object
Purpose-specific permission, where the host has decided consent is the right basis for this processing. Clickwrap does not make that decision; it makes the grant, the withdrawal, and the scope demonstrable.
77 78 79 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 77 def (statement_key, **) add_statement(statement_key, "consent", ) end |
#declare(statement_key, **options) ⇒ Object
A factual statement made by the actor. A declaration can expire without implying it was false when it was made.
83 84 85 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 83 def declare(statement_key, **) add_statement(statement_key, "declaration", ) end |
#do_not_record_browser_user_agent ⇒ Object
238 239 240 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 238 def do_not_record_browser_user_agent @request_evidence[:browser_user_agent] = RequestEvidencePolicy::NOT_RECORDED end |
#do_not_record_ip_address ⇒ Object
A policy-level refusal wins over an application-wide default. This is a
named method rather than record: false: a privacy-reducing decision
should read unambiguously in review and must not be confused with an
omitted option.
224 225 226 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 224 def do_not_record_ip_address @request_evidence[:ip_address] = RequestEvidencePolicy::NOT_RECORDED end |
#do_not_record_ip_geolocation ⇒ Object
278 279 280 281 282 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 278 def do_not_record_ip_geolocation @request_evidence[:ip_geolocation] = RequestEvidencePolicy::NOT_RECORDED @ip_geolocation_fields = {} @ip_geolocation_resolver_name = nil end |
#only_capture_from(*channels) ⇒ Object
Restrict which capture channels this policy accepts. By default all are allowed and the channel is recorded; a policy that should only ever be completed in a browser can say so.
120 121 122 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 120 def only_capture_from(*channels) @capture_channels = channels.flatten.map(&:to_s) end |
#only_present_in(*locales) ⇒ Object
Restrict the policy to locales it can actually present. A required legal statement should not fall back to a language nobody chose.
126 127 128 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 126 def only_present_in(*locales) @locales = locales.flatten.map(&:to_s) end |
#permit_acting_for(*represented_party_types, using: :host, when_actor_is_at_least: nil, when_actor_has_permission: nil, including_when_this_action_creates_the_represented_party: false, **unknown_options) ⇒ Object
Allow an actor to act for a represented party (an employee signing for an organization, a guardian, a service account). The receipt keeps the authenticated principal, the asserted actor, and the represented party as three separate facts. Clickwrap does not decide whether the authority is sufficient.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 157 def permit_acting_for(*represented_party_types, using: :host, when_actor_is_at_least: nil, when_actor_has_permission: nil, including_when_this_action_creates_the_represented_party: false, **) ("permit_acting_for", ) if represented_party_types.compact.all? { |type| type.to_s.strip.empty? } raise DefinitionError, "permit_acting_for needs at least one represented-party class name. " \ "Name every type this policy permits so it cannot authorize an unexpected kind of record." end @options[:permit_acting_for] = true @authority_rule = AuthorityRule.new( represented_party_types: represented_party_types, adapter_name: using, minimum_role: when_actor_is_at_least, required_permission: , allow_represented_party_creation: including_when_this_action_creates_the_represented_party ) end |
#permit_acting_for_organization(when_actor_is_at_least: nil, when_actor_has_permission: nil, including_when_this_action_creates_the_organization: false, **unknown_options) ⇒ Object
One-line integration with https://github.com/rameerez/organizations. The User remains the human actor; Organizations::Organization is the represented party. A policy must name at least one reviewed authority criterion rather than silently treating every member as able to bind it.
permit_acting_for_organization when_actor_is_at_least: :admin
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 186 def permit_acting_for_organization(when_actor_is_at_least: nil, when_actor_has_permission: nil, including_when_this_action_creates_the_organization: false, **) ("permit_acting_for_organization", ) if when_actor_is_at_least.nil? && .nil? raise DefinitionError, "permit_acting_for_organization needs `when_actor_is_at_least:` or " \ "`when_actor_has_permission:`. Organization membership alone does not establish " \ "legal authority to bind the organization." end permit_acting_for( "Organizations::Organization", using: :organizations_membership, when_actor_is_at_least:, when_actor_has_permission:, including_when_this_action_creates_the_represented_party: including_when_this_action_creates_the_organization ) end |
#permit_exemptions(because: nil, **unknown_options) ⇒ Object
Allow explicitly recorded system exemptions for this policy. Even when
allowed, an exemption never answers agreed_to? — it answers
exempted_from?. There is no "missing checkbox means system account"
inference anywhere in this gem.
146 147 148 149 150 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 146 def permit_exemptions(because: nil, **) ("permit_exemptions", ) @options[:permit_exemptions] = true @options[:permit_exemptions_because] = because end |
#persist_presentations_before_submission_for(duration, because: nil, **unknown_options) ⇒ Object
Keep the presentation manifest for renders that were never submitted.
The default path writes nothing on GET; this trades that for a record
of display attempts, which some high-assurance flows want. An abandoned
GET is labeled presented_by_server — never accepted, and never
seen_by_human.
111 112 113 114 115 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 111 def persist_presentations_before_submission_for(duration, because: nil, **) ("persist_presentations_before_submission_for", ) @persist_presentations_for = duration @persist_presentations_because = because end |
#record_browser_user_agent(encrypted: nil, delete_after: nil, retain_until: nil, fail_if_unavailable: false, because: nil, legal_basis_reference: nil, **unknown_options) ⇒ Object
228 229 230 231 232 233 234 235 236 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 228 def record_browser_user_agent(encrypted: nil, delete_after: nil, retain_until: nil, fail_if_unavailable: false, because: nil, legal_basis_reference: nil, **) ("record_browser_user_agent", ) @request_evidence[:browser_user_agent] = RequestEvidencePolicy::Setting.new( record: true, encrypted:, delete_after:, retain_until:, fail_if_unavailable:, because:, legal_basis_reference: ) end |
#record_ip_address(encrypted: nil, delete_after: nil, retain_until: nil, fail_if_unavailable: false, because: nil, legal_basis_reference: nil, **unknown_options) ⇒ Object
--- Request evidence -----------------------------------------------------
210 211 212 213 214 215 216 217 218 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 210 def record_ip_address(encrypted: nil, delete_after: nil, retain_until: nil, fail_if_unavailable: false, because: nil, legal_basis_reference: nil, **) ("record_ip_address", ) @request_evidence[:ip_address] = RequestEvidencePolicy::Setting.new( record: true, encrypted:, delete_after:, retain_until:, fail_if_unavailable:, because:, legal_basis_reference: ) end |
#record_ip_geolocation(country: false, region: false, city: false, postal_code: false, latitude_and_longitude: false, timezone: false, continent: false, metro_code: false, accuracy_radius_in_kilometers: false, using: nil, encrypted: nil, delete_after: nil, retain_until: nil, fail_if_unavailable: false, because: nil, legal_basis_reference: nil, data_protection_impact_assessment_reference: nil, **unknown_options) ⇒ Object
Each IP-geolocation data field is named separately, because each one is
a separate decision about what to keep about a person's network
context. latitude_and_longitude is one coupled choice: half a
coordinate is not a result. Whatever is enabled, the provider name,
source, estimated status, resolution time, and any accuracy or database
provenance the resolver supplies are stored with it automatically — a
policy cannot keep the coordinates and drop the uncertainty needed to
read them.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 250 def record_ip_geolocation(country: false, region: false, city: false, postal_code: false, latitude_and_longitude: false, timezone: false, continent: false, metro_code: false, accuracy_radius_in_kilometers: false, using: nil, encrypted: nil, delete_after: nil, retain_until: nil, fail_if_unavailable: false, because: nil, legal_basis_reference: nil, data_protection_impact_assessment_reference: nil, **) ("record_ip_geolocation", ) @ip_geolocation_fields = { "country" => country, "region" => region, "city" => city, "postal_code" => postal_code, "latitude_and_longitude" => latitude_and_longitude, "timezone" => timezone, "continent" => continent, "metro_code" => metro_code, "accuracy_radius_in_kilometers" => accuracy_radius_in_kilometers } @ip_geolocation_resolver_name = using @request_evidence[:ip_geolocation] = RequestEvidencePolicy::Setting.new( record: true, encrypted:, delete_after:, retain_until:, fail_if_unavailable:, because:, legal_basis_reference:, data_protection_impact_assessment_reference: ) end |
#retain_with(retention_class_key) ⇒ Object
--- Policy-level settings ------------------------------------------------
102 103 104 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 102 def retain_with(retention_class_key) @retention_class_key = retention_class_key.to_s end |
#review_request_evidence_configuration_on(date) ⇒ Object
A date by which someone should look at this policy's request-evidence
configuration again. clickwrap:doctor reports policies that collect
personal data without one, and policies whose date has passed.
287 288 289 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 287 def review_request_evidence_configuration_on(date) @review_request_evidence_configuration_on = date end |
#tenant_is(scope) ⇒ Object
Declares whether this policy is personal, tenant-bound, or deliberately usable in either context. The same policy-level decision is applied to presentation, capture, and verification, so ambient organization state cannot appear on only one side of a signed submission.
tenant_is :not_applicable # personal evidence; ignore ambient tenant
tenant_is :required # every call must resolve a tenant
tenant_is :optional # either context is deliberate (the default)
138 139 140 |
# File 'lib/clickwrap/dsl/policy_builder.rb', line 138 def tenant_is(scope) @tenant_scope = scope.to_s end |