systemprompt_users/repository/
device_cert.rs1use systemprompt_identifiers::{DeviceCertId, UserId};
7
8use crate::error::Result;
9use crate::models::{UserDeviceCert, UserDeviceCertRow};
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 UserDeviceCertRow,
27 r#"
28 INSERT INTO user_device_certs (id, user_id, fingerprint, label)
29 VALUES ($1, $2, $3, $4)
30 RETURNING id, user_id, 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 .map(UserDeviceCert::from)?;
40 Ok(row)
41 }
42
43 pub async fn find_active_device_cert_by_fingerprint(
44 &self,
45 fingerprint: &str,
46 ) -> Result<Option<UserDeviceCert>> {
47 let row = sqlx::query_as!(
48 UserDeviceCertRow,
49 r#"
50 SELECT id, user_id, fingerprint, label, enrolled_at, revoked_at
51 FROM user_device_certs
52 WHERE fingerprint = $1 AND revoked_at IS NULL
53 "#,
54 fingerprint,
55 )
56 .fetch_optional(&*self.write_pool)
57 .await
58 .map(|row| row.map(UserDeviceCert::from))?;
59 Ok(row)
60 }
61
62 pub async fn list_device_certs_for_user(
63 &self,
64 user_id: &UserId,
65 ) -> Result<Vec<UserDeviceCert>> {
66 let rows = sqlx::query_as!(
67 UserDeviceCertRow,
68 r#"
69 SELECT id, user_id, fingerprint, label, enrolled_at, revoked_at
70 FROM user_device_certs
71 WHERE user_id = $1
72 ORDER BY enrolled_at DESC
73 "#,
74 user_id.as_str(),
75 )
76 .fetch_all(&*self.write_pool)
77 .await
78 .map(|rows| {
79 rows.into_iter()
80 .map(UserDeviceCert::from)
81 .collect::<Vec<_>>()
82 })?;
83 Ok(rows)
84 }
85
86 pub async fn revoke_device_cert(&self, id: &DeviceCertId, user_id: &UserId) -> Result<bool> {
87 let result = sqlx::query!(
88 r#"
89 UPDATE user_device_certs
90 SET revoked_at = CURRENT_TIMESTAMP
91 WHERE id = $1 AND user_id = $2 AND revoked_at IS NULL
92 "#,
93 id.as_str(),
94 user_id.as_str(),
95 )
96 .execute(&*self.write_pool)
97 .await?;
98 Ok(result.rows_affected() > 0)
99 }
100}