Skip to main content

secrets_core/
api_keys.rs

1//! Helpers for store/distributor/billing API key references (opaque refs only; no secrets).
2//!
3//! URIs follow `secrets://<env>/<tenant>/<team|_>/<category>/<name>`:
4//! - Store → Repo API keys: category `store`, name `repo__{repo_ref}__api-key`
5//! - Distributor API keys: category `distributor`, name `{distributor_ref}__api-key`
6//! - Billing provider API keys: category `billing`, name `{billing_provider_id}__api-key`
7
8use crate::embedded::{SecretsCore, SecretsError};
9use crate::spec_compat::SecretUri;
10use crate::types::Scope;
11use greentic_types::{ApiKeyRef, DistributorRef, RepoRef, TenantCtx};
12use tracing::info;
13
14/// Build the canonical URI for a repo API key reference.
15pub fn repo_api_key_uri(tenant: &TenantCtx, repo_ref: &RepoRef) -> Result<SecretUri, SecretsError> {
16    let scope = scope_from_tenant(tenant)?;
17    let name = format!("repo__{}__api-key", repo_ref.as_str());
18    SecretUri::new(scope, "store", name).map_err(SecretsError::from)
19}
20
21/// Build the canonical URI for a distributor API key reference.
22pub fn distributor_api_key_uri(
23    tenant: &TenantCtx,
24    distributor_ref: &DistributorRef,
25) -> Result<SecretUri, SecretsError> {
26    let scope = scope_from_tenant(tenant)?;
27    let name = format!("{}__api-key", distributor_ref.as_str());
28    SecretUri::new(scope, "distributor", name).map_err(SecretsError::from)
29}
30
31/// Build the canonical URI for a billing provider API key reference.
32pub fn billing_api_key_uri(
33    tenant: &TenantCtx,
34    billing_provider_id: &str,
35) -> Result<SecretUri, SecretsError> {
36    let scope = scope_from_tenant(tenant)?;
37    let name = format!("{billing_provider_id}__api-key");
38    SecretUri::new(scope, "billing", name).map_err(SecretsError::from)
39}
40
41/// Retrieve a repo API key reference.
42pub async fn get_repo_api_key_ref(
43    core: &SecretsCore,
44    tenant: &TenantCtx,
45    repo_ref: &RepoRef,
46) -> Result<ApiKeyRef, SecretsError> {
47    let uri = repo_api_key_uri(tenant, repo_ref)?;
48    fetch_api_key_ref(core, tenant, "store", repo_ref.as_str(), &uri).await
49}
50
51/// Retrieve a distributor API key reference.
52pub async fn get_distributor_api_key_ref(
53    core: &SecretsCore,
54    tenant: &TenantCtx,
55    distributor_ref: &DistributorRef,
56) -> Result<ApiKeyRef, SecretsError> {
57    let uri = distributor_api_key_uri(tenant, distributor_ref)?;
58    fetch_api_key_ref(core, tenant, "distributor", distributor_ref.as_str(), &uri).await
59}
60
61/// Retrieve a billing provider API key reference.
62pub async fn get_billing_provider_api_key_ref(
63    core: &SecretsCore,
64    tenant: &TenantCtx,
65    billing_provider_id: &str,
66) -> Result<ApiKeyRef, SecretsError> {
67    let uri = billing_api_key_uri(tenant, billing_provider_id)?;
68    fetch_api_key_ref(core, tenant, "billing", billing_provider_id, &uri).await
69}
70
71async fn fetch_api_key_ref(
72    core: &SecretsCore,
73    tenant: &TenantCtx,
74    category: &str,
75    subject: &str,
76    uri: &SecretUri,
77) -> Result<ApiKeyRef, SecretsError> {
78    let res = core.get_json::<ApiKeyRef>(&uri.to_string()).await;
79    match &res {
80        Ok(value) => info!(
81            tenant = %tenant.tenant_id,
82            team = ?tenant.team,
83            category,
84            subject,
85            api_key_ref = %value.0,
86            "retrieved api key reference",
87        ),
88        Err(err) => info!(
89            tenant = %tenant.tenant_id,
90            team = ?tenant.team,
91            category,
92            subject,
93            error = %err,
94            "failed to retrieve api key reference",
95        ),
96    }
97    res
98}
99
100fn scope_from_tenant(ctx: &TenantCtx) -> Result<Scope, SecretsError> {
101    let env = ctx.env.as_ref();
102    let tenant = ctx.tenant_id.as_ref();
103    let team = ctx
104        .team
105        .as_ref()
106        .or(ctx.team_id.as_ref())
107        .map(|team| team.as_ref().to_string());
108    Scope::new(env, tenant, team).map_err(SecretsError::from)
109}