Skip to main content

shared/domain/signing_keys/
repository.rs

1use std::sync::Arc;
2
3use chrono::{Duration, Utc};
4
5use crate::error::{CoreError, InternalError, ResourceKind, Result, TokenErrorType};
6
7use super::model::SigningKey;
8use super::ports::database::DatabaseAdapter;
9use super::ports::query::QueryBuilder;
10
11#[derive(Clone)]
12pub struct SigningKeysRepository {
13    adapter: Arc<dyn DatabaseAdapter<SigningKey>>,
14}
15
16impl SigningKeysRepository {
17    pub fn new(adapter: Arc<dyn DatabaseAdapter<SigningKey>>) -> Self {
18        Self { adapter }
19    }
20}
21
22impl SigningKeysRepository {
23    #[tracing::instrument(name = "db.signing_key.insert", skip(self, signing_key))]
24    pub async fn insert(&self, signing_key: SigningKey) -> Result<String> {
25        match self.adapter.insert(signing_key).await {
26            Ok(id) => Ok(id),
27            Err(err) => {
28                tracing::error!("Failed to insert signing_key to database - {err}");
29                Err(CoreError::Internal(InternalError::Database(
30                    err.to_string(),
31                )))
32            }
33        }
34    }
35
36    #[tracing::instrument(name = "db.signing_keys.find", skip(self))]
37    pub async fn find(&self) -> Result<SigningKey> {
38        let filter = QueryBuilder::default().eq("status", "active");
39
40        match self.adapter.find_one(filter).await {
41            Ok(Some(key)) => Ok(key),
42            // FIXME use Permission not Role
43            Ok(None) => Err(CoreError::NotFound(ResourceKind::Token {
44                token_type: TokenErrorType::Token,
45            })),
46            Err(err) => {
47                tracing::error!(error_code = "InternalError::Database", error = %err, "Database query failed");
48                Err(err)
49            }
50        }
51    }
52
53    #[tracing::instrument(name = "db.signingkeys.find_valid_keys", skip(self))]
54    pub async fn find_valid_keys(&self) -> Result<Vec<SigningKey>> {
55        let filter = QueryBuilder::default()
56            .ne("status", "revoked")
57            .gt("expiresAt", Utc::now());
58
59        match self.adapter.find_all(filter).await {
60            Ok(permissions) => Ok(permissions),
61            Err(err) => {
62                tracing::error!(error_code = "InternalError::Database", error = %err, "Database query failed");
63                Err(err)
64            }
65        }
66    }
67
68    #[tracing::instrument(name = "db.signingkeys.findall", skip(self))]
69    pub async fn find_all(&self) -> Result<Vec<SigningKey>> {
70        let filter = QueryBuilder::default();
71
72        match self.adapter.find_all(filter).await {
73            Ok(keys) => Ok(keys),
74            Err(err) => {
75                tracing::error!(error_code = "InternalError::Database", error = %err, "Database query failed");
76                Err(err)
77            }
78        }
79    }
80
81    #[tracing::instrument(name = "db.signing_keys.find", skip(self))]
82    pub async fn retire_oldkey(&self, max_token_ttl: i64) -> Result<SigningKey> {
83        let filter = QueryBuilder::default().eq("status", "active");
84        let update = QueryBuilder::default()
85            .set("status", "retired")
86            .set("rotatedAt", Utc::now())
87            .set("expiresAt", Utc::now() + Duration::seconds(max_token_ttl));
88
89        match self.adapter.find_one_and_update(filter, update).await {
90            Ok(Some(key)) => Ok(key),
91            Ok(None) => Err(CoreError::NotFound(ResourceKind::Token {
92                token_type: TokenErrorType::Token,
93            })),
94            Err(err) => {
95                tracing::error!(error_code = "InternalError::Database", error = %err, "Database query failed");
96                Err(err)
97            }
98        }
99    }
100
101    #[tracing::instrument(name = "db.signing_keys.revoke", skip(self))]
102    pub async fn revoke_key(&self, id: &str) -> Result<SigningKey> {
103        let filter = QueryBuilder::default().eq("kid", id);
104        let update = QueryBuilder::default().set("status", "revoked");
105
106        match self.adapter.find_one_and_update(filter, update).await {
107            Ok(Some(key)) => Ok(key),
108            Ok(None) => Err(CoreError::NotFound(ResourceKind::Token {
109                token_type: TokenErrorType::Token,
110            })),
111            Err(err) => {
112                tracing::error!(error_code = "InternalError::Database", error = %err, "Database query failed");
113                Err(err)
114            }
115        }
116    }
117
118    #[tracing::instrument(name = "db.permission.find", skip(self, id))]
119    pub async fn delete(&self, id: &str) -> Result<()> {
120        let filter = QueryBuilder::default().eq("id", id);
121
122        match self.adapter.delete_one(filter).await {
123            Ok(_) => Ok(()),
124            Err(err) => {
125                tracing::error!(error_code = "InternalError::Database", error = %err, "Database query failed");
126                Err(err)
127            }
128        }
129    }
130}