@hraness/peopleblade 0.1.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.
- package/LICENSE +21 -0
- package/README.md +79 -0
- package/THIRD_PARTY_NOTICES.md +30 -0
- package/dist/cli.ts +61 -0
- package/dist/migrations/001_core.sql +223 -0
- package/dist/migrations/002_incremental_sources.sql +93 -0
- package/dist/migrations/003_identity_review.sql +67 -0
- package/dist/migrations/004_source_realms_and_observations.sql +91 -0
- package/dist/migrations/005_identity_representatives.sql +87 -0
- package/dist/migrations/006_profile_url_trust.sql +15 -0
- package/dist/migrations/007_person_notes.sql +39 -0
- package/dist/migrations/008_name_identity_trust.sql +33 -0
- package/dist/migrations/009_provider_collection_states.sql +23 -0
- package/dist/migrations/010_source_execution_receipts.sql +51 -0
- package/dist/migrations/011_source_account_convergence.sql +125 -0
- package/dist/migrations/012_granola_note_attribution.sql +389 -0
- package/dist/migrations/013_source_binding_incarnations.sql +339 -0
- package/dist/migrations/014_source_binding_transition_guard_recovery.sql +121 -0
- package/dist/peopleblade.js +20203 -0
- package/package.json +36 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
PRAGMA foreign_keys = ON;
|
|
2
|
+
|
|
3
|
+
CREATE TABLE source_binding_incarnations (
|
|
4
|
+
id INTEGER PRIMARY KEY,
|
|
5
|
+
source_binding_id INTEGER NOT NULL REFERENCES source_bindings(id) ON DELETE RESTRICT,
|
|
6
|
+
authority TEXT NOT NULL CHECK(length(authority) BETWEEN 1 AND 64),
|
|
7
|
+
ordinal INTEGER NOT NULL CHECK(ordinal >= 1),
|
|
8
|
+
subject_sha256 TEXT NOT NULL CHECK(length(subject_sha256) = 64 AND subject_sha256 NOT GLOB '*[^0-9a-f]*'),
|
|
9
|
+
auth_sha256 TEXT NOT NULL CHECK(length(auth_sha256) = 64 AND auth_sha256 NOT GLOB '*[^0-9a-f]*'),
|
|
10
|
+
previous_incarnation_id INTEGER REFERENCES source_binding_incarnations(id) ON DELETE RESTRICT,
|
|
11
|
+
transition_kind TEXT NOT NULL CHECK(transition_kind IN ('initial','explicit-rebind')),
|
|
12
|
+
transition_evidence_sha256 TEXT CHECK(
|
|
13
|
+
transition_evidence_sha256 IS NULL OR (
|
|
14
|
+
length(transition_evidence_sha256) = 64
|
|
15
|
+
AND transition_evidence_sha256 NOT GLOB '*[^0-9a-f]*'
|
|
16
|
+
)
|
|
17
|
+
),
|
|
18
|
+
transition_evidence_json TEXT CHECK(
|
|
19
|
+
transition_evidence_json IS NULL OR json_valid(transition_evidence_json)
|
|
20
|
+
),
|
|
21
|
+
first_seen_at TEXT NOT NULL,
|
|
22
|
+
last_seen_at TEXT NOT NULL,
|
|
23
|
+
retired_at TEXT,
|
|
24
|
+
metadata_json TEXT NOT NULL DEFAULT '{}' CHECK(json_valid(metadata_json)),
|
|
25
|
+
CHECK(
|
|
26
|
+
(transition_kind = 'initial' AND ordinal=1 AND previous_incarnation_id IS NULL
|
|
27
|
+
AND transition_evidence_sha256 IS NULL AND transition_evidence_json IS NULL)
|
|
28
|
+
OR
|
|
29
|
+
(transition_kind = 'explicit-rebind' AND ordinal>=2 AND previous_incarnation_id IS NOT NULL
|
|
30
|
+
AND transition_evidence_sha256 IS NOT NULL AND transition_evidence_json IS NOT NULL)
|
|
31
|
+
),
|
|
32
|
+
UNIQUE(source_binding_id, ordinal)
|
|
33
|
+
) STRICT;
|
|
34
|
+
|
|
35
|
+
CREATE UNIQUE INDEX source_binding_incarnations_current
|
|
36
|
+
ON source_binding_incarnations(source_binding_id) WHERE retired_at IS NULL;
|
|
37
|
+
CREATE INDEX source_binding_incarnations_history
|
|
38
|
+
ON source_binding_incarnations(source_binding_id, ordinal DESC);
|
|
39
|
+
CREATE INDEX source_binding_incarnations_subject_owner
|
|
40
|
+
ON source_binding_incarnations(authority, subject_sha256, source_binding_id);
|
|
41
|
+
|
|
42
|
+
CREATE TABLE source_binding_transition_guards (
|
|
43
|
+
source_binding_id INTEGER PRIMARY KEY REFERENCES source_bindings(id) ON DELETE RESTRICT,
|
|
44
|
+
retiring_incarnation_id INTEGER NOT NULL UNIQUE REFERENCES source_binding_incarnations(id) ON DELETE RESTRICT,
|
|
45
|
+
next_ordinal INTEGER NOT NULL CHECK(next_ordinal >= 2),
|
|
46
|
+
authority TEXT NOT NULL CHECK(length(authority) BETWEEN 1 AND 64),
|
|
47
|
+
next_subject_sha256 TEXT NOT NULL CHECK(length(next_subject_sha256) = 64 AND next_subject_sha256 NOT GLOB '*[^0-9a-f]*'),
|
|
48
|
+
next_auth_sha256 TEXT NOT NULL CHECK(length(next_auth_sha256) = 64 AND next_auth_sha256 NOT GLOB '*[^0-9a-f]*'),
|
|
49
|
+
transition_evidence_sha256 TEXT NOT NULL CHECK(
|
|
50
|
+
length(transition_evidence_sha256) = 64
|
|
51
|
+
AND transition_evidence_sha256 NOT GLOB '*[^0-9a-f]*'
|
|
52
|
+
),
|
|
53
|
+
transition_evidence_json TEXT NOT NULL CHECK(json_valid(transition_evidence_json)),
|
|
54
|
+
observed_at TEXT NOT NULL CHECK(julianday(observed_at) IS NOT NULL),
|
|
55
|
+
expected_last_seen_at TEXT NOT NULL CHECK(julianday(expected_last_seen_at) IS NOT NULL),
|
|
56
|
+
expected_metadata_json TEXT NOT NULL CHECK(json_valid(expected_metadata_json)),
|
|
57
|
+
expected_complete_realm_inventory_observed_at TEXT,
|
|
58
|
+
expected_complete_realm_inventory_sha256 TEXT,
|
|
59
|
+
complete_realm_inventory_sha256 TEXT NOT NULL CHECK(
|
|
60
|
+
length(complete_realm_inventory_sha256) = 64
|
|
61
|
+
AND complete_realm_inventory_sha256 NOT GLOB '*[^0-9a-f]*'
|
|
62
|
+
),
|
|
63
|
+
metadata_json TEXT NOT NULL CHECK(json_valid(metadata_json)),
|
|
64
|
+
CHECK(
|
|
65
|
+
(expected_complete_realm_inventory_observed_at IS NULL
|
|
66
|
+
AND expected_complete_realm_inventory_sha256 IS NULL)
|
|
67
|
+
OR
|
|
68
|
+
(julianday(expected_complete_realm_inventory_observed_at) IS NOT NULL
|
|
69
|
+
AND length(expected_complete_realm_inventory_sha256) = 64
|
|
70
|
+
AND expected_complete_realm_inventory_sha256 NOT GLOB '*[^0-9a-f]*')
|
|
71
|
+
)
|
|
72
|
+
) STRICT;
|
|
73
|
+
|
|
74
|
+
INSERT INTO source_binding_incarnations(
|
|
75
|
+
source_binding_id,authority,ordinal,subject_sha256,auth_sha256,transition_kind,
|
|
76
|
+
first_seen_at,last_seen_at,metadata_json
|
|
77
|
+
)
|
|
78
|
+
SELECT id,authority,1,subject_sha256,auth_sha256,'initial',first_seen_at,last_seen_at,metadata_json
|
|
79
|
+
FROM source_bindings;
|
|
80
|
+
|
|
81
|
+
DROP TRIGGER source_bindings_identity_immutable;
|
|
82
|
+
|
|
83
|
+
CREATE TRIGGER source_bindings_stable_identity_immutable
|
|
84
|
+
BEFORE UPDATE OF authority,auth_id ON source_bindings FOR EACH ROW
|
|
85
|
+
BEGIN SELECT RAISE(ABORT, 'source binding authority and authorization ID are immutable'); END;
|
|
86
|
+
|
|
87
|
+
CREATE TRIGGER source_bindings_initial_incarnation
|
|
88
|
+
AFTER INSERT ON source_bindings FOR EACH ROW
|
|
89
|
+
BEGIN
|
|
90
|
+
INSERT INTO source_binding_incarnations(
|
|
91
|
+
source_binding_id,authority,ordinal,subject_sha256,auth_sha256,transition_kind,
|
|
92
|
+
first_seen_at,last_seen_at,metadata_json
|
|
93
|
+
) VALUES (
|
|
94
|
+
NEW.id,NEW.authority,1,NEW.subject_sha256,NEW.auth_sha256,'initial',
|
|
95
|
+
NEW.first_seen_at,NEW.last_seen_at,NEW.metadata_json
|
|
96
|
+
);
|
|
97
|
+
END;
|
|
98
|
+
|
|
99
|
+
CREATE TRIGGER source_binding_incarnations_no_replace
|
|
100
|
+
BEFORE INSERT ON source_binding_incarnations FOR EACH ROW
|
|
101
|
+
WHEN EXISTS (
|
|
102
|
+
SELECT 1 FROM source_binding_incarnations
|
|
103
|
+
WHERE id=NEW.id
|
|
104
|
+
OR (source_binding_id=NEW.source_binding_id AND ordinal=NEW.ordinal)
|
|
105
|
+
)
|
|
106
|
+
BEGIN SELECT RAISE(ABORT, 'source binding incarnation history cannot be replaced'); END;
|
|
107
|
+
|
|
108
|
+
CREATE TRIGGER source_binding_incarnations_identity_immutable
|
|
109
|
+
BEFORE UPDATE ON source_binding_incarnations FOR EACH ROW
|
|
110
|
+
WHEN NEW.id IS NOT OLD.id
|
|
111
|
+
OR NEW.source_binding_id IS NOT OLD.source_binding_id
|
|
112
|
+
OR NEW.authority IS NOT OLD.authority
|
|
113
|
+
OR NEW.ordinal IS NOT OLD.ordinal
|
|
114
|
+
OR NEW.subject_sha256 IS NOT OLD.subject_sha256
|
|
115
|
+
OR NEW.auth_sha256 IS NOT OLD.auth_sha256
|
|
116
|
+
OR NEW.previous_incarnation_id IS NOT OLD.previous_incarnation_id
|
|
117
|
+
OR NEW.transition_kind IS NOT OLD.transition_kind
|
|
118
|
+
OR NEW.transition_evidence_sha256 IS NOT OLD.transition_evidence_sha256
|
|
119
|
+
OR NEW.transition_evidence_json IS NOT OLD.transition_evidence_json
|
|
120
|
+
OR NEW.first_seen_at IS NOT OLD.first_seen_at
|
|
121
|
+
BEGIN SELECT RAISE(ABORT, 'source binding incarnation identity is immutable'); END;
|
|
122
|
+
|
|
123
|
+
CREATE TRIGGER source_binding_incarnations_retired_state_immutable
|
|
124
|
+
BEFORE UPDATE OF last_seen_at,metadata_json ON source_binding_incarnations FOR EACH ROW
|
|
125
|
+
WHEN OLD.retired_at IS NOT NULL OR NEW.retired_at IS NOT NULL
|
|
126
|
+
BEGIN SELECT RAISE(ABORT, 'retired source binding incarnation state is immutable'); END;
|
|
127
|
+
|
|
128
|
+
CREATE TRIGGER source_binding_incarnations_current_state_shadow
|
|
129
|
+
BEFORE UPDATE OF last_seen_at,metadata_json ON source_binding_incarnations FOR EACH ROW
|
|
130
|
+
WHEN OLD.retired_at IS NULL AND NOT EXISTS (
|
|
131
|
+
SELECT 1 FROM source_bindings binding
|
|
132
|
+
WHERE binding.id=OLD.source_binding_id
|
|
133
|
+
AND binding.authority=OLD.authority
|
|
134
|
+
AND binding.last_seen_at=NEW.last_seen_at
|
|
135
|
+
AND binding.metadata_json=NEW.metadata_json
|
|
136
|
+
)
|
|
137
|
+
BEGIN SELECT RAISE(ABORT, 'current source binding incarnation state must shadow its binding'); END;
|
|
138
|
+
|
|
139
|
+
CREATE TRIGGER source_binding_incarnations_retirement_monotonic
|
|
140
|
+
BEFORE UPDATE OF retired_at ON source_binding_incarnations FOR EACH ROW
|
|
141
|
+
WHEN OLD.retired_at IS NOT NULL OR NEW.retired_at IS NULL
|
|
142
|
+
BEGIN SELECT RAISE(ABORT, 'source binding incarnation retirement is monotonic'); END;
|
|
143
|
+
|
|
144
|
+
CREATE TRIGGER source_binding_incarnations_retirement_authorized
|
|
145
|
+
BEFORE UPDATE OF retired_at ON source_binding_incarnations FOR EACH ROW
|
|
146
|
+
WHEN OLD.retired_at IS NULL AND NEW.retired_at IS NOT NULL AND NOT EXISTS (
|
|
147
|
+
SELECT 1 FROM source_binding_transition_guards guard
|
|
148
|
+
JOIN source_bindings binding ON binding.id=guard.source_binding_id
|
|
149
|
+
WHERE guard.source_binding_id=OLD.source_binding_id
|
|
150
|
+
AND guard.retiring_incarnation_id=OLD.id AND guard.next_ordinal=OLD.ordinal+1
|
|
151
|
+
AND guard.authority=OLD.authority
|
|
152
|
+
AND guard.observed_at=NEW.retired_at
|
|
153
|
+
AND julianday(guard.observed_at)>=julianday(OLD.last_seen_at)
|
|
154
|
+
AND guard.expected_last_seen_at=OLD.last_seen_at
|
|
155
|
+
AND guard.expected_metadata_json=OLD.metadata_json
|
|
156
|
+
AND binding.authority=OLD.authority
|
|
157
|
+
AND binding.subject_sha256=OLD.subject_sha256
|
|
158
|
+
AND binding.auth_sha256=OLD.auth_sha256
|
|
159
|
+
AND binding.last_seen_at=guard.expected_last_seen_at
|
|
160
|
+
AND binding.metadata_json=guard.expected_metadata_json
|
|
161
|
+
AND binding.complete_realm_inventory_observed_at
|
|
162
|
+
IS guard.expected_complete_realm_inventory_observed_at
|
|
163
|
+
AND binding.complete_realm_inventory_sha256
|
|
164
|
+
IS guard.expected_complete_realm_inventory_sha256
|
|
165
|
+
)
|
|
166
|
+
BEGIN SELECT RAISE(ABORT, 'current source binding incarnation requires an atomic successor'); END;
|
|
167
|
+
|
|
168
|
+
CREATE TRIGGER source_binding_transition_guards_snapshot_insert
|
|
169
|
+
BEFORE INSERT ON source_binding_transition_guards FOR EACH ROW
|
|
170
|
+
WHEN NOT EXISTS (
|
|
171
|
+
SELECT 1 FROM source_binding_incarnations incarnation
|
|
172
|
+
JOIN source_bindings binding ON binding.id=incarnation.source_binding_id
|
|
173
|
+
WHERE incarnation.id=NEW.retiring_incarnation_id
|
|
174
|
+
AND incarnation.source_binding_id=NEW.source_binding_id
|
|
175
|
+
AND incarnation.retired_at IS NULL
|
|
176
|
+
AND incarnation.ordinal+1=NEW.next_ordinal
|
|
177
|
+
AND incarnation.authority=NEW.authority
|
|
178
|
+
AND incarnation.last_seen_at=NEW.expected_last_seen_at
|
|
179
|
+
AND incarnation.metadata_json=NEW.expected_metadata_json
|
|
180
|
+
AND binding.authority=NEW.authority
|
|
181
|
+
AND binding.subject_sha256=incarnation.subject_sha256
|
|
182
|
+
AND binding.auth_sha256=incarnation.auth_sha256
|
|
183
|
+
AND binding.last_seen_at=NEW.expected_last_seen_at
|
|
184
|
+
AND binding.metadata_json=NEW.expected_metadata_json
|
|
185
|
+
AND binding.complete_realm_inventory_observed_at
|
|
186
|
+
IS NEW.expected_complete_realm_inventory_observed_at
|
|
187
|
+
AND binding.complete_realm_inventory_sha256
|
|
188
|
+
IS NEW.expected_complete_realm_inventory_sha256
|
|
189
|
+
)
|
|
190
|
+
BEGIN SELECT RAISE(ABORT, 'source binding transition guard does not match current reviewed state'); END;
|
|
191
|
+
|
|
192
|
+
CREATE TRIGGER source_binding_transition_guards_immutable
|
|
193
|
+
BEFORE UPDATE ON source_binding_transition_guards FOR EACH ROW
|
|
194
|
+
BEGIN SELECT RAISE(ABORT, 'source binding transition guard is immutable'); END;
|
|
195
|
+
|
|
196
|
+
CREATE TRIGGER source_binding_transition_guards_no_replace
|
|
197
|
+
BEFORE INSERT ON source_binding_transition_guards FOR EACH ROW
|
|
198
|
+
WHEN EXISTS (
|
|
199
|
+
SELECT 1 FROM source_binding_transition_guards
|
|
200
|
+
WHERE source_binding_id=NEW.source_binding_id
|
|
201
|
+
OR retiring_incarnation_id=NEW.retiring_incarnation_id
|
|
202
|
+
)
|
|
203
|
+
BEGIN SELECT RAISE(ABORT, 'source binding transition guard cannot be replaced'); END;
|
|
204
|
+
|
|
205
|
+
CREATE TRIGGER source_binding_transition_guards_consumed_delete
|
|
206
|
+
BEFORE DELETE ON source_binding_transition_guards FOR EACH ROW
|
|
207
|
+
WHEN NOT (
|
|
208
|
+
EXISTS (
|
|
209
|
+
SELECT 1 FROM source_binding_incarnations predecessor
|
|
210
|
+
WHERE predecessor.id=OLD.retiring_incarnation_id
|
|
211
|
+
AND predecessor.source_binding_id=OLD.source_binding_id
|
|
212
|
+
AND predecessor.authority=OLD.authority
|
|
213
|
+
AND predecessor.ordinal=OLD.next_ordinal-1
|
|
214
|
+
AND predecessor.retired_at=OLD.observed_at
|
|
215
|
+
AND predecessor.last_seen_at=OLD.expected_last_seen_at
|
|
216
|
+
AND predecessor.metadata_json=OLD.expected_metadata_json
|
|
217
|
+
)
|
|
218
|
+
AND EXISTS (
|
|
219
|
+
SELECT 1 FROM source_binding_incarnations successor
|
|
220
|
+
WHERE successor.source_binding_id=OLD.source_binding_id
|
|
221
|
+
AND successor.authority=OLD.authority
|
|
222
|
+
AND successor.ordinal=OLD.next_ordinal
|
|
223
|
+
AND successor.subject_sha256=OLD.next_subject_sha256
|
|
224
|
+
AND successor.auth_sha256=OLD.next_auth_sha256
|
|
225
|
+
AND successor.previous_incarnation_id=OLD.retiring_incarnation_id
|
|
226
|
+
AND successor.transition_kind='explicit-rebind'
|
|
227
|
+
AND successor.transition_evidence_sha256=OLD.transition_evidence_sha256
|
|
228
|
+
AND successor.transition_evidence_json=OLD.transition_evidence_json
|
|
229
|
+
AND successor.first_seen_at=OLD.observed_at
|
|
230
|
+
AND successor.last_seen_at=OLD.observed_at
|
|
231
|
+
AND successor.retired_at IS NULL
|
|
232
|
+
AND successor.metadata_json=OLD.metadata_json
|
|
233
|
+
)
|
|
234
|
+
AND EXISTS (
|
|
235
|
+
SELECT 1 FROM source_bindings binding
|
|
236
|
+
WHERE binding.id=OLD.source_binding_id
|
|
237
|
+
AND binding.authority=OLD.authority
|
|
238
|
+
AND binding.subject_sha256=OLD.next_subject_sha256
|
|
239
|
+
AND binding.auth_sha256=OLD.next_auth_sha256
|
|
240
|
+
AND binding.last_seen_at=OLD.observed_at
|
|
241
|
+
AND binding.complete_realm_inventory_observed_at=OLD.observed_at
|
|
242
|
+
AND binding.complete_realm_inventory_sha256=OLD.complete_realm_inventory_sha256
|
|
243
|
+
AND binding.metadata_json=OLD.metadata_json
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
BEGIN SELECT RAISE(ABORT, 'source binding transition guard can only be deleted after exact consumption'); END;
|
|
247
|
+
|
|
248
|
+
CREATE TRIGGER source_binding_incarnations_no_delete
|
|
249
|
+
BEFORE DELETE ON source_binding_incarnations FOR EACH ROW
|
|
250
|
+
BEGIN SELECT RAISE(ABORT, 'source binding incarnation history is immutable'); END;
|
|
251
|
+
|
|
252
|
+
CREATE TRIGGER source_binding_incarnations_subject_owner_insert
|
|
253
|
+
BEFORE INSERT ON source_binding_incarnations FOR EACH ROW
|
|
254
|
+
WHEN EXISTS (
|
|
255
|
+
SELECT 1 FROM source_binding_incarnations
|
|
256
|
+
WHERE authority=NEW.authority AND subject_sha256=NEW.subject_sha256
|
|
257
|
+
AND source_binding_id<>NEW.source_binding_id
|
|
258
|
+
)
|
|
259
|
+
BEGIN SELECT RAISE(ABORT, 'source binding subject belongs to another binding'); END;
|
|
260
|
+
|
|
261
|
+
CREATE TRIGGER source_binding_incarnations_chain_insert
|
|
262
|
+
BEFORE INSERT ON source_binding_incarnations FOR EACH ROW
|
|
263
|
+
WHEN NEW.transition_kind='explicit-rebind' AND NOT EXISTS (
|
|
264
|
+
SELECT 1 FROM source_binding_incarnations previous
|
|
265
|
+
WHERE previous.id=NEW.previous_incarnation_id
|
|
266
|
+
AND previous.source_binding_id=NEW.source_binding_id
|
|
267
|
+
AND previous.authority=NEW.authority
|
|
268
|
+
AND previous.ordinal=NEW.ordinal-1 AND previous.retired_at IS NOT NULL
|
|
269
|
+
AND (previous.subject_sha256<>NEW.subject_sha256 OR previous.auth_sha256<>NEW.auth_sha256)
|
|
270
|
+
AND EXISTS (
|
|
271
|
+
SELECT 1 FROM source_bindings binding
|
|
272
|
+
WHERE binding.id=NEW.source_binding_id AND binding.authority=NEW.authority
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
BEGIN SELECT RAISE(ABORT, 'source binding incarnation predecessor is invalid'); END;
|
|
276
|
+
|
|
277
|
+
CREATE TRIGGER source_binding_incarnations_atomic_successor
|
|
278
|
+
AFTER UPDATE OF retired_at ON source_binding_incarnations FOR EACH ROW
|
|
279
|
+
WHEN OLD.retired_at IS NULL AND NEW.retired_at IS NOT NULL
|
|
280
|
+
BEGIN
|
|
281
|
+
INSERT INTO source_binding_incarnations(
|
|
282
|
+
source_binding_id,authority,ordinal,subject_sha256,auth_sha256,previous_incarnation_id,
|
|
283
|
+
transition_kind,transition_evidence_sha256,transition_evidence_json,
|
|
284
|
+
first_seen_at,last_seen_at,metadata_json
|
|
285
|
+
)
|
|
286
|
+
SELECT source_binding_id,authority,next_ordinal,next_subject_sha256,next_auth_sha256,
|
|
287
|
+
retiring_incarnation_id,'explicit-rebind',transition_evidence_sha256,
|
|
288
|
+
transition_evidence_json,observed_at,observed_at,metadata_json
|
|
289
|
+
FROM source_binding_transition_guards
|
|
290
|
+
WHERE source_binding_id=NEW.source_binding_id
|
|
291
|
+
AND retiring_incarnation_id=NEW.id AND next_ordinal=NEW.ordinal+1;
|
|
292
|
+
|
|
293
|
+
UPDATE source_bindings
|
|
294
|
+
SET subject_sha256=(SELECT next_subject_sha256 FROM source_binding_transition_guards
|
|
295
|
+
WHERE source_binding_id=NEW.source_binding_id),
|
|
296
|
+
auth_sha256=(SELECT next_auth_sha256 FROM source_binding_transition_guards
|
|
297
|
+
WHERE source_binding_id=NEW.source_binding_id),
|
|
298
|
+
last_seen_at=(SELECT observed_at FROM source_binding_transition_guards
|
|
299
|
+
WHERE source_binding_id=NEW.source_binding_id),
|
|
300
|
+
complete_realm_inventory_observed_at=(SELECT observed_at FROM source_binding_transition_guards
|
|
301
|
+
WHERE source_binding_id=NEW.source_binding_id),
|
|
302
|
+
complete_realm_inventory_sha256=(SELECT complete_realm_inventory_sha256
|
|
303
|
+
FROM source_binding_transition_guards WHERE source_binding_id=NEW.source_binding_id),
|
|
304
|
+
metadata_json=(SELECT metadata_json FROM source_binding_transition_guards
|
|
305
|
+
WHERE source_binding_id=NEW.source_binding_id)
|
|
306
|
+
WHERE id=NEW.source_binding_id;
|
|
307
|
+
|
|
308
|
+
DELETE FROM source_binding_transition_guards WHERE source_binding_id=NEW.source_binding_id;
|
|
309
|
+
END;
|
|
310
|
+
|
|
311
|
+
CREATE TRIGGER source_bindings_identity_shadow_guard
|
|
312
|
+
BEFORE UPDATE OF subject_sha256,auth_sha256 ON source_bindings FOR EACH ROW
|
|
313
|
+
WHEN NOT EXISTS (
|
|
314
|
+
SELECT 1 FROM source_binding_incarnations
|
|
315
|
+
WHERE source_binding_id=NEW.id AND retired_at IS NULL
|
|
316
|
+
AND authority=NEW.authority
|
|
317
|
+
AND subject_sha256=NEW.subject_sha256 AND auth_sha256=NEW.auth_sha256
|
|
318
|
+
)
|
|
319
|
+
BEGIN SELECT RAISE(ABORT, 'source binding identity must shadow its current incarnation'); END;
|
|
320
|
+
|
|
321
|
+
CREATE TRIGGER source_bindings_current_incarnation_metadata
|
|
322
|
+
AFTER UPDATE OF last_seen_at,metadata_json ON source_bindings FOR EACH ROW
|
|
323
|
+
BEGIN
|
|
324
|
+
UPDATE source_binding_incarnations
|
|
325
|
+
SET last_seen_at=NEW.last_seen_at,metadata_json=NEW.metadata_json
|
|
326
|
+
WHERE source_binding_id=NEW.id AND retired_at IS NULL;
|
|
327
|
+
END;
|
|
328
|
+
|
|
329
|
+
CREATE VIEW current_source_binding_incarnations AS
|
|
330
|
+
SELECT incarnation.id,incarnation.source_binding_id,incarnation.authority,
|
|
331
|
+
incarnation.ordinal,incarnation.subject_sha256,incarnation.auth_sha256,
|
|
332
|
+
incarnation.first_seen_at,incarnation.last_seen_at,incarnation.metadata_json
|
|
333
|
+
FROM source_binding_incarnations incarnation
|
|
334
|
+
JOIN source_bindings binding
|
|
335
|
+
ON binding.id=incarnation.source_binding_id
|
|
336
|
+
AND binding.authority=incarnation.authority
|
|
337
|
+
AND binding.subject_sha256=incarnation.subject_sha256
|
|
338
|
+
AND binding.auth_sha256=incarnation.auth_sha256
|
|
339
|
+
WHERE incarnation.retired_at IS NULL;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
PRAGMA foreign_keys = ON;
|
|
2
|
+
|
|
3
|
+
DROP TRIGGER source_binding_transition_guards_snapshot_insert;
|
|
4
|
+
|
|
5
|
+
CREATE TRIGGER source_binding_transition_guards_snapshot_insert
|
|
6
|
+
BEFORE INSERT ON source_binding_transition_guards FOR EACH ROW
|
|
7
|
+
WHEN NOT EXISTS (
|
|
8
|
+
SELECT 1 FROM source_binding_incarnations incarnation
|
|
9
|
+
JOIN source_bindings binding ON binding.id=incarnation.source_binding_id
|
|
10
|
+
WHERE incarnation.id=NEW.retiring_incarnation_id
|
|
11
|
+
AND incarnation.source_binding_id=NEW.source_binding_id
|
|
12
|
+
AND incarnation.retired_at IS NULL
|
|
13
|
+
AND incarnation.ordinal+1=NEW.next_ordinal
|
|
14
|
+
AND incarnation.authority=NEW.authority
|
|
15
|
+
AND (incarnation.subject_sha256<>NEW.next_subject_sha256
|
|
16
|
+
OR incarnation.auth_sha256<>NEW.next_auth_sha256)
|
|
17
|
+
AND julianday(NEW.observed_at)>=julianday(incarnation.last_seen_at)
|
|
18
|
+
AND incarnation.last_seen_at=NEW.expected_last_seen_at
|
|
19
|
+
AND incarnation.metadata_json=NEW.expected_metadata_json
|
|
20
|
+
AND binding.authority=NEW.authority
|
|
21
|
+
AND binding.subject_sha256=incarnation.subject_sha256
|
|
22
|
+
AND binding.auth_sha256=incarnation.auth_sha256
|
|
23
|
+
AND binding.last_seen_at=NEW.expected_last_seen_at
|
|
24
|
+
AND binding.metadata_json=NEW.expected_metadata_json
|
|
25
|
+
AND binding.complete_realm_inventory_observed_at
|
|
26
|
+
IS NEW.expected_complete_realm_inventory_observed_at
|
|
27
|
+
AND binding.complete_realm_inventory_sha256
|
|
28
|
+
IS NEW.expected_complete_realm_inventory_sha256
|
|
29
|
+
AND NOT EXISTS (
|
|
30
|
+
SELECT 1 FROM source_binding_incarnations owner
|
|
31
|
+
WHERE owner.authority=NEW.authority
|
|
32
|
+
AND owner.subject_sha256=NEW.next_subject_sha256
|
|
33
|
+
AND owner.source_binding_id<>NEW.source_binding_id
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
BEGIN SELECT RAISE(ABORT, 'source binding transition guard does not match current reviewed state'); END;
|
|
37
|
+
|
|
38
|
+
DROP TRIGGER source_binding_transition_guards_consumed_delete;
|
|
39
|
+
|
|
40
|
+
CREATE TRIGGER source_binding_transition_guards_consumed_delete
|
|
41
|
+
BEFORE DELETE ON source_binding_transition_guards FOR EACH ROW
|
|
42
|
+
WHEN NOT (
|
|
43
|
+
(
|
|
44
|
+
EXISTS (
|
|
45
|
+
SELECT 1 FROM source_binding_incarnations predecessor
|
|
46
|
+
WHERE predecessor.id=OLD.retiring_incarnation_id
|
|
47
|
+
AND predecessor.source_binding_id=OLD.source_binding_id
|
|
48
|
+
AND predecessor.authority=OLD.authority
|
|
49
|
+
AND predecessor.ordinal=OLD.next_ordinal-1
|
|
50
|
+
AND predecessor.retired_at=OLD.observed_at
|
|
51
|
+
AND predecessor.last_seen_at=OLD.expected_last_seen_at
|
|
52
|
+
AND predecessor.metadata_json=OLD.expected_metadata_json
|
|
53
|
+
)
|
|
54
|
+
AND EXISTS (
|
|
55
|
+
SELECT 1 FROM source_binding_incarnations successor
|
|
56
|
+
WHERE successor.source_binding_id=OLD.source_binding_id
|
|
57
|
+
AND successor.authority=OLD.authority
|
|
58
|
+
AND successor.ordinal=OLD.next_ordinal
|
|
59
|
+
AND successor.subject_sha256=OLD.next_subject_sha256
|
|
60
|
+
AND successor.auth_sha256=OLD.next_auth_sha256
|
|
61
|
+
AND successor.previous_incarnation_id=OLD.retiring_incarnation_id
|
|
62
|
+
AND successor.transition_kind='explicit-rebind'
|
|
63
|
+
AND successor.transition_evidence_sha256=OLD.transition_evidence_sha256
|
|
64
|
+
AND successor.transition_evidence_json=OLD.transition_evidence_json
|
|
65
|
+
AND successor.first_seen_at=OLD.observed_at
|
|
66
|
+
AND successor.last_seen_at=OLD.observed_at
|
|
67
|
+
AND successor.retired_at IS NULL
|
|
68
|
+
AND successor.metadata_json=OLD.metadata_json
|
|
69
|
+
)
|
|
70
|
+
AND EXISTS (
|
|
71
|
+
SELECT 1 FROM source_bindings binding
|
|
72
|
+
WHERE binding.id=OLD.source_binding_id
|
|
73
|
+
AND binding.authority=OLD.authority
|
|
74
|
+
AND binding.subject_sha256=OLD.next_subject_sha256
|
|
75
|
+
AND binding.auth_sha256=OLD.next_auth_sha256
|
|
76
|
+
AND binding.last_seen_at=OLD.observed_at
|
|
77
|
+
AND binding.complete_realm_inventory_observed_at=OLD.observed_at
|
|
78
|
+
AND binding.complete_realm_inventory_sha256=OLD.complete_realm_inventory_sha256
|
|
79
|
+
AND binding.metadata_json=OLD.metadata_json
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
OR (
|
|
83
|
+
NOT EXISTS (
|
|
84
|
+
SELECT 1 FROM source_binding_incarnations successor
|
|
85
|
+
WHERE successor.source_binding_id=OLD.source_binding_id
|
|
86
|
+
AND successor.ordinal=OLD.next_ordinal
|
|
87
|
+
)
|
|
88
|
+
AND EXISTS (
|
|
89
|
+
SELECT 1 FROM source_binding_incarnations predecessor
|
|
90
|
+
JOIN source_bindings binding ON binding.id=predecessor.source_binding_id
|
|
91
|
+
WHERE predecessor.id=OLD.retiring_incarnation_id
|
|
92
|
+
AND predecessor.source_binding_id=OLD.source_binding_id
|
|
93
|
+
AND predecessor.authority=OLD.authority
|
|
94
|
+
AND predecessor.ordinal=OLD.next_ordinal-1
|
|
95
|
+
AND predecessor.retired_at IS NULL
|
|
96
|
+
AND binding.authority=OLD.authority
|
|
97
|
+
AND binding.subject_sha256=predecessor.subject_sha256
|
|
98
|
+
AND binding.auth_sha256=predecessor.auth_sha256
|
|
99
|
+
AND (
|
|
100
|
+
(predecessor.subject_sha256=OLD.next_subject_sha256
|
|
101
|
+
AND predecessor.auth_sha256=OLD.next_auth_sha256)
|
|
102
|
+
OR julianday(OLD.observed_at)<julianday(predecessor.last_seen_at)
|
|
103
|
+
OR predecessor.last_seen_at IS NOT OLD.expected_last_seen_at
|
|
104
|
+
OR predecessor.metadata_json IS NOT OLD.expected_metadata_json
|
|
105
|
+
OR binding.last_seen_at IS NOT OLD.expected_last_seen_at
|
|
106
|
+
OR binding.metadata_json IS NOT OLD.expected_metadata_json
|
|
107
|
+
OR binding.complete_realm_inventory_observed_at
|
|
108
|
+
IS NOT OLD.expected_complete_realm_inventory_observed_at
|
|
109
|
+
OR binding.complete_realm_inventory_sha256
|
|
110
|
+
IS NOT OLD.expected_complete_realm_inventory_sha256
|
|
111
|
+
OR EXISTS (
|
|
112
|
+
SELECT 1 FROM source_binding_incarnations owner
|
|
113
|
+
WHERE owner.authority=OLD.authority
|
|
114
|
+
AND owner.subject_sha256=OLD.next_subject_sha256
|
|
115
|
+
AND owner.source_binding_id<>OLD.source_binding_id
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
BEGIN SELECT RAISE(ABORT, 'source binding transition guard can only be deleted after exact consumption or proven invalidation'); END;
|