systemprompt_security/authz/types/
kinds.rs1use std::borrow::Cow;
7use std::fmt;
8use std::str::FromStr;
9
10use serde::{Deserialize, Serialize};
11
12use crate::authz::error::AuthzError;
13
14#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
31#[serde(transparent)]
32pub struct RuleType(Cow<'static, str>);
33
34impl RuleType {
35 pub const USER: Self = Self(Cow::Borrowed("user"));
37 pub const ROLE: Self = Self(Cow::Borrowed("role"));
39
40 pub fn extension(slug: impl Into<Cow<'static, str>>) -> Result<Self, AuthzError> {
49 let slug = slug.into();
50 let well_formed = !slug.is_empty()
51 && !slug.starts_with('_')
52 && !slug.ends_with('_')
53 && slug
54 .chars()
55 .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_');
56 if !well_formed || slug == Self::USER.as_str() || slug == Self::ROLE.as_str() {
57 return Err(AuthzError::InvalidRuleType(slug.into_owned()));
58 }
59 Ok(Self(slug))
60 }
61
62 #[must_use]
63 pub fn as_str(&self) -> &str {
64 &self.0
65 }
66}
67
68impl fmt::Display for RuleType {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 f.write_str(self.as_str())
71 }
72}
73
74impl From<&str> for RuleType {
75 fn from(s: &str) -> Self {
80 match s {
81 "user" => Self::USER,
82 "role" => Self::ROLE,
83 other => Self(Cow::Owned(other.to_owned())),
84 }
85 }
86}
87
88impl sqlx::Type<sqlx::Postgres> for RuleType {
89 fn type_info() -> <sqlx::Postgres as sqlx::Database>::TypeInfo {
90 <str as sqlx::Type<sqlx::Postgres>>::type_info()
91 }
92
93 fn compatible(ty: &<sqlx::Postgres as sqlx::Database>::TypeInfo) -> bool {
94 <str as sqlx::Type<sqlx::Postgres>>::compatible(ty)
95 }
96}
97
98impl<'q> sqlx::Encode<'q, sqlx::Postgres> for RuleType {
99 fn encode_by_ref(
100 &self,
101 buf: &mut <sqlx::Postgres as sqlx::Database>::ArgumentBuffer,
102 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
103 <&str as sqlx::Encode<'q, sqlx::Postgres>>::encode(self.as_str(), buf)
104 }
105}
106
107impl<'r> sqlx::Decode<'r, sqlx::Postgres> for RuleType {
108 fn decode(
109 value: <sqlx::Postgres as sqlx::Database>::ValueRef<'r>,
110 ) -> Result<Self, sqlx::error::BoxDynError> {
111 let raw = <&str as sqlx::Decode<'r, sqlx::Postgres>>::decode(value)?;
112 Ok(Self::from(raw))
113 }
114}
115
116#[derive(
117 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, sqlx::Type,
118)]
119#[sqlx(type_name = "TEXT", rename_all = "lowercase")]
120#[serde(rename_all = "lowercase")]
121pub enum Access {
122 Allow,
123 Deny,
124}
125
126impl fmt::Display for Access {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 f.write_str(match *self {
129 Self::Allow => "allow",
130 Self::Deny => "deny",
131 })
132 }
133}
134
135impl FromStr for Access {
136 type Err = AuthzError;
137
138 fn from_str(s: &str) -> Result<Self, Self::Err> {
139 match s {
140 "allow" => Ok(Self::Allow),
141 "deny" => Ok(Self::Deny),
142 other => Err(AuthzError::InvalidAccess(other.to_owned())),
143 }
144 }
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
148#[serde(rename_all = "snake_case")]
149pub enum EntityKind {
150 GatewayRoute,
151 McpServer,
152 Plugin,
153 Agent,
154 Marketplace,
155 Skill,
156 Hook,
157 SlackWorkspace,
158 SlackChannel,
159 TeamsTenant,
160 TeamsConversation,
161}
162
163impl EntityKind {
164 pub const fn as_str(self) -> &'static str {
165 match self {
166 Self::GatewayRoute => "gateway_route",
167 Self::McpServer => "mcp_server",
168 Self::Plugin => "plugin",
169 Self::Agent => "agent",
170 Self::Marketplace => "marketplace",
171 Self::Skill => "skill",
172 Self::Hook => "hook",
173 Self::SlackWorkspace => "slack_workspace",
174 Self::SlackChannel => "slack_channel",
175 Self::TeamsTenant => "teams_tenant",
176 Self::TeamsConversation => "teams_conversation",
177 }
178 }
179}
180
181impl FromStr for EntityKind {
182 type Err = AuthzError;
183
184 fn from_str(s: &str) -> Result<Self, Self::Err> {
185 match s {
186 "gateway_route" => Ok(Self::GatewayRoute),
187 "mcp_server" => Ok(Self::McpServer),
188 "plugin" => Ok(Self::Plugin),
189 "agent" => Ok(Self::Agent),
190 "marketplace" => Ok(Self::Marketplace),
191 "skill" => Ok(Self::Skill),
192 "hook" => Ok(Self::Hook),
193 "slack_workspace" => Ok(Self::SlackWorkspace),
194 "slack_channel" => Ok(Self::SlackChannel),
195 "teams_tenant" => Ok(Self::TeamsTenant),
196 "teams_conversation" => Ok(Self::TeamsConversation),
197 other => Err(AuthzError::Validation(format!(
198 "unknown entity_type: {other}"
199 ))),
200 }
201 }
202}
203
204impl fmt::Display for EntityKind {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 f.write_str(self.as_str())
207 }
208}