feat: commit signed op imports atomically

This commit is contained in:
Eric Wendland 2026-07-05 17:57:09 +02:00
commit 64e74acc9d
3 changed files with 150 additions and 47 deletions

View file

@ -1001,6 +1001,36 @@ impl Store {
Ok(())
}
pub fn insert_auth_op_with_signatures(
&self,
op: &StoredAuthOp,
signatures: &[StoredAuthSignature],
) -> Result<(), StoreError> {
let tx = self.conn.unchecked_transaction()?;
tx.execute(
r#"INSERT OR REPLACE INTO auth_ops(op_id, resource_id, op_json, created_at_ms)
VALUES (?1, ?2, ?3, ?4)"#,
params![op.op_id, op.resource_id, op.op_json, op.created_at_ms],
)?;
for signature in signatures {
tx.execute(
r#"INSERT OR REPLACE INTO auth_signatures(
op_id, signer, signer_public_key, namespace, signature, created_at_ms
) VALUES (?1, ?2, ?3, ?4, ?5, ?6)"#,
params![
signature.op_id,
signature.signer,
signature.signer_public_key,
signature.namespace,
signature.signature,
signature.created_at_ms
],
)?;
}
tx.commit()?;
Ok(())
}
pub fn list_auth_ops_for_resource(
&self,
resource_id: &str,
@ -1084,6 +1114,36 @@ impl Store {
Ok(())
}
pub fn insert_keychain_op_with_signatures(
&self,
op: &StoredKeychainOp,
signatures: &[StoredKeychainSignature],
) -> Result<(), StoreError> {
let tx = self.conn.unchecked_transaction()?;
tx.execute(
r#"INSERT OR REPLACE INTO keychain_ops(op_id, op_json, created_at_ms)
VALUES (?1, ?2, ?3)"#,
params![op.op_id, op.op_json, op.created_at_ms],
)?;
for signature in signatures {
tx.execute(
r#"INSERT OR REPLACE INTO keychain_signatures(
op_id, signer, signer_public_key, namespace, signature, created_at_ms
) VALUES (?1, ?2, ?3, ?4, ?5, ?6)"#,
params![
signature.op_id,
signature.signer,
signature.signer_public_key,
signature.namespace,
signature.signature,
signature.created_at_ms
],
)?;
}
tx.commit()?;
Ok(())
}
pub fn list_keychain_ops(&self) -> Result<Vec<StoredKeychainOp>, StoreError> {
let mut stmt = self.conn.prepare(
r#"SELECT op_id, op_json, created_at_ms
@ -1958,6 +2018,44 @@ mod tests {
);
}
#[test]
fn auth_op_with_signatures_rolls_back_when_signature_insert_fails() {
let store = Store::open_memory().expect("open");
let op = StoredAuthOp {
op_id: "auth-op:atomic".to_owned(),
resource_id: "resource:test".to_owned(),
op_json: "{}".to_owned(),
created_at_ms: 1,
};
let signature = StoredAuthSignature {
op_id: op.op_id.clone(),
signer: "key:admin".to_owned(),
signer_public_key: "ssh-ed25519 AAAA test".to_owned(),
namespace: "geth.auth.v1@geth.local".to_owned(),
signature: b"sig".to_vec(),
created_at_ms: 2,
};
store
.conn
.execute("DROP TABLE auth_signatures", [])
.expect("drop signatures table");
assert!(
store
.insert_auth_op_with_signatures(&op, &[signature])
.is_err()
);
let count: i64 = store
.conn
.query_row(
"SELECT COUNT(*) FROM auth_ops WHERE op_id = ?1",
[&op.op_id],
|row| row.get(0),
)
.expect("count auth ops");
assert_eq!(count, 0);
}
#[test]
fn keychain_ops_roundtrip() {
let store = Store::open_memory().expect("open");