use serde::{Deserialize, Serialize};
use url::Url;
use crate::{Error, Target};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Needed {
pub app: String,
pub kind: String,
pub id: String,
pub action: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Refused {
pub resource: String,
pub action: String,
pub needed: Needed,
pub request: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Refusal {
pub refused: Refused,
}
impl Refusal {
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(),
},
})
}
}