use serde::{Deserialize, Serialize};
use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema), schema(as = PassMode))]
pub enum Mode {
Outright,
ByDraft,
ByTwo,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Holder {
pub id: String,
pub kind: String,
pub responsible: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema), schema(as = PassResource))]
pub struct Resource {
pub kind: String,
pub id: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Right {
pub resource: Resource,
pub actions: Vec<String>,
pub mode: Mode,
pub grant: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
pub iss: String,
pub sub: String,
pub aud: String,
pub iat: u64,
pub exp: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub nbf: Option<u64>,
pub holder: Holder,
pub rights: Vec<Right>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rights_truncated: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Target {
pub kind: String,
pub id: String,
pub action: String,
}
impl Target {
pub fn new(kind: &str, id: &str, action: &str) -> Result<Self, Error> {
if [kind, id, action]
.iter()
.any(|value| value.is_empty() || value.chars().any(char::is_control))
{
return Err(Error::Invalid(
"permission target is empty or contains controls",
));
}
Ok(Self {
kind: kind.to_owned(),
id: id.to_owned(),
action: action.to_owned(),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Decision<'a> {
Allowed {
grant: &'a str,
},
Held {
grant: &'a str,
mode: Mode,
},
Refused,
}
pub(crate) fn in_prefix(kind: &str, prefix: &str) -> bool {
kind == prefix
|| kind
.strip_prefix(prefix)
.is_some_and(|rest| rest.starts_with('.'))
}
pub(crate) fn audience_owns(kind: &str, audience: &str) -> bool {
kind.split_once('.')
.map_or(audience == "lys", |(owner, _)| owner == audience)
}