Skip to main content

systemprompt_users/repository/
device_cert.rs

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