use serde::{Deserialize, Serialize};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::storage::{PermissionLevel, TokenScope};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
pub exp: u64,
pub iat: u64,
pub iss: String,
#[serde(default)]
pub roles: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub node_id: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub scopes: Vec<TokenScope>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub jti: Option<String>,
}
impl Claims {
pub fn new(
subject: impl Into<String>,
expiry: Duration,
roles: Vec<String>,
email: Option<String>,
) -> Self {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock before Unix epoch")
.as_secs();
Self {
sub: subject.into(),
exp: now + expiry.as_secs(),
iat: now,
iss: "zlayer".to_string(),
roles,
email,
node_id: None,
scopes: Vec::new(),
jti: None,
}
}
pub fn new_scoped(
subject: impl Into<String>,
expiry: Duration,
roles: Vec<String>,
scopes: Vec<TokenScope>,
jti: impl Into<String>,
) -> Self {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock before Unix epoch")
.as_secs();
Self {
sub: subject.into(),
exp: now + expiry.as_secs(),
iat: now,
iss: "zlayer".to_string(),
roles,
email: None,
node_id: None,
scopes,
jti: Some(jti.into()),
}
}
#[must_use]
pub fn is_expired(&self) -> bool {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock before Unix epoch")
.as_secs();
self.exp < now
}
#[must_use]
pub fn has_role(&self, role: &str) -> bool {
self.roles.iter().any(|r| r == role || r == "admin")
}
#[must_use]
pub fn is_scoped(&self) -> bool {
!self.scopes.is_empty()
}
#[must_use]
pub fn satisfies(
&self,
resource_kind: &str,
resource_id: Option<&str>,
level: PermissionLevel,
) -> bool {
if self.roles.iter().any(|r| r == "admin") {
return true;
}
self.scopes
.iter()
.any(|s| s.satisfies(resource_kind, resource_id, level))
}
}