Skip to main content

docbox_search/typesense/
api_key.rs

1use super::TypesenseSearchError;
2use docbox_secrets::{Secret, SecretManager};
3use serde::{Deserialize, Serialize};
4use std::fmt::Debug;
5use tokio::sync::Mutex;
6
7/// Provider for how an API key is sourced
8pub enum TypesenseApiKeyProvider {
9    ApiKey(TypesenseApiKey),
10    Secret(TypesenseApiKeySecret),
11}
12
13impl ApiKeyProvider for TypesenseApiKeyProvider {
14    async fn get_api_key(&self) -> Result<String, TypesenseSearchError> {
15        match self {
16            TypesenseApiKeyProvider::ApiKey(value) => value.get_api_key().await,
17            TypesenseApiKeyProvider::Secret(value) => value.get_api_key().await,
18        }
19    }
20}
21
22/// Trait for something that can provide an API key for typesense
23pub(crate) trait ApiKeyProvider {
24    async fn get_api_key(&self) -> Result<String, TypesenseSearchError>;
25}
26
27/// API key from a secret manager that must be loaded at runtime
28pub struct TypesenseApiKeySecret {
29    /// Secret manager access
30    secrets: SecretManager,
31
32    /// Name of the secret the API key is within
33    secret_name: String,
34
35    /// Current loaded value of the secret
36    secret_value: Mutex<Option<String>>,
37}
38
39impl TypesenseApiKeySecret {
40    pub fn new(secrets: SecretManager, secret_name: String) -> Self {
41        Self {
42            secrets,
43            secret_name,
44            secret_value: Default::default(),
45        }
46    }
47}
48
49impl ApiKeyProvider for TypesenseApiKeySecret {
50    async fn get_api_key(&self) -> Result<String, TypesenseSearchError> {
51        let secret_value = &mut *self.secret_value.lock().await;
52        if let Some(value) = secret_value.as_ref() {
53            return Ok(value.clone());
54        }
55
56        match self.secrets.get_secret(&self.secret_name).await {
57            Ok(Some(Secret::String(value))) => Ok(value),
58
59            Ok(Some(Secret::Binary(_))) => {
60                tracing::error!("expected string secret for typesense api key but got binary");
61                Err(TypesenseSearchError::GetSecret)
62            }
63
64            Ok(None) => {
65                tracing::error!(secret_name = ?self.secret_name, "secret not found");
66                Err(TypesenseSearchError::GetSecret)
67            }
68
69            Err(error) => {
70                tracing::error!(?error, "failed to get api key secret");
71                Err(TypesenseSearchError::GetSecret)
72            }
73        }
74    }
75}
76
77/// Known API key string value
78#[derive(Clone, Deserialize, Serialize)]
79#[serde(transparent)]
80pub struct TypesenseApiKey(String);
81
82impl TypesenseApiKey {
83    pub fn new(value: String) -> Self {
84        Self(value)
85    }
86}
87
88// Prevent the API key itself from appearing in logs
89impl Debug for TypesenseApiKey {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        f.write_str("< API KEY >")
92    }
93}
94
95impl ApiKeyProvider for TypesenseApiKey {
96    async fn get_api_key(&self) -> Result<String, TypesenseSearchError> {
97        Ok(self.0.clone())
98    }
99}