use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use systemprompt_identifiers::{CallId, PolicyId, SessionId, UserId};
use super::governed::{GovernedInput, GovernedTarget};
use crate::authz::error::AuthzError;
use crate::authz::types::Decision;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SecretLocation {
pub kind: String,
pub path: String,
pub redacted: String,
}
impl SecretLocation {
pub fn new(
kind: impl Into<String>,
path: impl Into<String>,
redacted: impl Into<String>,
) -> Self {
Self {
kind: kind.into(),
path: path.into(),
redacted: redacted.into(),
}
}
}
impl fmt::Display for SecretLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.path.is_empty() {
write!(f, "{} ({})", self.kind, self.redacted)
} else {
write!(f, "{}.{} ({})", self.kind, self.path, self.redacted)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RateLimitWindow {
pub name: String,
pub seconds: u64,
pub limit: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum AgentScope {
User { user_id: UserId },
System,
}
impl AgentScope {
#[must_use]
pub const fn user_id(&self) -> Option<&UserId> {
match self {
Self::User { user_id } => Some(user_id),
Self::System => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "TEXT", rename_all = "lowercase")]
#[serde(rename_all = "lowercase")]
pub enum AccessScope {
Admin,
User,
Unknown,
}
impl AccessScope {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Admin => "admin",
Self::User => "user",
Self::Unknown => "unknown",
}
}
}
impl fmt::Display for AccessScope {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for AccessScope {
type Err = AuthzError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"admin" => Ok(Self::Admin),
"user" => Ok(Self::User),
"unknown" | "" => Ok(Self::Unknown),
other => Err(AuthzError::Validation(format!(
"unknown access scope: {other}"
))),
}
}
}
#[derive(Debug)]
pub struct PolicyContext<'a> {
pub target: GovernedTarget,
pub agent_scope: AgentScope,
pub access_scope: AccessScope,
pub session_id: &'a SessionId,
pub user_id: &'a UserId,
pub input: &'a GovernedInput,
pub call_id: &'a CallId,
}
pub trait GovernancePolicy: Send + Sync + fmt::Debug {
fn id(&self) -> PolicyId;
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
fn evaluate(&self, ctx: &PolicyContext<'_>) -> Decision;
}