liminal_server/http/
admin_outright.rs1use serde_json::json;
4
5use super::{
6 AdminDrafts, AdminResponse, AtomicDraftStore, DraftError, LysPassConsumer, Target, refused,
7};
8
9pub struct OutrightAsk {
12 verified: crate::auth_lys::lys_pass_adapter::LysPass,
13 client: lys_pass::Client,
14}
15
16impl<S: AtomicDraftStore> AdminDrafts<S> {
17 pub fn outright(
23 &self,
24 authorization: Option<&str>,
25 at_ms: u64,
26 ) -> Result<Result<OutrightAsk, AdminResponse>, DraftError> {
27 let Some(pass) = authorization.and_then(|header| header.strip_prefix("Bearer ")) else {
28 return Ok(Err(refused(401, "agent_create_pass_missing")));
29 };
30 if pass.is_empty() || pass.len() > self.limits.credential_bytes.get() {
31 return Ok(Err(refused(401, "agent_create_pass_invalid")));
32 }
33 let Ok(verified) = self
34 .adapter
35 .verify_offline(pass.as_bytes(), "liminal", at_ms / 1000)
36 else {
37 return Ok(Err(refused(401, "agent_create_pass_invalid")));
38 };
39 if verified.verified().claims().holder.kind != "person" {
40 return Ok(Err(refused(403, "agent_create_person_required")));
41 }
42 let http = reqwest::Client::builder()
43 .no_proxy()
44 .redirect(reqwest::redirect::Policy::none())
45 .build()
46 .map_err(|_| DraftError::Lys("transport_failed".to_owned()))?;
47 let issuer = crate::auth_lys::live::lys_origin(
48 self.ask.as_deref().unwrap_or_else(|| self.adapter.issuer()),
49 )
50 .map_err(|_| DraftError::Contract)?;
51 let client = lys_pass::Client::new(http, issuer)
52 .map_err(|error| DraftError::Lys(error.name().to_owned()))?;
53 Ok(Ok(OutrightAsk { verified, client }))
54 }
55}
56
57impl OutrightAsk {
58 pub async fn check(
64 self,
65 target: &Target,
66 connector_pass: &str,
67 ) -> Result<
68 Result<(crate::auth_lys::lys_pass_adapter::LysPass, String), AdminResponse>,
69 DraftError,
70 > {
71 let holder = self.verified.verified().claims().holder.id.clone();
72 let answer = self
73 .client
74 .check_batch(connector_pass, &holder, std::slice::from_ref(target), None)
75 .await
76 .map_err(|error| DraftError::Lys(error.name().to_owned()))?;
77 let Some(result) = answer.results.first() else {
78 return Err(DraftError::Contract);
79 };
80 match (&result.grant, result.allowed, result.mode) {
81 (Some(grant), true, Some(lys_pass::Mode::Outright)) => {
82 Ok(Ok((self.verified, grant.clone())))
83 }
84 _ => Ok(Err(AdminResponse {
85 status: 403,
86 body: json!({"refusal": result.refusal.clone().unwrap_or_else(|| "agent_create_outright_required".to_owned())}),
87 })),
88 }
89 }
90}