use chrono::{DateTime, Utc};
use geekorm::{Connection, prelude::*};
use log::debug;
use serde::{Deserialize, Serialize};
use super::Snapshot;
#[derive(Table, Debug, Default, Clone, Serialize, Deserialize)]
pub struct SnapshotMetadata {
#[geekorm(primary_key, auto_increment)]
pub id: PrimaryKey<i32>,
#[geekorm(foreign_key = "Snapshot.id")]
pub snapshot_id: ForeignKey<u64, Snapshot>,
pub key: SnapshotMetadataKey,
pub value: Vec<u8>,
#[geekorm(new = "Utc::now()")]
pub created_at: DateTime<Utc>,
#[geekorm(new = "Utc::now()")]
pub updated_at: DateTime<Utc>,
}
impl SnapshotMetadata {
pub async fn init(connection: &Connection<'_>) -> Result<(), crate::KonarrError> {
log::debug!("Initialising Snapshot Metadata Model");
let all = match Self::all(connection).await {
Ok(all) => all,
Err(e) => {
log::error!("Failed to get all metadata: {:?}", e);
log::error!("Please report this error to the Konarr team");
return Err(crate::KonarrError::GeekOrm(e));
}
};
log::debug!("Found {} metadata entries", all.len());
Ok(())
}
pub async fn update_or_create(
connection: &Connection<'_>,
snapshot: impl Into<PrimaryKey<i32>>,
key: &SnapshotMetadataKey,
value: impl Into<Vec<u8>>,
) -> Result<Self, crate::KonarrError> {
let snapshot = snapshot.into();
let value = value.into();
debug!("Updating Metadata for Snapshot({:?}) :: {} ", snapshot, key);
Ok(match Self::find_by_key(connection, snapshot, &key).await {
Ok(Some(mut meta)) => {
meta.value = value;
meta.updated_at = chrono::Utc::now();
meta.update(connection).await?;
meta
}
_ => Self::add(connection, snapshot, key, value).await?,
})
}
pub async fn add(
connection: &Connection<'_>,
snapshot: impl Into<PrimaryKey<i32>>,
key: &SnapshotMetadataKey,
value: impl Into<Vec<u8>>,
) -> Result<Self, crate::KonarrError> {
let snapshot = snapshot.into();
debug!("Adding Metadata to Snapshot({:?}) :: {} ", snapshot, key);
let mut meta = Self::new(snapshot, key.clone(), value.into());
meta.save(connection).await?;
Ok(meta)
}
pub async fn find_by_key(
connection: &Connection<'_>,
snapshot: impl Into<PrimaryKey<i32>>,
key: &SnapshotMetadataKey,
) -> Result<Option<Self>, crate::KonarrError> {
let snapshot = snapshot.into();
Ok(Some(
Self::query_first(
connection,
Self::query_select()
.where_eq("snapshot_id", snapshot)
.and()
.where_eq("key", key)
.build()?,
)
.await?,
))
}
pub async fn find_by_sha(
connection: &Connection<'_>,
sha: impl Into<String>,
) -> Result<Option<Self>, crate::KonarrError> {
let sha = sha.into();
if sha.is_empty() {
return Ok(None);
}
Ok(Some(
Self::query_first(
connection,
Self::query_select()
.where_eq("key", SnapshotMetadataKey::BomSha)
.and()
.where_eq("value", sha)
.build()?,
)
.await?,
))
}
pub fn as_string(&self) -> String {
std::str::from_utf8(&self.value).unwrap().to_string()
}
pub fn as_bool(&self) -> bool {
self.as_string().parse().unwrap_or_default()
}
pub fn as_i32(&self) -> i32 {
self.as_string().parse().unwrap_or_default()
}
pub fn as_u32(&self) -> u32 {
self.as_string().parse().unwrap_or_default()
}
}
#[derive(Data, Debug, Default, Clone, Hash, Eq, PartialEq)]
#[allow(missing_docs)]
pub enum SnapshotMetadataKey {
#[geekorm(key = "rescan")]
Rescan,
#[geekorm(key = "os")]
Os,
#[geekorm(key = "os.version")]
OsVersion,
#[geekorm(key = "os.arch")]
OsArch,
#[geekorm(key = "os.variant")]
OsVariant,
#[geekorm(key = "os.kernel")]
OsKernel,
#[geekorm(key = "os.distro")]
OsDistro,
#[geekorm(key = "os.distro.version")]
OsDistroVersion,
#[geekorm(key = "container.engine")]
ContainerEngine,
#[geekorm(key = "container.engine.version")]
ContainerEngineVersion,
#[geekorm(key = "container")]
Container,
#[geekorm(key = "container.image")]
ContainerImage,
#[geekorm(key = "container.sha")]
ContainerSha,
#[geekorm(key = "container.version")]
ContainerVersion,
#[geekorm(key = "container.created")]
ContainerCreated,
#[geekorm(key = "container.image.created")]
ContainerImageCreated,
#[geekorm(key = "container.image.history")]
ContainerImageHistory,
#[geekorm(key = "container.description")]
ContainerDescription,
#[geekorm(key = "container.url")]
ContainerUrl,
#[geekorm(key = "container.licenses")]
ContainerLicenses,
#[geekorm(key = "container.authors")]
ContainerAuthor,
#[geekorm(key = "bom.type")]
BomType,
#[geekorm(key = "bom.version")]
BomVersion,
#[geekorm(key = "bom.sha")]
BomSha,
#[geekorm(key = "bom.tool")]
BomTool,
#[geekorm(key = "bom.tool.name")]
BomToolName,
#[geekorm(key = "bom.tool.version")]
BomToolVersion,
#[geekorm(key = "bom.path")]
BomPath,
#[geekorm(key = "dependencies.total", aliases = "bom.dependencies.count")]
DependenciesTotal,
#[geekorm(key = "security.tools.alerts")]
SecurityToolsAlerts,
#[geekorm(
key = "security.alerts.total",
aliases = "security.total.count,security.counts.total"
)]
SecurityAlertTotal,
#[geekorm(
key = "security.alerts.critical",
aliases = "security.critical.count,security.counts.critical"
)]
SecurityAlertCritical,
#[geekorm(
key = "security.alerts.high",
aliases = "security.high.count,security.counts.high"
)]
SecurityAlertHigh,
#[geekorm(
key = "security.alerts.medium",
aliases = "security.medium.count,security.counts.medium"
)]
SecurityAlertMedium,
#[geekorm(
key = "security.alerts.low",
aliases = "security.low.count,security.counts.low"
)]
SecurityAlertLow,
#[geekorm(
key = "security.alerts.informational",
aliases = "security.informational.count,security.counts.informational"
)]
SecurityAlertInformational,
#[geekorm(
key = "security.alerts.unmaintained",
aliases = "security.unmaintained.count"
)]
SecurityAlertUnmaintained,
#[geekorm(
key = "security.alerts.malware",
aliases = "security.malware.count,security.counts.malware"
)]
SecurityAlertMalware,
#[geekorm(
key = "security.alerts.unknown",
aliases = "security.unknown.count,security.counts.unknown"
)]
SecurityAlertUnknown,
#[geekorm(key = "unknown")]
#[default]
Unknown,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_keys() {
let key = SnapshotMetadataKey::from("os");
assert_eq!(key, SnapshotMetadataKey::Os);
let keys = vec!["security.critical.count", "security.counts.critical"];
for key in keys {
assert_eq!(
SnapshotMetadataKey::from(key),
SnapshotMetadataKey::SecurityAlertCritical
);
}
}
}