1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! One permission refusal shape for every product route.
use serde::{Deserialize, Serialize};
use url::Url;
use crate::{Error, Target};
/// The grant the caller needs to request.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Needed {
/// The application audience.
pub app: String,
/// The exact resource kind.
pub kind: String,
/// The exact resource id.
pub id: String,
/// The requested action.
pub action: String,
}
/// The contents of the shared refusal envelope.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Refused {
/// The resource as kind and id.
pub resource: String,
/// The action refused.
pub action: String,
/// The grant that would permit it.
pub needed: Needed,
/// The Lys address at which to request that grant.
pub request: String,
}
/// A serialized refusal with no product-specific outer shape.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Refusal {
/// The refused act and how to request permission.
pub refused: Refused,
}
impl Refusal {
/// Name an exact needed grant and the issuer's existing request screen.
///
/// # Errors
///
/// Refuses an empty or control-bearing target, a missing application
/// or a kind that application does not own. The issuer must parse as
/// an HTTP or HTTPS address with a host and no credentials, query or
/// fragment; an invalid address is refused before a link is returned.
pub fn new(app: &str, kind: &str, id: &str, action: &str, issuer: &str) -> Result<Self, Error> {
let target = Target::new(kind, id, action)?;
if app.is_empty() || !crate::rights::audience_owns(kind, app) {
return Err(Error::RightsOutsideAudience);
}
let mut base = Url::parse(issuer)?;
if !matches!(base.scheme(), "http" | "https")
|| base.host_str().is_none()
|| !base.username().is_empty()
|| base.password().is_some()
|| base.query().is_some()
|| base.fragment().is_some()
{
return Err(Error::Invalid("grant request issuer is invalid"));
}
if !base.path().ends_with('/') {
base.set_path(&format!("{}/", base.path()));
}
let mut request = base;
request.set_fragment(Some("/requests"));
Ok(Self {
refused: Refused {
resource: format!("{kind}:{id}"),
action: target.action.clone(),
needed: Needed {
app: app.to_owned(),
kind: target.kind,
id: target.id,
action: target.action,
},
request: request.into(),
},
})
}
}