2023-11-18 18:00:54 +01:00
|
|
|
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<Link>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
struct Link {
|
|
|
|
pub rel: String,
|
|
|
|
pub href: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
enum DescriptorTypes {
|
|
|
|
Issuer(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum Rels {
|
|
|
|
Connect1Issuer,
|
|
|
|
Unsupported,
|
2023-11-18 19:19:08 +01:00
|
|
|
All,
|
2023-11-18 18:00:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<String> 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, ""),
|
2023-11-18 19:19:08 +01:00
|
|
|
Rels::All => write!(f, ""),
|
2023-11-18 18:00:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<DescriptorTypes> for Rels {
|
|
|
|
fn from(value: DescriptorTypes) -> Self {
|
|
|
|
match value {
|
|
|
|
DescriptorTypes::Issuer(_) => Self::Connect1Issuer,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<DescriptorTypes> 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<ResourceString> 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<String>,
|
2023-11-18 19:19:08 +01:00
|
|
|
// Todo: rel should allow for multiple values
|
2023-11-18 18:00:54 +01:00
|
|
|
rel: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn finger(Query(q): Query<FingerQuery>, Host(host): Host) -> impl IntoResponse {
|
|
|
|
let rel = match q.rel {
|
|
|
|
Some(x) => Rels::from(x),
|
2023-11-18 19:19:08 +01:00
|
|
|
None => Rels::All,
|
2023-11-18 18:00:54 +01:00
|
|
|
};
|
|
|
|
|
2023-11-18 19:19:08 +01:00
|
|
|
if rel == Rels::Unsupported {
|
|
|
|
return (
|
|
|
|
StatusCode::NOT_IMPLEMENTED,
|
|
|
|
"Rel type not supported".to_string(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-11-18 18:00:54 +01:00
|
|
|
let resource = match q.resource {
|
|
|
|
Some(x) => Resource::from(ResourceString(x)),
|
|
|
|
None => Resource::Unsupported,
|
|
|
|
};
|
|
|
|
|
2023-11-18 19:19:08 +01:00
|
|
|
tracing::warn!("{:?} - {:?}", rel, resource);
|
2023-11-18 18:00:54 +01:00
|
|
|
|
|
|
|
match resource {
|
|
|
|
Resource::Acct(subject) => {
|
2023-11-18 19:19:08 +01:00
|
|
|
let links = match rel {
|
|
|
|
Rels::Connect1Issuer => vec![DescriptorTypes::Issuer(host).into()],
|
|
|
|
Rels::All => vec![DescriptorTypes::Issuer(host).into()],
|
|
|
|
Rels::Unsupported => vec![],
|
2023-11-18 18:00:54 +01:00
|
|
|
};
|
|
|
|
|
2023-11-18 19:19:08 +01:00
|
|
|
let doc = ResourceDescriptor { subject, links };
|
|
|
|
|
2023-11-18 18:00:54 +01:00
|
|
|
(StatusCode::OK, serde_json::to_string(&doc).unwrap())
|
|
|
|
}
|
|
|
|
Resource::Unsupported => (
|
|
|
|
StatusCode::NOT_IMPLEMENTED,
|
|
|
|
"Resource type not supported".to_string(),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|