systemprompt_users/repository/
device_cert.rs1use systemprompt_identifiers::{DeviceCertId, UserId};
7
8use crate::error::Result;
9use crate::models::UserDeviceCert;
10use crate::repository::UserRepository;
11
12#[derive(Debug)]
13pub struct EnrollDeviceCertParams<'a> {
14 pub id: &'a DeviceCertId,
15 pub user_id: &'a UserId,
16 pub fingerprint: &'a str,
17 pub label: &'a str,
18}
19
20impl UserRepository {
21 pub async fn enroll_device_cert(
22 &self,
23 params: EnrollDeviceCertParams<'_>,
24 ) -> Result<UserDeviceCert> {
25 let row = sqlx::query_as!(
26 UserDeviceCert,
27 r#"
28 INSERT INTO user_device_certs (id, user_id, fingerprint, label)
29 VALUES ($1, $2, $3, $4)
30 RETURNING id, user_id as "user_id: UserId", fingerprint, label, enrolled_at, revoked_at
31 "#,
32 params.id.as_str(),
33 params.user_id.as_str(),
34 params.fingerprint,
35 params.label,
36 )
37 .fetch_one(&*self.write_pool)
38 .await?;
39 Ok(row)
40 }
41
42 pub async fn find_active_device_cert_by_fingerprint(
43 &self,
44 fingerprint: &str,
45 ) -> Result<Option<UserDeviceCert>> {
46 let row = sqlx::query_as!(
47 UserDeviceCert,
48 r#"
49 SELECT id, user_id as "user_id: UserId", fingerprint, label, enrolled_at, revoked_at
50 FROM user_device_certs
51 WHERE fingerprint = $1 AND revoked_at IS NULL
52 "#,
53 fingerprint,
54 )
55 .fetch_optional(&*self.write_pool)
56 .await?;
57 Ok(row)
58 }
59
60 pub async fn list_device_certs_for_user(
61 &self,
62 user_id: &UserId,
63 ) -> Result<Vec<UserDeviceCert>> {
64 let rows = sqlx::query_as!(
65 UserDeviceCert,
66 r#"
67 SELECT id, user_id as "user_id: UserId", fingerprint, label, enrolled_at, revoked_at
68 FROM user_device_certs
69 WHERE user_id = $1
70 ORDER BY enrolled_at DESC
71 "#,
72 user_id.as_str(),
73 )
74 .fetch_all(&*self.write_pool)
75 .await?;
76 Ok(rows)
77 }
78
79 pub async fn revoke_device_cert(&self, id: &DeviceCertId, user_id: &UserId) -> Result<bool> {
80 let result = sqlx::query!(
81 r#"
82 UPDATE user_device_certs
83 SET revoked_at = CURRENT_TIMESTAMP
84 WHERE id = $1 AND user_id = $2 AND revoked_at IS NULL
85 "#,
86 id.as_str(),
87 user_id.as_str(),
88 )
89 .execute(&*self.write_pool)
90 .await?;
91 Ok(result.rows_affected() > 0)
92 }
93}