1use serde::{Deserialize, Serialize};
4
5use crate::Error;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "snake_case")]
10#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema), schema(as = PassMode))]
11pub enum Mode {
12 Outright,
14 ByDraft,
16 ByTwo,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22#[serde(deny_unknown_fields)]
23pub struct Holder {
24 pub id: String,
26 pub kind: String,
28 pub responsible: Option<String>,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34#[serde(deny_unknown_fields)]
35#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema), schema(as = PassResource))]
36pub struct Resource {
37 pub kind: String,
39 pub id: String,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45#[serde(deny_unknown_fields)]
46pub struct Right {
47 pub resource: Resource,
49 pub actions: Vec<String>,
51 pub mode: Mode,
53 pub grant: String,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct Claims {
60 pub iss: String,
62 pub sub: String,
64 pub aud: String,
66 pub iat: u64,
68 pub exp: u64,
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub nbf: Option<u64>,
73 pub holder: Holder,
75 #[serde(default, skip_serializing_if = "Vec::is_empty")]
78 pub agent_key_thumbprints: Vec<String>,
79 pub rights: Vec<Right>,
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub rights_truncated: Option<String>,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(deny_unknown_fields)]
89pub struct Target {
90 pub kind: String,
92 pub id: String,
94 pub action: String,
96}
97
98impl Target {
99 pub fn new(kind: &str, id: &str, action: &str) -> Result<Self, Error> {
106 if [kind, id, action]
107 .iter()
108 .any(|value| value.is_empty() || value.chars().any(char::is_control))
109 {
110 return Err(Error::Invalid(
111 "permission target is empty or contains controls",
112 ));
113 }
114 Ok(Self {
115 kind: kind.to_owned(),
116 id: id.to_owned(),
117 action: action.to_owned(),
118 })
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum Decision<'a> {
125 Allowed {
127 grant: &'a str,
129 },
130 Held {
132 grant: &'a str,
134 mode: Mode,
136 },
137 Refused,
139}
140
141pub(crate) fn in_prefix(kind: &str, prefix: &str) -> bool {
142 kind == prefix
143 || kind
144 .strip_prefix(prefix)
145 .is_some_and(|rest| rest.starts_with('.'))
146}
147
148pub(crate) fn audience_owns(kind: &str, audience: &str) -> bool {
149 kind.split_once('.')
150 .map_or(audience == "lys", |(owner, _)| owner == audience)
151}