use crate::data::{JsonObject, JsonValue, Optional, Patch};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
pub type AccessPolicyExtensions = BTreeMap<String, JsonValue>;
#[derive(Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Debug)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum AccessPolicyEffect {
Allow,
Deny,
}
#[derive(Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Debug)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum AccessPolicySubjectKind {
User,
Group,
Agent,
}
#[derive(Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccessPolicySubject {
#[serde(rename = "type")]
pub kind: AccessPolicySubjectKind,
pub id: String,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
impl AccessPolicySubject {
pub fn new(kind: AccessPolicySubjectKind, id: impl Into<String>) -> Self {
Self {
kind,
id: id.into(),
extra: BTreeMap::new(),
}
}
}
#[derive(Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum AccessPolicySelection {
All,
Names(Vec<String>),
}
impl Serialize for AccessPolicySelection {
fn serialize<S: serde::Serializer>(&self, s: S) -> std::result::Result<S::Ok, S::Error> {
match self {
Self::All => s.serialize_str("ALL"),
Self::Names(v) => v.serialize(s),
}
}
}
impl<'de> Deserialize<'de> for AccessPolicySelection {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
let value = serde_json::Value::deserialize(d)?;
match value {
serde_json::Value::String(s) if s == "ALL" => Ok(Self::All),
serde_json::Value::Array(v) if !v.is_empty() => v
.into_iter()
.map(|x| {
x.as_str()
.filter(|s| !s.is_empty())
.map(str::to_owned)
.ok_or_else(|| serde::de::Error::custom("invalid selection"))
})
.collect::<std::result::Result<Vec<_>, D::Error>>()
.map(Self::Names),
_ => Err(serde::de::Error::custom("invalid selection")),
}
}
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
#[non_exhaustive]
pub enum AccessPolicyTarget {
#[serde(rename = "mcp_server")]
McpServer {
server_id: String,
tools: AccessPolicySelection,
#[serde(flatten)]
extra: AccessPolicyExtensions,
},
#[serde(rename = "provider")]
Provider {
provider_id: String,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
models: Optional<AccessPolicySelection>,
#[serde(flatten)]
extra: AccessPolicyExtensions,
},
}
impl AccessPolicyTarget {
pub fn mcp_server(id: impl Into<String>, tools: AccessPolicySelection) -> Self {
Self::McpServer {
server_id: id.into(),
tools,
extra: BTreeMap::new(),
}
}
pub fn provider(id: impl Into<String>) -> Self {
Self::Provider {
provider_id: id.into(),
models: Optional::Unset,
extra: BTreeMap::new(),
}
}
}
#[derive(Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CreateAccessPolicy {
pub name: String,
pub subjects: Vec<AccessPolicySubject>,
pub targets: Vec<AccessPolicyTarget>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub workspace_id: Optional<String>,
#[serde(default, skip_serializing_if = "Patch::is_unset")]
pub description: Patch<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub effect: Optional<AccessPolicyEffect>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub enabled: Optional<bool>,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
impl CreateAccessPolicy {
pub fn new(
name: impl Into<String>,
subjects: Vec<AccessPolicySubject>,
targets: Vec<AccessPolicyTarget>,
) -> Self {
Self {
name: name.into(),
subjects,
targets,
workspace_id: Optional::Unset,
description: Patch::Unset,
effect: Optional::Unset,
enabled: Optional::Unset,
extra: BTreeMap::new(),
}
}
}
#[derive(Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct UpdateAccessPolicy {
pub version: u64,
pub subjects: Vec<AccessPolicySubject>,
pub targets: Vec<AccessPolicyTarget>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub name: Optional<String>,
#[serde(default, skip_serializing_if = "Patch::is_unset")]
pub description: Patch<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub effect: Optional<AccessPolicyEffect>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub enabled: Optional<bool>,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
impl UpdateAccessPolicy {
pub fn new(
version: u64,
subjects: Vec<AccessPolicySubject>,
targets: Vec<AccessPolicyTarget>,
) -> Self {
Self {
version,
subjects,
targets,
name: Optional::Unset,
description: Patch::Unset,
effect: Optional::Unset,
enabled: Optional::Unset,
extra: BTreeMap::new(),
}
}
}
#[derive(Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccessPolicyListOptions {
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub workspace_id: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub effect: Optional<AccessPolicyEffect>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub enabled: Optional<bool>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub user_id: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub agent_id: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub server_id: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub search: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub created_by: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub start_time: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub end_time: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub page_size: Optional<u64>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub current_page: Optional<u64>,
}
#[derive(Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccessPolicySubjectDetails {
#[serde(rename = "type", default, skip_serializing_if = "Optional::is_unset")]
pub kind: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub id: Optional<String>,
#[serde(default, skip_serializing_if = "Patch::is_unset")]
pub name: Patch<String>,
#[serde(default, skip_serializing_if = "Patch::is_unset")]
pub email: Patch<String>,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
crate::redaction::redacted_debug!(AccessPolicySubjectDetails);
#[derive(Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccessPolicyCreated {
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub id: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub object: Optional<String>,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
crate::redaction::redacted_debug!(AccessPolicyCreated);
#[derive(Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccessPolicy {
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub id: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub name: Optional<String>,
#[serde(default, skip_serializing_if = "Patch::is_unset")]
pub description: Patch<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub enabled: Optional<bool>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub effect: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub subjects: Optional<Vec<AccessPolicySubjectDetails>>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub targets: Optional<Vec<AccessPolicyTargetDetails>>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub version: Optional<i64>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub status: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub workspace_id: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub organisation_id: Optional<String>,
#[serde(default, skip_serializing_if = "Patch::is_unset")]
pub created_by: Patch<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub created_at: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub last_updated_at: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub object: Optional<String>,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
crate::redaction::redacted_debug!(AccessPolicy);
#[derive(Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccessPolicyListItem {
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub id: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub name: Optional<String>,
#[serde(default, skip_serializing_if = "Patch::is_unset")]
pub description: Patch<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub enabled: Optional<bool>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub effect: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub allow_all_users: Optional<bool>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub allow_all_agents: Optional<bool>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub target_server_count: Optional<i64>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub target_provider_count: Optional<i64>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub version: Optional<i64>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub status: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub workspace_id: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub organisation_id: Optional<String>,
#[serde(default, skip_serializing_if = "Patch::is_unset")]
pub created_by: Patch<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub created_at: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub last_updated_at: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub object: Optional<String>,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
crate::redaction::redacted_debug!(AccessPolicyListItem);
#[derive(Clone, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccessPolicyPage {
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub object: Optional<String>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub total: Optional<i64>,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub data: Optional<Vec<AccessPolicyListItem>>,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
crate::redaction::redacted_debug!(AccessPolicyPage);
#[derive(Clone, Serialize, Deserialize)]
#[serde(untagged)]
#[non_exhaustive]
pub enum AccessPolicyReturnedSelection {
Value(String),
Names(Vec<String>),
}
#[derive(Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccessPolicyMcpServerDetails {
#[serde(rename = "type")]
pub kind: String,
pub server_id: String,
pub tools: AccessPolicyReturnedSelection,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
#[derive(Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AccessPolicyProviderDetails {
#[serde(rename = "type")]
pub kind: String,
pub provider_id: String,
#[serde(default, skip_serializing_if = "Optional::is_unset")]
pub models: Optional<AccessPolicyReturnedSelection>,
#[serde(flatten)]
pub extra: AccessPolicyExtensions,
}
#[derive(Clone, Serialize)]
#[serde(untagged)]
#[non_exhaustive]
pub enum AccessPolicyTargetDetails {
McpServer(AccessPolicyMcpServerDetails),
Provider(AccessPolicyProviderDetails),
Unknown(JsonObject),
}
impl<'de> Deserialize<'de> for AccessPolicyTargetDetails {
fn deserialize<D: serde::Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
let value = serde_json::Value::deserialize(d)?;
let kind = value
.get("type")
.and_then(serde_json::Value::as_str)
.ok_or_else(|| serde::de::Error::custom("invalid target kind"))?;
match kind {
"mcp_server" => crate::json_decode::from_value(value).map(Self::McpServer),
"provider" => crate::json_decode::from_value(value).map(Self::Provider),
_ => crate::json_decode::from_value(value).map(Self::Unknown),
}
.map_err(|_| serde::de::Error::custom("invalid target"))
}
}
crate::redaction::redacted_debug!(
AccessPolicySubject,
AccessPolicySelection,
AccessPolicyTarget,
CreateAccessPolicy,
UpdateAccessPolicy,
AccessPolicyListOptions,
AccessPolicyReturnedSelection,
AccessPolicyMcpServerDetails,
AccessPolicyProviderDetails,
AccessPolicyTargetDetails
);