1use serde::{Deserialize, Serialize};
4use url::Url;
5
6use crate::{Error, Target};
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(deny_unknown_fields)]
11pub struct Needed {
12 pub app: String,
14 pub kind: String,
16 pub id: String,
18 pub action: String,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(deny_unknown_fields)]
25pub struct Refused {
26 pub resource: String,
28 pub action: String,
30 pub needed: Needed,
32 pub request: String,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(deny_unknown_fields)]
39pub struct Refusal {
40 pub refused: Refused,
42}
43
44impl Refusal {
45 pub fn new(app: &str, kind: &str, id: &str, action: &str, issuer: &str) -> Result<Self, Error> {
54 let target = Target::new(kind, id, action)?;
55 if app.is_empty() || !crate::rights::audience_owns(kind, app) {
56 return Err(Error::RightsOutsideAudience);
57 }
58 let mut base = Url::parse(issuer)?;
59 if !matches!(base.scheme(), "http" | "https")
60 || base.host_str().is_none()
61 || !base.username().is_empty()
62 || base.password().is_some()
63 || base.query().is_some()
64 || base.fragment().is_some()
65 {
66 return Err(Error::Invalid("grant request issuer is invalid"));
67 }
68 if !base.path().ends_with('/') {
69 base.set_path(&format!("{}/", base.path()));
70 }
71 let mut request = base;
72 request.set_fragment(Some("/requests"));
73 Ok(Self {
74 refused: Refused {
75 resource: format!("{kind}:{id}"),
76 action: target.action.clone(),
77 needed: Needed {
78 app: app.to_owned(),
79 kind: target.kind,
80 id: target.id,
81 action: target.action,
82 },
83 request: request.into(),
84 },
85 })
86 }
87}