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> {
47 let target = Target::new(kind, id, action)?;
48 if app.is_empty() || !crate::rights::audience_owns(kind, app) {
49 return Err(Error::RightsOutsideAudience);
50 }
51 let mut base = Url::parse(issuer)?;
52 if !matches!(base.scheme(), "http" | "https")
53 || base.host_str().is_none()
54 || !base.username().is_empty()
55 || base.password().is_some()
56 || base.query().is_some()
57 || base.fragment().is_some()
58 {
59 return Err(Error::Invalid("grant request issuer is invalid"));
60 }
61 if !base.path().ends_with('/') {
62 base.set_path(&format!("{}/", base.path()));
63 }
64 let mut request = base;
65 request.set_fragment(Some("/requests"));
66 Ok(Self {
67 refused: Refused {
68 resource: format!("{kind}:{id}"),
69 action: target.action.clone(),
70 needed: Needed {
71 app: app.to_owned(),
72 kind: target.kind,
73 id: target.id,
74 action: target.action,
75 },
76 request: request.into(),
77 },
78 })
79 }
80}