cortex/models/
webauthn.rs1use chrono::NaiveDateTime;
15use diesel::prelude::*;
16use diesel::result::Error;
17use serde_json::Value;
18use uuid::Uuid;
19
20use crate::schema::{webauthn_credentials, webauthn_users};
21
22#[derive(Queryable, Identifiable, Debug, Clone)]
24#[diesel(table_name = webauthn_users, primary_key(owner))]
25pub struct WebauthnUser {
26 pub owner: String,
28 pub handle: Uuid,
30 pub created_at: NaiveDateTime,
32}
33
34#[derive(Insertable)]
35#[diesel(table_name = webauthn_users)]
36struct NewWebauthnUser<'a> {
37 owner: &'a str,
38 handle: Uuid,
39}
40
41impl WebauthnUser {
42 pub fn ensure(connection: &mut PgConnection, owner: &str) -> Result<Uuid, Error> {
45 use crate::schema::webauthn_users::dsl;
46 if let Some(handle) = dsl::webauthn_users
47 .filter(dsl::owner.eq(owner))
48 .select(dsl::handle)
49 .first::<Uuid>(connection)
50 .optional()?
51 {
52 return Ok(handle);
53 }
54 diesel::insert_into(webauthn_users::table)
55 .values(NewWebauthnUser {
56 owner,
57 handle: Uuid::new_v4(),
58 })
59 .on_conflict(dsl::owner)
60 .do_nothing()
61 .execute(connection)?;
62 dsl::webauthn_users
64 .filter(dsl::owner.eq(owner))
65 .select(dsl::handle)
66 .first::<Uuid>(connection)
67 }
68}
69
70#[derive(Queryable, Identifiable, Debug, Clone)]
73#[diesel(table_name = webauthn_credentials)]
74pub struct WebauthnCredential {
75 pub id: i64,
77 pub owner: String,
79 pub label: String,
81 pub credential: Value,
83 pub created_at: NaiveDateTime,
85 pub last_used: Option<NaiveDateTime>,
87}
88
89#[derive(Insertable)]
90#[diesel(table_name = webauthn_credentials)]
91struct NewWebauthnCredential<'a> {
92 owner: &'a str,
93 label: &'a str,
94 credential: &'a Value,
95}
96
97impl WebauthnCredential {
98 pub fn store(
100 connection: &mut PgConnection,
101 owner: &str,
102 label: &str,
103 credential: &Value,
104 ) -> Result<(), Error> {
105 diesel::insert_into(webauthn_credentials::table)
106 .values(NewWebauthnCredential {
107 owner,
108 label,
109 credential,
110 })
111 .execute(connection)
112 .map(|_| ())
113 }
114
115 pub fn for_owner(connection: &mut PgConnection, owner: &str) -> Result<Vec<Self>, Error> {
117 use crate::schema::webauthn_credentials::dsl;
118 dsl::webauthn_credentials
119 .filter(dsl::owner.eq(owner))
120 .order(dsl::id)
121 .get_results(connection)
122 }
123
124 pub fn delete(connection: &mut PgConnection, id: i64, owner: &str) -> Result<usize, Error> {
127 use crate::schema::webauthn_credentials::dsl;
128 diesel::delete(
129 dsl::webauthn_credentials
130 .filter(dsl::id.eq(id))
131 .filter(dsl::owner.eq(owner)),
132 )
133 .execute(connection)
134 }
135
136 pub fn update_after_use(
139 connection: &mut PgConnection,
140 id: i64,
141 credential: &Value,
142 ) -> Result<(), Error> {
143 use crate::schema::webauthn_credentials::dsl;
144 diesel::update(dsl::webauthn_credentials.filter(dsl::id.eq(id)))
145 .set((
146 dsl::credential.eq(credential),
147 dsl::last_used.eq(chrono::Utc::now().naive_utc()),
148 ))
149 .execute(connection)
150 .map(|_| ())
151 }
152
153 pub fn touch(connection: &mut PgConnection, id: i64) -> Result<(), Error> {
156 use crate::schema::webauthn_credentials::dsl;
157 diesel::update(dsl::webauthn_credentials.filter(dsl::id.eq(id)))
158 .set(dsl::last_used.eq(chrono::Utc::now().naive_utc()))
159 .execute(connection)
160 .map(|_| ())
161 }
162}