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> {
101 if [kind, id, action]
102 .iter()
103 .any(|value| value.is_empty() || value.chars().any(char::is_control))
104 {
105 return Err(Error::Invalid(
106 "permission target is empty or contains controls",
107 ));
108 }
109 Ok(Self {
110 kind: kind.to_owned(),
111 id: id.to_owned(),
112 action: action.to_owned(),
113 })
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum Decision<'a> {
120 Allowed {
122 grant: &'a str,
124 },
125 Held {
127 grant: &'a str,
129 mode: Mode,
131 },
132 Refused,
134}
135
136pub(crate) fn in_prefix(kind: &str, prefix: &str) -> bool {
137 kind == prefix
138 || kind
139 .strip_prefix(prefix)
140 .is_some_and(|rest| rest.starts_with('.'))
141}
142
143pub(crate) fn audience_owns(kind: &str, audience: &str) -> bool {
144 kind.split_once('.')
145 .map_or(audience == "lys", |(owner, _)| owner == audience)
146}