use axum::extract::{Host, Query}; use axum::http::StatusCode; use axum::response::IntoResponse; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Formatter}; #[derive(Serialize)] struct ResourceDescriptor { pub subject: String, pub links: Vec, } #[derive(Serialize)] struct Link { pub rel: String, pub href: String, } #[derive(Clone)] enum DescriptorTypes { Issuer(String), } #[derive(Debug, PartialEq)] pub enum Rels { Connect1Issuer, Unsupported, } impl From for Rels { fn from(value: String) -> Self { match value.as_str() { "http://openid.net/specs/connect/1.0/issuer" => Self::Connect1Issuer, _ => Self::Unsupported, } } } impl Display for Rels { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Rels::Connect1Issuer => write!(f, "http://openid.net/specs/connect/1.0/issuer"), Rels::Unsupported => write!(f, ""), } } } impl From for Rels { fn from(value: DescriptorTypes) -> Self { match value { DescriptorTypes::Issuer(_) => Self::Connect1Issuer, } } } impl From for Link { fn from(value: DescriptorTypes) -> Self { match value.clone() { DescriptorTypes::Issuer(host) => Self { rel: Rels::from(value).to_string(), href: format!("https://{}", host), }, } } } #[derive(Debug, PartialEq)] enum Resource { Acct(String), Unsupported, } struct ResourceString(String); impl From for Resource { fn from(ResourceString(value): ResourceString) -> Self { let (t, v) = match value.split_once(':') { Some(x) => x, None => return Self::Unsupported, }; match t { "acct" => Self::Acct(v.to_string()), _ => Self::Unsupported, } } } #[derive(Debug, Deserialize)] pub struct FingerQuery { resource: Option, rel: Option, } pub async fn finger(Query(q): Query, Host(host): Host) -> impl IntoResponse { let rel = match q.rel { Some(x) => Rels::from(x), None => Rels::Unsupported, }; let resource = match q.resource { Some(x) => Resource::from(ResourceString(x)), None => Resource::Unsupported, }; tracing::warn!("{} - {:?}", rel, resource); match resource { Resource::Acct(subject) => { let doc = ResourceDescriptor { subject, links: vec![match rel { Rels::Connect1Issuer => DescriptorTypes::Issuer(host).into(), Rels::Unsupported => { return ( StatusCode::NOT_IMPLEMENTED, "Rel type not supported".to_string(), ) } }], }; (StatusCode::OK, serde_json::to_string(&doc).unwrap()) } Resource::Unsupported => ( StatusCode::NOT_IMPLEMENTED, "Resource type not supported".to_string(), ), } }