liminal_server/auth_lys/
refusal.rs1use super::{AccessAction, AccessResource, LYS_APP};
4use serde::{Deserialize, Serialize};
5
6#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(deny_unknown_fields)]
9pub struct NeededGrant {
10 pub app: String,
12 pub kind: String,
14 pub id: String,
16 pub action: String,
18}
19
20#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(deny_unknown_fields)]
26pub struct RequiredGrant {
27 pub resource: String,
29 pub action: String,
31 pub needed: NeededGrant,
33 pub request: String,
35}
36
37impl RequiredGrant {
38 #[must_use]
40 pub fn resource_id(&self) -> &str {
41 self.resource
42 .split_once(':')
43 .map_or(self.resource.as_str(), |(_, id)| id)
44 }
45}
46
47#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(deny_unknown_fields)]
51pub struct AccessRefusal {
52 pub refused: Box<RequiredGrant>,
55}
56
57impl AccessRefusal {
58 #[must_use]
60 pub fn new(resource: AccessResource, action: AccessAction, request: String) -> Self {
61 let AccessResource { kind, id } = resource;
62 Self::for_target(LYS_APP, kind.qualified(), &id, action.name(), request)
63 }
64
65 #[must_use]
68 pub fn for_target(app: &str, kind: &str, id: &str, action: &str, request: String) -> Self {
69 Self {
70 refused: Box::new(RequiredGrant {
71 resource: format!("{kind}:{id}"),
72 action: action.to_owned(),
73 needed: NeededGrant {
74 app: app.to_owned(),
75 kind: kind.to_owned(),
76 id: id.to_owned(),
77 action: action.to_owned(),
78 },
79 request,
80 }),
81 }
82 }
83}
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
87pub enum RefusalCause {
88 #[error("required grant is missing")]
90 MissingGrant,
91 #[error("renewed-without-grant")]
93 RenewedWithoutGrant,
94 #[error("pass lifetime has expired")]
96 Expired,
97 #[error("pass audience is not liminal")]
99 WrongAudience,
100 #[error("pass signature was refused")]
102 Signature,
103 #[error("subject placement is restricted")]
105 RestrictedPlacement,
106 #[error("grant mode cannot authorize a hot action")]
108 HotMode,
109 #[error("lys_could_not_be_asked")]
111 DeliberateUnavailable,
112}
113
114#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
116#[error("{cause}")]
117pub struct AccessDecisionError {
118 pub cause: RefusalCause,
120 pub refusal: AccessRefusal,
122}