systemprompt-models 0.2.1

Foundation data models for systemprompt.io AI governance infrastructure. Shared DTOs, config, and domain types consumed by every layer of the MCP governance pipeline.
Documentation
use serde::{Deserialize, Serialize};
use std::str::FromStr;

use super::{
    JwtAudience, Permission, RateLimitTier, TokenType, UserType, parse_permissions,
    permissions_to_string,
};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JwtClaims {
    pub sub: String,
    pub iat: i64,
    pub exp: i64,
    pub iss: String,
    #[serde(
        serialize_with = "serialize_audiences",
        deserialize_with = "deserialize_audiences"
    )]
    pub aud: Vec<JwtAudience>,
    pub jti: String,

    #[serde(
        serialize_with = "serialize_scope",
        deserialize_with = "deserialize_scope"
    )]
    pub scope: Vec<Permission>,

    pub username: String,
    pub email: String,
    pub user_type: UserType,

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub roles: Vec<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_id: Option<String>,
    pub token_type: TokenType,
    pub auth_time: i64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub session_id: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub rate_limit_tier: Option<RateLimitTier>,
}

fn serialize_audiences<S>(auds: &[JwtAudience], s: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    use serde::ser::SerializeSeq;
    let mut seq = s.serialize_seq(Some(auds.len()))?;
    for aud in auds {
        seq.serialize_element(aud.as_str())?;
    }
    seq.end()
}

fn deserialize_audiences<'de, D>(d: D) -> Result<Vec<JwtAudience>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let strings: Vec<String> = Vec::deserialize(d)?;
    strings
        .iter()
        .map(|s| JwtAudience::from_str(s).map_err(serde::de::Error::custom))
        .collect()
}

fn serialize_scope<S>(permissions: &[Permission], s: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    s.serialize_str(&permissions_to_string(permissions))
}

fn deserialize_scope<'de, D>(d: D) -> Result<Vec<Permission>, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let scope_string: String = String::deserialize(d)?;
    parse_permissions(&scope_string).map_err(serde::de::Error::custom)
}

impl JwtClaims {
    pub fn has_permission(&self, permission: Permission) -> bool {
        self.scope.contains(&permission)
    }

    pub fn permissions(&self) -> &[Permission] {
        &self.scope
    }

    pub fn get_permissions(&self) -> Vec<Permission> {
        self.scope.clone()
    }

    pub fn get_scopes(&self) -> Vec<String> {
        self.scope.iter().map(ToString::to_string).collect()
    }

    pub fn is_admin(&self) -> bool {
        self.has_permission(Permission::Admin)
    }

    pub fn is_registered_user(&self) -> bool {
        self.has_permission(Permission::User)
    }

    pub fn is_anonymous(&self) -> bool {
        self.has_permission(Permission::Anonymous)
    }

    pub fn has_audience(&self, aud: &JwtAudience) -> bool {
        self.aud.contains(aud)
    }

    pub fn has_role(&self, role: &str) -> bool {
        self.roles.iter().any(|r| r == role)
    }

    pub fn roles(&self) -> &[String] {
        &self.roles
    }
}