use std::collections::HashSet;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NodeApiPermission {
#[serde(rename = "all")]
All,
#[serde(rename = "node.health_check")]
NodeHealthCheck,
#[serde(rename = "workload.read")]
WorkloadRead,
#[serde(rename = "workload.write")]
WorkloadWrite,
#[serde(rename = "confdb.entity.read")]
ConfDbEntityRead,
#[serde(rename = "confdb.entity.delete")]
ConfDbEntityDelete,
#[serde(rename = "tls_cert.read")]
TlsCertRead,
#[serde(rename = "tls_cert.delete")]
TlsCertDelete,
#[serde(rename = "dns_cache.read")]
DnsCacheRead,
#[serde(rename = "dns_cache.write")]
DnsCacheWrite,
#[serde(rename = "platform_config.read")]
PlatformConfigRead,
#[serde(rename = "api_token.generate")]
ApiTokenGenerate,
#[serde(rename = "job.read")]
JobRead,
#[serde(rename = "cluster_health.read")]
ClusterHealthRead,
#[serde(rename = "module_cache.read")]
ModuleCacheRead,
#[serde(rename = "module_cache.write")]
ModuleCacheWrite,
}
impl NodeApiPermission {
fn as_str(self) -> &'static str {
match self {
Self::All => "all",
Self::NodeHealthCheck => "node.health_check",
Self::WorkloadRead => "workload.read",
Self::WorkloadWrite => "workload.write",
Self::ConfDbEntityRead => "confdb.entity.read",
Self::ConfDbEntityDelete => "confdb.entity.delete",
Self::TlsCertRead => "tls_cert.read",
Self::TlsCertDelete => "tls_cert.delete",
Self::DnsCacheRead => "dns_cache.read",
Self::DnsCacheWrite => "dns_cache.write",
Self::PlatformConfigRead => "platform_config.read",
Self::ApiTokenGenerate => "api_token.generate",
Self::JobRead => "job.read",
Self::ClusterHealthRead => "cluster_health.read",
Self::ModuleCacheRead => "module_cache.read",
Self::ModuleCacheWrite => "module_cache.write",
}
}
}
impl std::str::FromStr for NodeApiPermission {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"all" => Ok(NodeApiPermission::All),
"node.health_check" => Ok(NodeApiPermission::NodeHealthCheck),
"workload.read" => Ok(NodeApiPermission::WorkloadRead),
"workload.write" => Ok(NodeApiPermission::WorkloadWrite),
"confdb.entity.read" => Ok(NodeApiPermission::ConfDbEntityRead),
"confdb.entity.delete" => Ok(NodeApiPermission::ConfDbEntityDelete),
"tls_cert.read" => Ok(NodeApiPermission::TlsCertRead),
"tls_cert.delete" => Ok(NodeApiPermission::TlsCertDelete),
"dns_cache.read" => Ok(NodeApiPermission::DnsCacheRead),
"dns_cache.write" => Ok(NodeApiPermission::DnsCacheWrite),
"platform_config.read" => Ok(NodeApiPermission::PlatformConfigRead),
"api_token.generate" => Ok(NodeApiPermission::ApiTokenGenerate),
"job.read" => Ok(NodeApiPermission::JobRead),
"cluster_health.read" => Ok(NodeApiPermission::ClusterHealthRead),
"module_cache.read" => Ok(NodeApiPermission::ModuleCacheRead),
"module_cache.write" => Ok(NodeApiPermission::ModuleCacheWrite),
_ => Err(anyhow::anyhow!("invalid node api permission: {}", s)),
}
}
}
impl std::fmt::Display for NodeApiPermission {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct BaseClaims {
#[serde(with = "time::serde::timestamp")]
pub exp: OffsetDateTime,
#[serde(with = "time::serde::timestamp")]
pub iat: OffsetDateTime,
pub sub: String,
pub node_api_permissions: Option<HashSet<NodeApiPermission>>,
}
impl BaseClaims {
pub fn has_node_api_permission(&self, perm: NodeApiPermission) -> bool {
match &self.node_api_permissions {
Some(perms) => perms.contains(&perm) || perms.contains(&NodeApiPermission::All),
None => true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_base_claims_has_permission() {
let c = BaseClaims {
exp: OffsetDateTime::now_utc(),
iat: OffsetDateTime::now_utc(),
sub: "test".to_string(),
node_api_permissions: Some(
vec![
NodeApiPermission::WorkloadRead,
NodeApiPermission::ConfDbEntityRead,
]
.into_iter()
.collect(),
),
};
assert!(c.has_node_api_permission(NodeApiPermission::WorkloadRead));
assert!(c.has_node_api_permission(NodeApiPermission::ConfDbEntityRead));
assert!(!c.has_node_api_permission(NodeApiPermission::WorkloadWrite));
assert!(!c.has_node_api_permission(NodeApiPermission::All));
let c = BaseClaims {
node_api_permissions: Some(vec![NodeApiPermission::All].into_iter().collect()),
..c
};
assert!(c.has_node_api_permission(NodeApiPermission::WorkloadRead));
assert!(c.has_node_api_permission(NodeApiPermission::ConfDbEntityRead));
assert!(c.has_node_api_permission(NodeApiPermission::All));
let c = BaseClaims {
node_api_permissions: None,
..c
};
assert!(c.has_node_api_permission(NodeApiPermission::WorkloadRead));
assert!(c.has_node_api_permission(NodeApiPermission::ConfDbEntityRead));
assert!(c.has_node_api_permission(NodeApiPermission::All));
}
}