| 1 | -- SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | |
| 3 | -- name: InsertUserGPGKey :one |
| 4 | -- Inserts a parsed primary GPG key. Subkeys land in user_gpg_subkeys |
| 5 | -- in the same transaction (see InsertUserGPGSubkey). expires_at is |
| 6 | -- nullable; many keys have no expiration. revoked_at stays NULL on |
| 7 | -- insert; soft-delete sets it. |
| 8 | INSERT INTO user_gpg_keys ( |
| 9 | user_id, name, fingerprint, key_id, armored, |
| 10 | can_sign, can_encrypt_comms, can_encrypt_storage, can_certify, can_authenticate, |
| 11 | uids, subkeys, primary_algo, expires_at |
| 12 | ) |
| 13 | VALUES ( |
| 14 | $1, $2, $3, $4, $5, |
| 15 | $6, $7, $8, $9, $10, |
| 16 | $11, $12, $13, $14 |
| 17 | ) |
| 18 | RETURNING id, user_id, name, fingerprint, key_id, armored, |
| 19 | can_sign, can_encrypt_comms, can_encrypt_storage, can_certify, can_authenticate, |
| 20 | uids, subkeys, primary_algo, |
| 21 | created_at, last_used_at, revoked_at, expires_at; |
| 22 | |
| 23 | -- name: ListUserGPGKeys :many |
| 24 | -- Paginated list for the REST surface; HTML settings page reuses with |
| 25 | -- a generous limit and no offset. |
| 26 | SELECT id, user_id, name, fingerprint, key_id, armored, |
| 27 | can_sign, can_encrypt_comms, can_encrypt_storage, can_certify, can_authenticate, |
| 28 | uids, subkeys, primary_algo, |
| 29 | created_at, last_used_at, revoked_at, expires_at |
| 30 | FROM user_gpg_keys |
| 31 | WHERE user_id = $1 AND revoked_at IS NULL |
| 32 | ORDER BY created_at DESC |
| 33 | LIMIT $2 OFFSET $3; |
| 34 | |
| 35 | -- name: CountUserGPGKeys :one |
| 36 | -- Excludes revoked rows so the per-user cap (100) counts live keys. |
| 37 | SELECT count(*) FROM user_gpg_keys WHERE user_id = $1 AND revoked_at IS NULL; |
| 38 | |
| 39 | -- name: GetUserGPGKey :one |
| 40 | -- Scoped single-key lookup for REST GET-by-id. user_id filter prevents |
| 41 | -- cross-user reads (existence-leak-safe: returns no row if the id |
| 42 | -- belongs to another user). |
| 43 | SELECT id, user_id, name, fingerprint, key_id, armored, |
| 44 | can_sign, can_encrypt_comms, can_encrypt_storage, can_certify, can_authenticate, |
| 45 | uids, subkeys, primary_algo, |
| 46 | created_at, last_used_at, revoked_at, expires_at |
| 47 | FROM user_gpg_keys |
| 48 | WHERE id = $1 AND user_id = $2; |
| 49 | |
| 50 | -- name: GetUserGPGKeyByFingerprint :one |
| 51 | -- Uniqueness probe used by the add path to surface a friendly |
| 52 | -- "this key is already registered" error before the unique index |
| 53 | -- violation. Returns any row matching the fingerprint regardless of |
| 54 | -- which user owns it (global uniqueness is the contract). |
| 55 | SELECT id, user_id, name, fingerprint, key_id, armored, |
| 56 | can_sign, can_encrypt_comms, can_encrypt_storage, can_certify, can_authenticate, |
| 57 | uids, subkeys, primary_algo, |
| 58 | created_at, last_used_at, revoked_at, expires_at |
| 59 | FROM user_gpg_keys |
| 60 | WHERE fingerprint = $1 AND revoked_at IS NULL; |
| 61 | |
| 62 | -- name: SoftDeleteUserGPGKey :execrows |
| 63 | -- Scoped soft-delete: stamps revoked_at, preserves the row for audit |
| 64 | -- continuity. Returns the number of rows affected so the handler can |
| 65 | -- distinguish "not found" from "deleted" without a follow-up query. |
| 66 | UPDATE user_gpg_keys |
| 67 | SET revoked_at = now() |
| 68 | WHERE id = $1 AND user_id = $2 AND revoked_at IS NULL; |
| 69 | |
| 70 | -- name: TouchUserGPGKeyLastUsed :exec |
| 71 | -- Best-effort last-used stamp called from the verification path when |
| 72 | -- a signature successfully resolves to this key. No timeout / error |
| 73 | -- propagation; the caller fires-and-forgets via a goroutine. |
| 74 | UPDATE user_gpg_keys SET last_used_at = now() WHERE id = $1; |
| 75 |