systemprompt_security/authz/
types.rs1use std::fmt;
12use std::str::FromStr;
13
14use serde::{Deserialize, Serialize};
15use systemprompt_identifiers::{RuleId, TraceId, UserId};
16
17use super::error::AuthzError;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
20#[sqlx(type_name = "TEXT", rename_all = "lowercase")]
21#[serde(rename_all = "lowercase")]
22pub enum RuleType {
23 User,
24 Role,
25 Department,
26}
27
28impl fmt::Display for RuleType {
29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30 f.write_str(match *self {
31 Self::User => "user",
32 Self::Role => "role",
33 Self::Department => "department",
34 })
35 }
36}
37
38impl FromStr for RuleType {
39 type Err = AuthzError;
40
41 fn from_str(s: &str) -> Result<Self, Self::Err> {
42 match s {
43 "user" => Ok(Self::User),
44 "role" => Ok(Self::Role),
45 "department" => Ok(Self::Department),
46 other => Err(AuthzError::InvalidRuleType(other.to_owned())),
47 }
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
52#[sqlx(type_name = "TEXT", rename_all = "lowercase")]
53#[serde(rename_all = "lowercase")]
54pub enum Access {
55 Allow,
56 Deny,
57}
58
59impl fmt::Display for Access {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.write_str(match *self {
62 Self::Allow => "allow",
63 Self::Deny => "deny",
64 })
65 }
66}
67
68impl FromStr for Access {
69 type Err = AuthzError;
70
71 fn from_str(s: &str) -> Result<Self, Self::Err> {
72 match s {
73 "allow" => Ok(Self::Allow),
74 "deny" => Ok(Self::Deny),
75 other => Err(AuthzError::InvalidAccess(other.to_owned())),
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
81#[serde(rename_all = "snake_case")]
82pub enum EntityKind {
83 GatewayRoute,
84 McpServer,
85 Plugin,
86 Agent,
87 Marketplace,
88 Skill,
89 Hook,
90}
91
92impl EntityKind {
93 pub const fn as_str(self) -> &'static str {
94 match self {
95 Self::GatewayRoute => "gateway_route",
96 Self::McpServer => "mcp_server",
97 Self::Plugin => "plugin",
98 Self::Agent => "agent",
99 Self::Marketplace => "marketplace",
100 Self::Skill => "skill",
101 Self::Hook => "hook",
102 }
103 }
104}
105
106impl FromStr for EntityKind {
107 type Err = AuthzError;
108
109 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 match s {
111 "gateway_route" => Ok(Self::GatewayRoute),
112 "mcp_server" => Ok(Self::McpServer),
113 "plugin" => Ok(Self::Plugin),
114 "agent" => Ok(Self::Agent),
115 "marketplace" => Ok(Self::Marketplace),
116 "skill" => Ok(Self::Skill),
117 "hook" => Ok(Self::Hook),
118 other => Err(AuthzError::Validation(format!(
119 "unknown entity_type: {other}"
120 ))),
121 }
122 }
123}
124
125impl fmt::Display for EntityKind {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 f.write_str(self.as_str())
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
132pub struct AccessRule {
133 pub id: RuleId,
134 pub rule_type: RuleType,
135 pub rule_value: String,
136 pub access: Access,
137 pub default_included: bool,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
139 pub justification: Option<String>,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
143#[serde(tag = "decision", rename_all = "lowercase")]
144pub enum Decision {
145 Allow,
146 Deny {
147 reason: String,
148 #[serde(default, skip_serializing_if = "Option::is_none")]
149 justification: Option<String>,
150 },
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct AuthzRequest {
155 pub entity_type: EntityKind,
156 pub entity_id: String,
157 pub user_id: UserId,
158 #[serde(default)]
159 pub roles: Vec<String>,
160 #[serde(default)]
161 pub department: String,
162 pub trace_id: TraceId,
163 #[serde(default)]
164 pub context: serde_json::Value,
167}
168
169#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
170#[serde(tag = "decision", rename_all = "lowercase")]
171pub enum AuthzDecision {
172 Allow,
173 Deny { reason: String, policy: String },
174}