greentic_deploy_spec/
credentials.rs1use crate::capability_slot::PackDescriptor;
7use crate::error::SpecError;
8use crate::refs::SecretRef;
9use crate::version::SchemaVersion;
10use chrono::{DateTime, Utc};
11use greentic_types::EnvId;
12use serde::{Deserialize, Serialize};
13use std::path::PathBuf;
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(rename_all = "lowercase")]
17pub enum CredentialsMode {
18 Requirements,
19 Bootstrap,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(rename_all = "lowercase")]
24pub enum CredentialsValidationResult {
25 Pass,
26 Fail,
27}
28
29#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
30pub struct CredentialsValidation {
31 pub last_run_at: DateTime<Utc>,
32 pub result: CredentialsValidationResult,
33 #[serde(default)]
34 pub missing_capabilities: Vec<String>,
35}
36
37#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
38pub struct CredentialsBootstrap {
39 pub admin_credential_consumed_at: DateTime<Utc>,
40 pub rules_pack_ref: PathBuf,
42 pub generated_credentials_ref: SecretRef,
43}
44
45#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
46pub struct CredentialsExpiry {
47 pub expires_at: DateTime<Utc>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
49 pub rotate_at: Option<DateTime<Utc>>,
50}
51
52#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
53pub struct Credentials {
54 pub schema: SchemaVersion,
55 pub env_id: EnvId,
56 pub deployer_kind: PackDescriptor,
57 pub mode: CredentialsMode,
58 pub provided_credentials_ref: SecretRef,
59 pub validation: CredentialsValidation,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
62 pub bootstrap: Option<CredentialsBootstrap>,
63 #[serde(default, skip_serializing_if = "Option::is_none")]
64 pub expiry: Option<CredentialsExpiry>,
65}
66
67impl Credentials {
68 pub fn schema_str() -> &'static str {
69 SchemaVersion::CREDENTIALS_V1
70 }
71
72 pub fn validate(&self) -> Result<(), SpecError> {
78 if self.schema.as_str() != SchemaVersion::CREDENTIALS_V1 {
79 return Err(SpecError::SchemaMismatch {
80 expected: SchemaVersion::CREDENTIALS_V1,
81 actual: self.schema.as_str().to_string(),
82 });
83 }
84 check_secret_ref_env(
85 "credentials.provided_credentials_ref",
86 &self.provided_credentials_ref,
87 &self.env_id,
88 )?;
89 if let Some(bootstrap) = &self.bootstrap {
90 check_secret_ref_env(
91 "credentials.bootstrap.generated_credentials_ref",
92 &bootstrap.generated_credentials_ref,
93 &self.env_id,
94 )?;
95 }
96 Ok(())
97 }
98}
99
100fn check_secret_ref_env(
101 context: &'static str,
102 secret_ref: &SecretRef,
103 expected_env: &EnvId,
104) -> Result<(), SpecError> {
105 let actual = secret_ref.env_segment();
106 if actual != expected_env.as_str() {
107 return Err(SpecError::CrossEnvRef {
108 context,
109 uri: secret_ref.as_str().to_string(),
110 expected_env: expected_env.clone(),
111 actual_env: actual.to_string(),
112 });
113 }
114 Ok(())
115}