use chrono::{DateTime, Utc};
use super::policy::ttl_seconds;
use super::{ValkeyDeviceStore, ValkeyDeviceStoreError};
use crate::device::types::{Device, FingerprintHash};
use crate::session::crypto::BoundFormat;
pub(super) fn device_key(prefix: &str, tenant: &str, id: &str) -> String {
format!("{prefix}:dev:{tenant}:{id}")
}
pub(super) fn row_id(tenant: &str, id: &str) -> Vec<u8> {
let mut out = Vec::with_capacity(tenant.len() + 1 + id.len());
out.extend_from_slice(tenant.as_bytes());
out.push(0);
out.extend_from_slice(id.as_bytes());
out
}
pub(super) fn fingerprint_key(prefix: &str, tenant: &str, hex_hash: &str) -> String {
format!("{prefix}:dev:fp:{tenant}:{hex_hash}")
}
pub(super) fn user_index_key(prefix: &str, tenant: &str, user: &str) -> String {
format!("{prefix}:dev:user:{tenant}:{user}")
}
pub(super) fn family_index_key(prefix: &str, tenant: &str, family: &str) -> String {
format!("{prefix}:dev:fam:{tenant}:{family}")
}
pub(super) fn tenant_index_key(prefix: &str, tenant: &str) -> String {
format!("{prefix}:dev:tenant:{tenant}")
}
pub(super) fn fingerprint_hex(h: &FingerprintHash) -> String {
use std::fmt::Write as _;
let bytes = h.as_bytes();
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
write!(s, "{:02x}", b).expect("writing into a String never fails");
}
s
}
impl ValkeyDeviceStore {
pub(super) fn ttl_seconds_for(&self, device: &Device, now: DateTime<Utc>) -> Option<i64> {
ttl_seconds(&self.sweep_config, device, now)
}
pub(super) fn encode_row(&self, device: &Device) -> Result<Vec<u8>, ValkeyDeviceStoreError> {
let bytes = rmp_serde::to_vec_named(device)?;
match &self.crypto {
Some(c) => Ok(c.seal_bound(
BoundFormat::DEVICE_ROW,
&row_id(&device.tenant_id.to_string(), &device.id.to_string()),
&bytes,
)?),
None => Ok(bytes),
}
}
pub(super) fn decode_row(
&self,
tenant: &str,
id: &str,
payload: &[u8],
) -> Result<Device, ValkeyDeviceStoreError> {
let plaintext = match &self.crypto {
Some(c) => {
let opened = c.open_bound(BoundFormat::DEVICE_ROW, &row_id(tenant, id), payload)?;
if opened.legacy {
tracing::debug!("device row predates row binding; re-sealed on next write");
}
opened.plaintext
}
None => payload.to_vec(),
};
Ok(rmp_serde::from_slice(&plaintext)?)
}
}