git.delta.rocks / jrsonnet / refs/commits / b00d46da7979

difftreelog

source

crates/fleet-base/src/secret.rs4.3 KiBsourcehistory
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 def = self.definition_value()?;21		Ok(!nix_go!(def.generator).is_null())22	}23	pub fn is_shared(&self) -> Result<bool> {24		let def = self.definition_value()?;25		Ok(nix_go_json!(def.shared))26	}27	pub fn expectations(&self) -> Result<Expectations> {28		let def = self.definition_value()?;29		let parts = nix_go!(def.parts);3031		let mut public_parts = BTreeSet::new();32		let mut private_parts = BTreeSet::new();33		for part in parts.list_fields()? {34			if nix_go_json!(parts[&part].encrypted) {35				private_parts.insert(part.clone());36			} else {37				public_parts.insert(part.clone());38			}39		}4041		Ok(Expectations {42			owners: BTreeSet::from([self.0.clone()]),43			generation_data: nix_go_json!(def.expectedGenerationData),44			public_parts,45			private_parts,46		})47	}48	pub fn definition_value(&self) -> Result<Value> {49		let value = &self.1;50		Ok(nix_go!(value.definition))51	}52}5354pub struct SharedSecretDefinition(pub(crate) Value);55impl SharedSecretDefinition {56	pub fn is_managed(&self) -> Result<bool> {57		let value = &self.0;58		Ok(!nix_go!(value.generator).is_null())59	}60	pub fn expectations(&self) -> Result<Expectations> {61		let value = &self.0;62		Ok(Expectations {63			owners: nix_go_json!(value.expectedOwners),64			generation_data: nix_go_json!(value.expectedGenerationData),65			public_parts: nix_go_json!(value.expectedPublicParts),66			private_parts: nix_go_json!(value.expectedPrivateParts),67		})68	}69	pub fn definition_value(&self) -> Value {70		self.0.clone()71	}72}7374#[derive(thiserror::Error, Debug)]75pub enum RegenerationReason {76	#[error("owners added: {0:?}")]77	OwnersAdded(BTreeSet<String>),78	#[error("owners added: {0:?}")]79	OwnersRemoved(BTreeSet<String>),80	#[error("unexpected generation data, expected: {expected:?}, found: {found:?}")]81	GenerationData {82		expected: serde_json::Value,83		found: serde_json::Value,84	},85	#[error("unexpected part list, expected: {expected:?}, found: {found:?}")]86	PartList {87		expected: BTreeSet<String>,88		found: BTreeSet<String>,89	},90	#[error("part {0} is expected to be encrypted")]91	ExpectedPrivate(String),92	#[error("part {0} is not expected to be encrypted")]93	ExpectedPublic(String),94	#[error("secret is expired at {0}")]95	Expired(DateTime<Utc>),96}9798pub fn secret_needs_regeneration(99	secret: &FleetSecretData,100	owners: &BTreeSet<String>,101	expectations: &Expectations,102) -> Option<RegenerationReason> {103	if !owners.is_empty() {104		let added: BTreeSet<String> = expectations.owners.difference(owners).cloned().collect();105		if !added.is_empty() {106			return Some(RegenerationReason::OwnersAdded(added));107		}108109		let removed: BTreeSet<String> = owners.difference(&expectations.owners).cloned().collect();110		if !removed.is_empty() {111			return Some(RegenerationReason::OwnersRemoved(removed));112		}113	}114115	if secret.generation_data != expectations.generation_data {116		return Some(RegenerationReason::GenerationData {117			expected: expectations.generation_data.clone(),118			found: secret.generation_data.clone(),119		});120	}121122	if !expectations.public_parts.is_empty() || !expectations.private_parts.is_empty() {123		let expected: BTreeSet<String> = expectations124			.public_parts125			.union(&expectations.private_parts)126			.cloned()127			.collect();128		let found: BTreeSet<String> = secret.parts.keys().cloned().collect();129130		if found != expected {131			return Some(RegenerationReason::PartList { expected, found });132		}133134		for (name, value) in secret.parts.iter() {135			if value.raw.encrypted {136				if !expectations.private_parts.contains(name) {137					return Some(RegenerationReason::ExpectedPrivate(name.clone()));138				}139			} else if !expectations.public_parts.contains(name) {140				return Some(RegenerationReason::ExpectedPublic(name.clone()));141			}142		}143	}144145	if let Some(expiration) = secret.expires_at {146		// TODO: Leeway?147		if expiration < Utc::now() {148			return Some(RegenerationReason::Expired(expiration));149		}150	}151152	None153}