1use std::collections::BTreeSet;23use anyhow::Result;4use chrono::{DateTime, Utc};5use nix_eval::{Value, nix_go, nix_go_json};67use crate::fleetdata::FleetSecretData;89#[derive(Debug)]10pub struct Expectations {11 pub owners: BTreeSet<String>,12 pub generation_data: serde_json::Value,13 pub public_parts: BTreeSet<String>,14 pub private_parts: BTreeSet<String>,15}1617pub struct HostSecretDefinition(pub(crate) String, pub(crate) Value);18impl HostSecretDefinition {19 pub fn is_managed(&self) -> Result<bool> {20 let value = &self.1;21 Ok(!nix_go!(value.generator).is_null())22 }23 pub fn expectations(&self) -> Result<Expectations> {24 let value = &self.1;25 Ok(Expectations {26 owners: BTreeSet::from([self.0.clone()]),27 generation_data: nix_go_json!(value.expectedGenerationData),28 public_parts: nix_go_json!(value.expectedPublicParts),29 private_parts: nix_go_json!(value.expectedPrivateParts),30 })31 }32 pub fn inner(&self) -> Value {33 self.1.clone()34 }35}3637pub struct SharedSecretDefinition(pub(crate) Value);38impl SharedSecretDefinition {39 pub fn is_managed(&self) -> Result<bool> {40 let value = &self.0;41 Ok(!nix_go!(value.generator).is_null())42 }43 pub fn expectations(&self) -> Result<Expectations> {44 let value = &self.0;45 Ok(Expectations {46 owners: nix_go_json!(value.expectedOwners),47 generation_data: nix_go_json!(value.expectedGenerationData),48 public_parts: nix_go_json!(value.expectedPublicParts),49 private_parts: nix_go_json!(value.expectedPrivateParts),50 })51 }52 pub fn inner(&self) -> Value {53 self.0.clone()54 }55}5657#[derive(thiserror::Error, Debug)]58pub enum RegenerationReason {59 #[error("owners added: {0:?}")]60 OwnersAdded(BTreeSet<String>),61 #[error("owners added: {0:?}")]62 OwnersRemoved(BTreeSet<String>),63 #[error("unexpected generation data, expected: {expected:?}, found: {found:?}")]64 GenerationData {65 expected: serde_json::Value,66 found: serde_json::Value,67 },68 #[error("unexpected part list, expected: {expected:?}, found: {found:?}")]69 PartList {70 expected: BTreeSet<String>,71 found: BTreeSet<String>,72 },73 #[error("part {0} is expected to be encrypted")]74 ExpectedPrivate(String),75 #[error("part {0} is not expected to be encrypted")]76 ExpectedPublic(String),77 #[error("secret is expired at {0}")]78 Expired(DateTime<Utc>),79}8081pub fn secret_needs_regeneration(82 secret: &FleetSecretData,83 owners: &BTreeSet<String>,84 expectations: &Expectations,85) -> Option<RegenerationReason> {86 if !owners.is_empty() {87 let added: BTreeSet<String> = expectations.owners.difference(owners).cloned().collect();88 if !added.is_empty() {89 return Some(RegenerationReason::OwnersAdded(added));90 }9192 let removed: BTreeSet<String> = owners.difference(&expectations.owners).cloned().collect();93 if !removed.is_empty() {94 return Some(RegenerationReason::OwnersRemoved(removed));95 }96 }9798 if secret.generation_data != expectations.generation_data {99 return Some(RegenerationReason::GenerationData {100 expected: expectations.generation_data.clone(),101 found: secret.generation_data.clone(),102 });103 }104105 if !expectations.public_parts.is_empty() || !expectations.private_parts.is_empty() {106 let expected: BTreeSet<String> = expectations107 .public_parts108 .union(&expectations.private_parts)109 .cloned()110 .collect();111 let found: BTreeSet<String> = secret.parts.keys().cloned().collect();112113 if found != expected {114 return Some(RegenerationReason::PartList { expected, found });115 }116117 for (name, value) in secret.parts.iter() {118 if value.raw.encrypted {119 if !expectations.private_parts.contains(name) {120 return Some(RegenerationReason::ExpectedPrivate(name.clone()));121 }122 } else if !expectations.public_parts.contains(name) {123 return Some(RegenerationReason::ExpectedPublic(name.clone()));124 }125 }126 }127128 if let Some(expiration) = secret.expires_at {129 130 if expiration < Utc::now() {131 return Some(RegenerationReason::Expired(expiration));132 }133 }134135 None136}