agreements 0.1.0 → 0.2.0
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 +5 -0
- data/README.md +22 -7
- data/app/models/agreements/acceptance.rb +1 -1
- data/lib/agreements/recorder.rb +6 -4
- data/lib/agreements/version.rb +1 -1
- data/lib/agreements.rb +10 -4
- data/lib/generators/agreements/install/templates/create_agreements_tables.rb.tt +2 -0
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbb4e3cb21a8e743ea9fab48f117242cad49d78e752258bbb992894498355be4
|
|
4
|
+
data.tar.gz: f2160a49ff3799851018060c773643c9b829bad4a1d3e22944913cef62dae501
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 70299f73fe7bc2b6760aae7b1389fa3913298175554ac001963270372c97af651bec611613ae51d367a320dd6d785b154517cccc1c468eece83f7b25e3c0212c
|
|
7
|
+
data.tar.gz: 7a27e58cf17b1068ecee2705f510f38f6900829eb77c5177b8a77237cb19be6e581b728f8a2eba56f973e8850977fa72e612af28dbdbafd1d787d11d86ad2e60
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 - 2026-08-27
|
|
4
|
+
|
|
5
|
+
- Record the exact localized acceptance statement and locale shown by the host.
|
|
6
|
+
- Publish releases through RubyGems trusted publishing.
|
|
7
|
+
|
|
3
8
|
## 0.1.0 - 2026-08-25
|
|
4
9
|
|
|
5
10
|
- Extract the agreement-version and acceptance primitives proven in EthicsPortal.
|
data/README.md
CHANGED
|
@@ -4,8 +4,8 @@ Auditable acceptance of externally hosted legal agreements for Rails.
|
|
|
4
4
|
|
|
5
5
|
`agreements` answers one durable question:
|
|
6
6
|
|
|
7
|
-
> Which agreement version did this subject accept,
|
|
8
|
-
> acceptance, under what authority, and when?
|
|
7
|
+
> Which agreement version and localized statement did this subject accept,
|
|
8
|
+
> who performed the acceptance, under what authority, and when?
|
|
9
9
|
|
|
10
10
|
It deliberately does not host legal documents or provide a legal CMS. Your
|
|
11
11
|
legal or marketing site remains authoritative; the gem stores immutable
|
|
@@ -29,6 +29,13 @@ view, initializer, JavaScript, CSS, or admin dashboard to integrate.
|
|
|
29
29
|
|
|
30
30
|
Ruby >= 3.2 · Rails >= 7.1 and < 9 · PostgreSQL and SQLite
|
|
31
31
|
|
|
32
|
+
### Upgrading from 0.1
|
|
33
|
+
|
|
34
|
+
Version 0.2 adds non-null `acceptance_statement` and `locale` columns to
|
|
35
|
+
`agreements_acceptances`. Add them in a host migration before upgrading. For
|
|
36
|
+
existing evidence, copy each version's canonical statement and use an honest
|
|
37
|
+
locale such as `und` when the displayed locale is unknown.
|
|
38
|
+
|
|
32
39
|
## Define a version
|
|
33
40
|
|
|
34
41
|
Agreement versions are deployed data. Create them in a data migration and keep
|
|
@@ -79,19 +86,24 @@ application. Submit the exact displayed version as a hidden field:
|
|
|
79
86
|
Then record only that version while it remains current:
|
|
80
87
|
|
|
81
88
|
```ruby
|
|
89
|
+
statement = I18n.t("agreements.user_terms.statement")
|
|
90
|
+
|
|
82
91
|
acceptance = Agreements.accept!(
|
|
83
92
|
"user_terms",
|
|
84
93
|
version_id: params.dig(:acceptance, :agreement_version_id),
|
|
85
94
|
subject: current_user,
|
|
86
95
|
actor: current_user,
|
|
87
|
-
authority: "self"
|
|
96
|
+
authority: "self",
|
|
97
|
+
acceptance_statement: statement,
|
|
98
|
+
locale: I18n.locale.to_s
|
|
88
99
|
)
|
|
89
100
|
```
|
|
90
101
|
|
|
91
102
|
`Agreements.accept!` resolves the current version server-side, requires the
|
|
92
103
|
submitted ID to match it, derives opaque keys from the server-owned subject and
|
|
93
|
-
actor, and records
|
|
94
|
-
|
|
104
|
+
actor, and records the exact localized plain-text statement selected by the
|
|
105
|
+
host. Do not accept either value from browser parameters. Retries, double
|
|
106
|
+
clicks, and concurrent submissions return the original acceptance evidence.
|
|
95
107
|
|
|
96
108
|
A missing, malformed, wrong-agreement, or stale ID raises
|
|
97
109
|
`Agreements::VersionNotCurrent`. Its `current_version` is ready to render:
|
|
@@ -112,7 +124,9 @@ Agreements.accept!(
|
|
|
112
124
|
version_id: params.dig(:acceptance, :agreement_version_id),
|
|
113
125
|
subject: current_organization,
|
|
114
126
|
actor: current_user,
|
|
115
|
-
authority: "organization_owner"
|
|
127
|
+
authority: "organization_owner",
|
|
128
|
+
acceptance_statement: I18n.t("agreements.organization_dpa.statement"),
|
|
129
|
+
locale: I18n.locale.to_s
|
|
116
130
|
)
|
|
117
131
|
```
|
|
118
132
|
|
|
@@ -168,6 +182,7 @@ the response contract your application supports.
|
|
|
168
182
|
- agreement-version foreign key;
|
|
169
183
|
- opaque subject and actor keys;
|
|
170
184
|
- authority;
|
|
185
|
+
- exact localized acceptance statement and locale;
|
|
171
186
|
- server acceptance time;
|
|
172
187
|
- timestamps.
|
|
173
188
|
|
|
@@ -201,7 +216,7 @@ registry alone does not deploy a new version to an existing installation.
|
|
|
201
216
|
- Draft/publish/activation workflows or an admin dashboard.
|
|
202
217
|
- Electronic signatures or identity proofing.
|
|
203
218
|
- IP addresses, user agents, fingerprints, geolocation, or request provenance.
|
|
204
|
-
- Presentation manifests, snapshots, scroll tracking, or one-time nonces.
|
|
219
|
+
- Presentation manifests, browser snapshots, scroll tracking, or one-time nonces.
|
|
205
220
|
- Consent withdrawal, declarations, attestations, authorizations, retention,
|
|
206
221
|
legal holds, integrity chains, or compliance reporting.
|
|
207
222
|
- Application-specific onboarding, marketing consent, tenancy, ownership,
|
|
@@ -6,7 +6,7 @@ module Agreements
|
|
|
6
6
|
class_name: "Agreements::Version",
|
|
7
7
|
inverse_of: :acceptances
|
|
8
8
|
|
|
9
|
-
validates :subject_key, :actor_key, :authority, :accepted_at, presence: true
|
|
9
|
+
validates :subject_key, :actor_key, :authority, :acceptance_statement, :locale, :accepted_at, presence: true
|
|
10
10
|
validates :authority, format: { with: /\A[a-z0-9_]+\z/ }
|
|
11
11
|
|
|
12
12
|
def readonly?
|
data/lib/agreements/recorder.rb
CHANGED
|
@@ -2,13 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module Agreements
|
|
4
4
|
class Recorder
|
|
5
|
-
def self.call(agreement_version:,
|
|
5
|
+
def self.call(agreement_version:, evidence:)
|
|
6
6
|
Acceptance.create_or_find_by!(
|
|
7
7
|
agreement_version: agreement_version,
|
|
8
|
-
subject_key: Agreements.identity_key(subject)
|
|
8
|
+
subject_key: Agreements.identity_key(evidence.fetch(:subject))
|
|
9
9
|
) do |acceptance|
|
|
10
|
-
acceptance.actor_key = Agreements.identity_key(actor)
|
|
11
|
-
acceptance.authority = authority
|
|
10
|
+
acceptance.actor_key = Agreements.identity_key(evidence.fetch(:actor))
|
|
11
|
+
acceptance.authority = evidence.fetch(:authority)
|
|
12
|
+
acceptance.acceptance_statement = evidence.fetch(:acceptance_statement)
|
|
13
|
+
acceptance.locale = evidence.fetch(:locale)
|
|
12
14
|
acceptance.accepted_at = Time.current
|
|
13
15
|
end
|
|
14
16
|
end
|
data/lib/agreements/version.rb
CHANGED
data/lib/agreements.rb
CHANGED
|
@@ -23,15 +23,21 @@ module Agreements
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
# Explicit keywords keep this evidence boundary distinct from browser params.
|
|
27
|
+
# rubocop:disable-next Metrics/ParameterLists
|
|
28
|
+
def accept!(agreement_key, version_id:, subject:, actor:, authority:, acceptance_statement:, locale:)
|
|
27
29
|
version = current_version(agreement_key)
|
|
28
30
|
raise VersionNotCurrent, version unless version&.id.to_s == version_id.to_s
|
|
29
31
|
|
|
30
32
|
Recorder.call(
|
|
31
33
|
agreement_version: version,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
evidence: {
|
|
35
|
+
subject: subject,
|
|
36
|
+
actor: actor,
|
|
37
|
+
authority: authority,
|
|
38
|
+
acceptance_statement: acceptance_statement,
|
|
39
|
+
locale: locale
|
|
40
|
+
}
|
|
35
41
|
)
|
|
36
42
|
end
|
|
37
43
|
|
|
@@ -21,6 +21,8 @@ class CreateAgreementsTables < ActiveRecord::Migration<%= migration_version %>
|
|
|
21
21
|
t.string :subject_key, null: false
|
|
22
22
|
t.string :actor_key, null: false
|
|
23
23
|
t.string :authority, null: false
|
|
24
|
+
t.text :acceptance_statement, null: false
|
|
25
|
+
t.string :locale, null: false
|
|
24
26
|
t.datetime :accepted_at, null: false
|
|
25
27
|
t.timestamps
|
|
26
28
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: agreements
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Shmarov
|
|
@@ -31,10 +31,10 @@ dependencies:
|
|
|
31
31
|
version: '9'
|
|
32
32
|
description: |
|
|
33
33
|
A small Rails engine that stores immutable external agreement-version
|
|
34
|
-
metadata and append-only acceptance evidence. It distinguishes
|
|
35
|
-
bound by an agreement from the actor accepting under recorded
|
|
36
|
-
rejects stale rendered versions, and leaves documents,
|
|
37
|
-
authorization, routes, and UI in the host application.
|
|
34
|
+
metadata and append-only localized acceptance evidence. It distinguishes
|
|
35
|
+
the subject bound by an agreement from the actor accepting under recorded
|
|
36
|
+
authority, rejects stale rendered versions, and leaves documents,
|
|
37
|
+
authentication, authorization, routes, and UI in the host application.
|
|
38
38
|
email:
|
|
39
39
|
- yashm@outlook.com
|
|
40
40
|
executables: []
|
|
@@ -81,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0'
|
|
83
83
|
requirements: []
|
|
84
|
-
rubygems_version: 4.0.
|
|
84
|
+
rubygems_version: 4.0.16
|
|
85
85
|
specification_version: 4
|
|
86
86
|
summary: Auditable acceptance of externally hosted legal agreements for Rails.
|
|
87
87
|
test_files: []
|