clickwrap 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +91 -0
- data/README.md +23 -5
- data/guides/naming.md +1 -1
- data/guides/request-evidence.md +23 -2
- data/lib/clickwrap/configuration.rb +12 -8
- data/lib/clickwrap/dsl/policy_builder.rb +31 -6
- data/lib/clickwrap/request_evidence_policy.rb +5 -2
- data/lib/clickwrap/version.rb +1 -1
- data/lib/clickwrap/vocabulary.rb +9 -0
- data/lib/generators/clickwrap/install_generator.rb +15 -8
- data/lib/generators/clickwrap/templates/initializer.rb.erb +12 -5
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55f77c92e4ac5400a7c7fba781bd2a592875f3deef59fd8b3c1d6269e302ffc6
|
|
4
|
+
data.tar.gz: f15af7f288d8349bc8cd322b28b472e68bca750a8c17718648d7fa2db3877021
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fceb30a214fe8e4cdfcc19a775f8082fefe61424eba2e463958240dec07a0c8d099e1d3c6e5e0b9d67bd47eb11a445ee591f4b520c08f847479fbed277f197f2
|
|
7
|
+
data.tar.gz: e8d53673c7a5d9ba553078ace895a187e404b6a486525ee56d362f584e262f3bb9f9000287bf57311b22a40e2049140867f5eefdb300e945a7d69d25ff31dcf4
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,97 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.3.1] - 2026-08-20
|
|
10
|
+
|
|
11
|
+
### Changed — the rest of the collection friction, and the principle behind removing it
|
|
12
|
+
|
|
13
|
+
0.3.0 made *enabling* request evidence one switch and gave the initializer
|
|
14
|
+
defaults honest gem-supplied purposes. It left the friction standing
|
|
15
|
+
everywhere else: a policy-level `record_ip_address` still read as though it
|
|
16
|
+
wanted a sentence, `record_ip_geolocation` refused to do anything without a
|
|
17
|
+
field list, and the encryption escape hatch still demanded a reason be
|
|
18
|
+
phrased twice. This finishes the job the owner asked for.
|
|
19
|
+
|
|
20
|
+
The principle, in their words: **the host application and its privacy policy
|
|
21
|
+
own *why* data is collected; this gem records *what* was collected, honestly,
|
|
22
|
+
and is nobody's nanny.** Its job is evidence mechanics, not gatekeeping
|
|
23
|
+
collection. Everything below now works exactly as written:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
Clickwrap.configure do |config|
|
|
27
|
+
config.record_request_evidence_by_default = true
|
|
28
|
+
config.keep_recorded_ip_addresses_indefinitely!
|
|
29
|
+
config.deliberately_store_request_evidence_unencrypted!
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Clickwrap.policy :anything do
|
|
33
|
+
agree_to :terms
|
|
34
|
+
record_ip_address
|
|
35
|
+
record_browser_user_agent
|
|
36
|
+
record_ip_geolocation
|
|
37
|
+
end
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- **All three `record_` verbs take zero keyword arguments.** `because:`,
|
|
41
|
+
`legal_basis_reference:`, `data_protection_impact_assessment_reference:`,
|
|
42
|
+
`delete_after:`, `retain_until:`, and `encrypted:` are optional in fact and
|
|
43
|
+
not merely in the signature — nothing downstream refuses their absence.
|
|
44
|
+
(`legal_basis_reference` and the DPIA reference never were required anywhere
|
|
45
|
+
in the gem; a test now pins that they never become so.)
|
|
46
|
+
- **`record_ip_geolocation` with no field named records the coarse trio** —
|
|
47
|
+
country, region, city — the same set `record_request_evidence_by_default`
|
|
48
|
+
turns on, and nothing finer. The field keywords moved from `false` to `nil`
|
|
49
|
+
defaults so "did not mention this field" is distinguishable from "named it
|
|
50
|
+
and turned it off". Naming even one field means you are choosing the set
|
|
51
|
+
yourself, and the set is exactly what you named. Naming every field `false`
|
|
52
|
+
is still refused, because calling the verb and disabling everything cannot
|
|
53
|
+
mean anything; `do_not_record_ip_geolocation` is how to say that.
|
|
54
|
+
- **`keep_recorded_{ip_addresses,browser_user_agents,ip_geolocation}_indefinitely!`
|
|
55
|
+
take no arguments at all.**
|
|
56
|
+
- **`deliberately_store_request_evidence_unencrypted!` no longer needs a
|
|
57
|
+
`because:`.** The method NAME is the ceremony: `encrypt_recorded_* = false`
|
|
58
|
+
still cannot be reached without writing that line, and a reviewer still
|
|
59
|
+
finds it in the diff. When the host writes no reason the gem records
|
|
60
|
+
`Vocabulary::DEFAULT_REASON_FOR_STORING_REQUEST_EVIDENCE_UNENCRYPTED`.
|
|
61
|
+
Encryption itself is untouched — on by default for all three categories,
|
|
62
|
+
with a test pinning that the one switch does not weaken it.
|
|
63
|
+
- **The install generator stops refusing an incomplete category.**
|
|
64
|
+
`--record-ip-addresses-by-default` with no reason and no period writes the
|
|
65
|
+
file and simply omits those two lines, so the gem's own defaults apply.
|
|
66
|
+
- The `ReviewedText` placeholder check now only ever applies to text a host
|
|
67
|
+
actually supplied. Absence is never scaffolding.
|
|
68
|
+
|
|
69
|
+
### Unchanged, deliberately
|
|
70
|
+
|
|
71
|
+
- Reasons that are not about *collection* keep their required `because:`:
|
|
72
|
+
`delete_recorded_ip_address!` and its siblings, `dispose_core_event!`,
|
|
73
|
+
`place_on_legal_hold!` / `release_legal_hold!`, `plan_disposition_for`,
|
|
74
|
+
unredacted receipt export, and the lifecycle verbs. Those record a
|
|
75
|
+
destructive act, an access, or a state change — there the audit trail *is*
|
|
76
|
+
the reason, and there is no honest default for "why did somebody delete
|
|
77
|
+
this".
|
|
78
|
+
- Still refused, because each is the host contradicting themselves rather than
|
|
79
|
+
leaving a blank: scaffolding text the host actually wrote standing in for a
|
|
80
|
+
purpose (including in the installer, where a `TODO` in a shipped initializer
|
|
81
|
+
is worse than no line and the gem would reject it at boot anyway), a
|
|
82
|
+
negative deletion period passed to the installer, a deletion clock declared
|
|
83
|
+
alongside `keep_recorded_..._indefinitely!` for the same category, and
|
|
84
|
+
`record_ip_geolocation` with every field explicitly off.
|
|
85
|
+
- The gem's code default is still record-nothing. Claim boundaries, receipt
|
|
86
|
+
state labeling (`not_configured` / `unavailable` / `recorded` /
|
|
87
|
+
`deleted_after_retention`), and every released receipt format are untouched.
|
|
88
|
+
|
|
89
|
+
### Documentation
|
|
90
|
+
|
|
91
|
+
- README, `guides/request-evidence.md`, and `guides/naming.md` show the
|
|
92
|
+
zero-keyword forms as the ordinary way to write a policy, with purposes,
|
|
93
|
+
legal bases, and clocks as the upgrade path.
|
|
94
|
+
- `CLAUDE.md` / `AGENTS.md` rule 6 is rewritten around the principle above and
|
|
95
|
+
records the owner directive and its date. What it keeps as non-negotiable:
|
|
96
|
+
never a switch whose NAME hides what it collects, never an overclaim, never
|
|
97
|
+
a blurred receipt state, encryption on by default behind its named call,
|
|
98
|
+
host-supplied scaffolding rejected, and contradictions refused.
|
|
99
|
+
|
|
9
100
|
## [0.3.0] - 2026-08-20
|
|
10
101
|
|
|
11
102
|
### Changed — recording request evidence is one switch, not a checklist
|
data/README.md
CHANGED
|
@@ -733,7 +733,7 @@ end
|
|
|
733
733
|
|
|
734
734
|
That records, on every policy: the IP address the request arrived from, the browser user agent it sent, and a coarse country / region / city estimate for that address. Add [`trackdown`](https://github.com/rameerez/trackdown) 0.4+ to your Gemfile and the geolocation half resolves itself — Clickwrap picks up the official adapter with no wiring line, so trackdown plus Cloudflare genuinely is "bundle it and flip the switch".
|
|
735
735
|
|
|
736
|
-
Nothing else is required,
|
|
736
|
+
Nothing else is required — anywhere, at either level. **Your privacy policy owns the why; the gem records the what, honestly, and is nobody's nanny.** Clickwrap supplies its own defaults for every part you did not write:
|
|
737
737
|
|
|
738
738
|
- **Purpose.** Every recorded field carries one into the receipt. Yours if you wrote one, otherwise Clickwrap's: *"Corroborate who performed each recorded act, from where, on what client — to defend the recorded agreement itself."* The [privacy inventory](#operations) marks which of the two it is reading back (`"purpose_source": "gem_default"` vs `"host"`), so a gem sentence never passes for a decision your team reviewed.
|
|
739
739
|
- **How long.** No clock means it keeps pace with the evidence it corroborates — the same posture core evidence has had since 0.2.0. A corroboration scheduled to expire before the agreement it corroborates is a scheduled weakening of the record.
|
|
@@ -772,7 +772,23 @@ end
|
|
|
772
772
|
|
|
773
773
|
Two things are still refused, and both are you contradicting yourself rather than leaving a blank: scaffolding text (`"TODO: ask legal"`) standing in for a purpose, and a deletion clock set alongside `keep_recorded_..._indefinitely!` for the same category.
|
|
774
774
|
|
|
775
|
-
|
|
775
|
+
### Per policy, with as much or as little as you want
|
|
776
|
+
|
|
777
|
+
A single surface can name the fields itself instead of inheriting the default. The frictionless form takes no arguments at all:
|
|
778
|
+
|
|
779
|
+
```ruby
|
|
780
|
+
Clickwrap.policy :withdrawal_authorization do
|
|
781
|
+
authorize :regulated_action, one_time: true, valid_for: 10.minutes
|
|
782
|
+
|
|
783
|
+
record_ip_address
|
|
784
|
+
record_browser_user_agent
|
|
785
|
+
record_ip_geolocation
|
|
786
|
+
end
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
`record_ip_geolocation` with no field named records the same coarse trio as the switch — country, region, city. Name even one field and you are choosing the set yourself, and the set is exactly what you named.
|
|
790
|
+
|
|
791
|
+
The same declarations with the full record a reviewed team would want:
|
|
776
792
|
|
|
777
793
|
```ruby
|
|
778
794
|
Clickwrap.policy :regulated_authorization do
|
|
@@ -791,9 +807,11 @@ end
|
|
|
791
807
|
Recorded values live in a separately encrypted annex with their own retention, so
|
|
792
808
|
they can be deleted later without rewriting the core event payload. Core payloads
|
|
793
809
|
have their own reviewed disposition path and leave a digest-linked tombstone.
|
|
794
|
-
Encryption is on by default
|
|
795
|
-
`
|
|
796
|
-
|
|
810
|
+
Encryption is on by default for all three categories. Turning it off keeps its
|
|
811
|
+
own ceremony — you cannot reach `encrypt_recorded_ip_addresses = false` without
|
|
812
|
+
first writing `config.deliberately_store_request_evidence_unencrypted!` — but the
|
|
813
|
+
ceremony is the method name a reviewer finds in the diff, not a sentence the gem
|
|
814
|
+
makes you type. `because:` there is optional too.
|
|
797
815
|
|
|
798
816
|
For IP geolocation, [`trackdown`](https://github.com/rameerez/trackdown) 0.4 or newer is the official resolver, and Clickwrap uses it automatically when your bundle has it and you named no resolver of your own. Set it explicitly when you want a different provider per policy, or when you are wiring Trackdown's per-request CDN trust:
|
|
799
817
|
|
data/guides/naming.md
CHANGED
|
@@ -291,7 +291,7 @@ object.
|
|
|
291
291
|
| `chain_event_history_with`, `anchor_event_history_with`, `timestamp_receipts_with` | one `integrity_level` setting | Three different mechanisms making three different claims. One setting would let a reader infer the strongest from the presence of the weakest |
|
|
292
292
|
| `after_event_is_committed` | `after_commit` | Says which commit, and reads as a sentence |
|
|
293
293
|
| `authorize_unredacted_request_evidence_access_with` | `access_control` | Long, and correct. It names exactly which access it authorizes |
|
|
294
|
-
| `deliberately_store_request_evidence_unencrypted
|
|
294
|
+
| `deliberately_store_request_evidence_unencrypted!` | `encryption: false` | Turning encryption off should be a sentence a reviewer can find in a diff — not a `false`. The method NAME is the ceremony; its `because:` is optional |
|
|
295
295
|
|
|
296
296
|
The last row is the pattern worth copying. When an option has a consequence somebody should
|
|
297
297
|
have to think about, make the name carry the thinking.
|
data/guides/request-evidence.md
CHANGED
|
@@ -310,7 +310,26 @@ Clickwrap.policy :regulated_authorization do
|
|
|
310
310
|
end
|
|
311
311
|
```
|
|
312
312
|
|
|
313
|
-
Every keyword there is doing work
|
|
313
|
+
Every keyword there is doing work, and **every one of them is optional**. The same three
|
|
314
|
+
declarations with nothing at all supplied are valid, and record the same fields:
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
Clickwrap.policy :frictionless_regulated_authorization do
|
|
318
|
+
authorize :regulated_action, one_time: true, valid_for: 10.minutes
|
|
319
|
+
|
|
320
|
+
record_ip_address
|
|
321
|
+
record_browser_user_agent
|
|
322
|
+
record_ip_geolocation
|
|
323
|
+
retain_with :regulated_evidence
|
|
324
|
+
end
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
`record_ip_geolocation` with no field named records the coarse trio — country, region, city —
|
|
328
|
+
and nothing finer. Name even one field and the set is exactly what you named; name every field
|
|
329
|
+
`false` and Clickwrap refuses, because calling `record_ip_geolocation` and disabling everything
|
|
330
|
+
cannot mean anything (`do_not_record_ip_geolocation` is how to say that).
|
|
331
|
+
|
|
332
|
+
What each keyword adds when you do supply it:
|
|
314
333
|
|
|
315
334
|
- **`because:`** is the present purpose, in a sentence someone outside engineering can read. It
|
|
316
335
|
is stored and printed by `bin/rails clickwrap:privacy:inventory`. It is optional: a policy
|
|
@@ -319,7 +338,9 @@ Every keyword there is doing work:
|
|
|
319
338
|
reviewed. What is refused is scaffolding text — `"TODO: ask legal"` is not a purpose.
|
|
320
339
|
- **`legal_basis_reference:`** and **`data_protection_impact_assessment_reference:`** are
|
|
321
340
|
host-supplied pointers to your own reviewed documents. Clickwrap stores them. It does not
|
|
322
|
-
read them, validate them, or
|
|
341
|
+
read them, validate them, endorse them, or ever require them — nothing in the gem refuses a
|
|
342
|
+
recorded field for want of either, and nothing ever will. Your privacy policy owns the why;
|
|
343
|
+
the gem records the what.
|
|
323
344
|
- **`delete_after:`** and **`retain_until:`** are both optional. When neither the policy, its
|
|
324
345
|
retention class, nor the configuration names a schedule, the field keeps pace with the
|
|
325
346
|
evidence it corroborates — the same posture the core event has — and the annex is stamped
|
|
@@ -1227,15 +1227,19 @@ module Clickwrap
|
|
|
1227
1227
|
@keep_recorded_request_evidence_indefinitely[category.to_sym]
|
|
1228
1228
|
end
|
|
1229
1229
|
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1230
|
+
# The named escape hatch for turning encryption off. The ceremony is the
|
|
1231
|
+
# method: you cannot reach `encrypt_recorded_* = false` without writing a
|
|
1232
|
+
# line that says out loud what you are doing, and that line is what a
|
|
1233
|
+
# reviewer finds in a diff. Since 0.3.1 the `because:` is optional — the
|
|
1234
|
+
# gem records its own sentence when you do not write one — because the
|
|
1235
|
+
# host's privacy policy owns the why, and demanding it twice never stopped
|
|
1236
|
+
# anybody who had already typed this method name.
|
|
1237
|
+
#
|
|
1238
|
+
# Encryption itself is unchanged: on by default, for all three categories.
|
|
1239
|
+
def deliberately_store_request_evidence_unencrypted!(because: nil)
|
|
1237
1240
|
@deliberately_storing_request_evidence_unencrypted = true
|
|
1238
|
-
@reason_for_storing_request_evidence_unencrypted =
|
|
1241
|
+
@reason_for_storing_request_evidence_unencrypted =
|
|
1242
|
+
because.presence || Vocabulary::DEFAULT_REASON_FOR_STORING_REQUEST_EVIDENCE_UNENCRYPTED
|
|
1239
1243
|
end
|
|
1240
1244
|
|
|
1241
1245
|
def storing_request_evidence_unencrypted? = @deliberately_storing_request_evidence_unencrypted == true
|
|
@@ -239,24 +239,34 @@ module Clickwrap
|
|
|
239
239
|
@request_evidence[:browser_user_agent] = RequestEvidencePolicy::NOT_RECORDED
|
|
240
240
|
end
|
|
241
241
|
|
|
242
|
-
# Each IP-geolocation data field
|
|
243
|
-
# a separate decision about what to keep about a person's network
|
|
242
|
+
# Each IP-geolocation data field can be named separately, because each one
|
|
243
|
+
# is a separate decision about what to keep about a person's network
|
|
244
244
|
# context. `latitude_and_longitude` is one coupled choice: half a
|
|
245
245
|
# coordinate is not a result. Whatever is enabled, the provider name,
|
|
246
246
|
# source, estimated status, resolution time, and any accuracy or database
|
|
247
247
|
# provenance the resolver supplies are stored with it automatically — a
|
|
248
248
|
# policy cannot keep the coordinates and drop the uncertainty needed to
|
|
249
249
|
# read them.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
#
|
|
251
|
+
# Naming no field at all is the frictionless form:
|
|
252
|
+
#
|
|
253
|
+
# record_ip_geolocation
|
|
254
|
+
#
|
|
255
|
+
# It records the same coarse trio as
|
|
256
|
+
# `config.record_request_evidence_by_default` — country, region, city —
|
|
257
|
+
# and nothing finer, because that is what "IP geolocation" means in this
|
|
258
|
+
# gem when nobody narrows it. Naming even one field means you are
|
|
259
|
+
# choosing the set yourself, and then the set is exactly what you named.
|
|
260
|
+
def record_ip_geolocation(country: nil, region: nil, city: nil, postal_code: nil,
|
|
261
|
+
latitude_and_longitude: nil, timezone: nil, continent: nil,
|
|
262
|
+
metro_code: nil, accuracy_radius_in_kilometers: nil,
|
|
253
263
|
using: nil, encrypted: nil, delete_after: nil, retain_until: nil,
|
|
254
264
|
fail_if_unavailable: false, because: nil,
|
|
255
265
|
legal_basis_reference: nil,
|
|
256
266
|
data_protection_impact_assessment_reference: nil,
|
|
257
267
|
**unknown_options)
|
|
258
268
|
refuse_unknown_options!("record_ip_geolocation", unknown_options)
|
|
259
|
-
|
|
269
|
+
named = {
|
|
260
270
|
"country" => country,
|
|
261
271
|
"region" => region,
|
|
262
272
|
"city" => city,
|
|
@@ -267,6 +277,7 @@ module Clickwrap
|
|
|
267
277
|
"metro_code" => metro_code,
|
|
268
278
|
"accuracy_radius_in_kilometers" => accuracy_radius_in_kilometers
|
|
269
279
|
}
|
|
280
|
+
@ip_geolocation_fields = default_ip_geolocation_fields_when_none_named(named)
|
|
270
281
|
@ip_geolocation_resolver_name = using
|
|
271
282
|
|
|
272
283
|
@request_evidence[:ip_geolocation] = RequestEvidencePolicy::Setting.new(
|
|
@@ -404,6 +415,20 @@ module Clickwrap
|
|
|
404
415
|
end
|
|
405
416
|
end
|
|
406
417
|
|
|
418
|
+
# `nil` means "the policy did not mention this field"; `false` means "the
|
|
419
|
+
# policy named it and turned it off". The distinction is the whole reason
|
|
420
|
+
# the keywords default to nil: a policy that mentions nothing gets the
|
|
421
|
+
# coarse trio, and a policy that explicitly sets every field to false
|
|
422
|
+
# still reaches the coherence check that tells it to say
|
|
423
|
+
# `do_not_record_ip_geolocation` instead.
|
|
424
|
+
def default_ip_geolocation_fields_when_none_named(named)
|
|
425
|
+
return named.transform_values { |value| value == true } if named.any? { |_, value| !value.nil? }
|
|
426
|
+
|
|
427
|
+
Vocabulary::IP_GEOLOCATION_DATA_FIELDS.to_h do |field|
|
|
428
|
+
[field, Vocabulary::COARSE_IP_GEOLOCATION_DATA_FIELDS.include?(field)]
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
407
432
|
def resolved_ip_geolocation_fields(setting)
|
|
408
433
|
return {} unless setting.record?
|
|
409
434
|
return @ip_geolocation_fields if @request_evidence.key?(:ip_geolocation)
|
|
@@ -241,8 +241,11 @@ module Clickwrap
|
|
|
241
241
|
|
|
242
242
|
if ip_geolocation.record? && enabled.empty?
|
|
243
243
|
raise DefinitionError,
|
|
244
|
-
"Policy #{policy_key} calls `record_ip_geolocation`
|
|
245
|
-
"Name the fields you
|
|
244
|
+
"Policy #{policy_key} calls `record_ip_geolocation` and then turns every field " \
|
|
245
|
+
"off, which cannot mean anything. Name the fields you want, for example " \
|
|
246
|
+
"`country: true`; call `record_ip_geolocation` with no fields at all for the " \
|
|
247
|
+
"coarse country, region, and city; or say `do_not_record_ip_geolocation` if that " \
|
|
248
|
+
"is what you meant."
|
|
246
249
|
end
|
|
247
250
|
|
|
248
251
|
if ip_geolocation.record? &&
|
data/lib/clickwrap/version.rb
CHANGED
data/lib/clickwrap/vocabulary.rb
CHANGED
|
@@ -187,6 +187,15 @@ module Clickwrap
|
|
|
187
187
|
DEFAULT_REASON_FOR_KEEPING_REQUEST_EVIDENCE_INDEFINITELY =
|
|
188
188
|
"Corroboration lives as long as the evidence it corroborates"
|
|
189
189
|
|
|
190
|
+
# The reason recorded when a host turns off encryption for request evidence
|
|
191
|
+
# without writing their own. Calling the method is still the ceremony —
|
|
192
|
+
# `deliberately_store_request_evidence_unencrypted!` is a sentence a
|
|
193
|
+
# reviewer finds in a diff and cannot misread — but the gem no longer
|
|
194
|
+
# demands the sentence be phrased twice.
|
|
195
|
+
DEFAULT_REASON_FOR_STORING_REQUEST_EVIDENCE_UNENCRYPTED =
|
|
196
|
+
"The application deliberately stores request evidence unencrypted; the reason lives " \
|
|
197
|
+
"outside Clickwrap"
|
|
198
|
+
|
|
190
199
|
# Provenance that travels with any stored IP-geolocation result. A policy
|
|
191
200
|
# cannot keep provider-derived coordinates while stripping the uncertainty
|
|
192
201
|
# needed to interpret them.
|
|
@@ -1160,23 +1160,30 @@ module Clickwrap
|
|
|
1160
1160
|
validate_ip_geolocation_resolver_class_name!
|
|
1161
1161
|
end
|
|
1162
1162
|
|
|
1163
|
+
# A missing purpose and a missing deletion period are both fine, and the
|
|
1164
|
+
# generated file simply omits those lines: the gem records its own stated
|
|
1165
|
+
# purpose and keeps the field as long as the evidence it corroborates.
|
|
1166
|
+
# Scaffolding text is the one thing still refused, because a `TODO` the
|
|
1167
|
+
# installer writes into a shipped initializer is worse than no line at
|
|
1168
|
+
# all — the gem would reject it at boot anyway.
|
|
1163
1169
|
def validate_enabled_category!(label, enabled:, because:, delete_after_days:,
|
|
1164
1170
|
reason_option:, retention_option:)
|
|
1165
1171
|
return unless enabled
|
|
1166
1172
|
|
|
1167
|
-
|
|
1173
|
+
if Clickwrap::ReviewedText.placeholder?(because)
|
|
1168
1174
|
raise Thor::Error,
|
|
1169
|
-
"Clickwrap cannot enable #{label} with a
|
|
1170
|
-
"Give the application's reviewed, present-tense reason
|
|
1171
|
-
"#{reason_option}=\"...\", or
|
|
1175
|
+
"Clickwrap cannot enable #{label} with a scaffolding reason " \
|
|
1176
|
+
"(#{because.inspect}). Give the application's reviewed, present-tense reason " \
|
|
1177
|
+
"with #{reason_option}=\"...\", or omit it entirely and let Clickwrap record " \
|
|
1178
|
+
"its own stated purpose. No files were written."
|
|
1172
1179
|
end
|
|
1173
1180
|
|
|
1174
|
-
return
|
|
1181
|
+
return unless delete_after_days.negative?
|
|
1175
1182
|
|
|
1176
1183
|
raise Thor::Error,
|
|
1177
|
-
"
|
|
1178
|
-
"
|
|
1179
|
-
"
|
|
1184
|
+
"#{retention_option} cannot be negative (got #{delete_after_days}). Give the " \
|
|
1185
|
+
"number of days your application reviewed, or omit it and #{label} will keep " \
|
|
1186
|
+
"pace with the evidence it corroborates. No files were written."
|
|
1180
1187
|
end
|
|
1181
1188
|
|
|
1182
1189
|
def validate_ip_geolocation_coordinates!
|
|
@@ -287,19 +287,25 @@ Clickwrap.configure do |config|
|
|
|
287
287
|
# event — the historical agreement, declaration, or authorization stays intact
|
|
288
288
|
# and verifiable without it.
|
|
289
289
|
|
|
290
|
-
<%- if record_ip_addresses? -%>
|
|
290
|
+
<%- if record_ip_addresses? && reason_for_recording_ip_addresses.present? -%>
|
|
291
291
|
config.reason_for_recording_ip_addresses_by_default =
|
|
292
292
|
<%= reason_for_recording_ip_addresses.inspect %>
|
|
293
|
+
<%- end -%>
|
|
294
|
+
<%- if record_ip_addresses? && delete_recorded_ip_addresses_after_days.positive? -%>
|
|
293
295
|
config.delete_recorded_ip_addresses_after = <%= delete_recorded_ip_addresses_after_days %>.days
|
|
294
296
|
<%- end -%>
|
|
295
|
-
<%- if record_browser_user_agents? -%>
|
|
297
|
+
<%- if record_browser_user_agents? && reason_for_recording_browser_user_agents.present? -%>
|
|
296
298
|
config.reason_for_recording_browser_user_agents_by_default =
|
|
297
299
|
<%= reason_for_recording_browser_user_agents.inspect %>
|
|
300
|
+
<%- end -%>
|
|
301
|
+
<%- if record_browser_user_agents? && delete_recorded_browser_user_agents_after_days.positive? -%>
|
|
298
302
|
config.delete_recorded_browser_user_agents_after = <%= delete_recorded_browser_user_agents_after_days %>.days
|
|
299
303
|
<%- end -%>
|
|
300
|
-
<%- if any_ip_geolocation_field? -%>
|
|
304
|
+
<%- if any_ip_geolocation_field? && reason_for_recording_ip_geolocation.present? -%>
|
|
301
305
|
config.reason_for_recording_ip_geolocation_by_default =
|
|
302
306
|
<%= reason_for_recording_ip_geolocation.inspect %>
|
|
307
|
+
<%- end -%>
|
|
308
|
+
<%- if any_ip_geolocation_field? && delete_recorded_ip_geolocation_after_days.positive? -%>
|
|
303
309
|
config.delete_recorded_ip_geolocation_after = <%= delete_recorded_ip_geolocation_after_days %>.days
|
|
304
310
|
<%- end -%>
|
|
305
311
|
<%- if records_any_request_evidence? -%>
|
|
@@ -332,8 +338,9 @@ Clickwrap.configure do |config|
|
|
|
332
338
|
# Turning one of these off puts the raw value in plain text in your database,
|
|
333
339
|
# and therefore in every ordinary backup and database dump. It is allowed,
|
|
334
340
|
# because some applications have a reviewed reason, but it is never a quiet
|
|
335
|
-
# one-character change: Clickwrap refuses `false` until you have
|
|
336
|
-
# `config.deliberately_store_request_evidence_unencrypted
|
|
341
|
+
# one-character change: Clickwrap refuses `false` until you have written
|
|
342
|
+
# `config.deliberately_store_request_evidence_unencrypted!` above it. Writing
|
|
343
|
+
# that line IS the whole ceremony — its `because:` is optional.
|
|
337
344
|
#
|
|
338
345
|
# config.encrypt_recorded_ip_addresses = true
|
|
339
346
|
# config.encrypt_recorded_browser_user_agents = true
|