mirror of
https://github.com/versia-pub/activitypub.git
synced 2025-12-06 14:48:19 +01:00
feat: AP webfinger
This commit is contained in:
parent
f22cae919f
commit
b1c78822de
11
Cargo.lock
generated
11
Cargo.lock
generated
|
|
@ -2091,6 +2091,7 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
|
"webfinger",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4173,6 +4174,16 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webfinger"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f395336b42f1be22490390830ccfe7a735725eef086cd0574bf8759408ecb3af"
|
||||||
|
dependencies = [
|
||||||
|
"reqwest 0.11.27",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.25.4"
|
version = "0.25.4"
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ serde_derive = "1.0.201"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
async-recursion = "1.1.1"
|
async-recursion = "1.1.1"
|
||||||
base64-url = "3.0.0"
|
base64-url = "3.0.0"
|
||||||
|
webfinger = "0.5.1"
|
||||||
|
|
||||||
[dependencies.sea-orm]
|
[dependencies.sea-orm]
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
|
|
|
||||||
21
src/http.rs
21
src/http.rs
|
|
@ -2,8 +2,13 @@ use crate::{
|
||||||
database::StateHandle,
|
database::StateHandle,
|
||||||
entities::user,
|
entities::user,
|
||||||
error::Error,
|
error::Error,
|
||||||
lysand::{self, conversion::receive_lysand_note},
|
lysand::{
|
||||||
|
self,
|
||||||
|
conversion::{db_user_from_url, receive_lysand_note},
|
||||||
|
},
|
||||||
objects::person::{DbUser, PersonAcceptedActivities},
|
objects::person::{DbUser, PersonAcceptedActivities},
|
||||||
|
utils::generate_user_id,
|
||||||
|
API_DOMAIN, LYSAND_DOMAIN,
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
actix_web::{inbox::receive_activity, signing_actor},
|
actix_web::{inbox::receive_activity, signing_actor},
|
||||||
|
|
@ -18,6 +23,7 @@ use anyhow::anyhow;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use webfinger::resolve;
|
||||||
|
|
||||||
pub fn listen(config: &FederationConfig<StateHandle>) -> Result<(), Error> {
|
pub fn listen(config: &FederationConfig<StateHandle>) -> Result<(), Error> {
|
||||||
let hostname = config.domain();
|
let hostname = config.domain();
|
||||||
|
|
@ -101,9 +107,18 @@ pub async fn webfinger(
|
||||||
data: Data<StateHandle>,
|
data: Data<StateHandle>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let name = extract_webfinger_name(&query.resource, &data)?;
|
let name = extract_webfinger_name(&query.resource, &data)?;
|
||||||
let db_user = data.read_user(name).await?;
|
let db_user = data.read_user(name.clone()).await;
|
||||||
|
let user;
|
||||||
|
if db_user.is_ok() {
|
||||||
|
user = db_user.unwrap();
|
||||||
|
} else {
|
||||||
|
let res = resolve("acct:".to_string() + name + "@" + &LYSAND_DOMAIN, true)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
user = db_user_from_url(Url::parse(&res.subject)?).await?;
|
||||||
|
}
|
||||||
Ok(HttpResponse::Ok().json(build_webfinger_response(
|
Ok(HttpResponse::Ok().json(build_webfinger_response(
|
||||||
query.resource.clone(),
|
query.resource.clone(),
|
||||||
Url::parse(&db_user.id)?,
|
generate_user_id(&API_DOMAIN, &user.id)?,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
|
fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
|
||||||
protocol::context::WithContext,
|
protocol::{context::WithContext, public_key::PublicKey},
|
||||||
traits::Object,
|
traits::Object,
|
||||||
FEDERATION_CONTENT_TYPE,
|
FEDERATION_CONTENT_TYPE,
|
||||||
};
|
};
|
||||||
|
|
@ -18,8 +18,8 @@ use crate::{
|
||||||
error,
|
error,
|
||||||
lysand::conversion::{lysand_post_from_db, lysand_user_from_db},
|
lysand::conversion::{lysand_post_from_db, lysand_user_from_db},
|
||||||
objects,
|
objects,
|
||||||
utils::{base_url_decode, generate_create_id},
|
utils::{base_url_decode, generate_create_id, generate_user_id},
|
||||||
Response, DB, FEDERATION_CONFIG,
|
Response, API_DOMAIN, DB, FEDERATION_CONFIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
|
|
@ -78,17 +78,17 @@ async fn query_post(
|
||||||
}
|
}
|
||||||
|
|
||||||
let opt_model = prelude::Post::find()
|
let opt_model = prelude::Post::find()
|
||||||
.filter(post::Column::Url.eq(query.url.clone().unwrap().as_str()))
|
.filter(post::Column::Url.eq(query.url.clone().unwrap().as_str()))
|
||||||
.one(db)
|
.one(db)
|
||||||
|
.await?;
|
||||||
|
let target;
|
||||||
|
if let Some(model) = opt_model {
|
||||||
|
target = model;
|
||||||
|
} else {
|
||||||
|
target = ObjectId::<post::Model>::from(Url::parse(query.url.clone().unwrap().as_str())?)
|
||||||
|
.dereference(&data.to_request_data())
|
||||||
.await?;
|
.await?;
|
||||||
let target;
|
}
|
||||||
if let Some(model) = opt_model {
|
|
||||||
target = model;
|
|
||||||
} else {
|
|
||||||
target = ObjectId::<post::Model>::from(Url::parse(query.url.clone().unwrap().as_str())?)
|
|
||||||
.dereference(&data.to_request_data())
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.content_type("application/json")
|
.content_type("application/json")
|
||||||
|
|
@ -117,6 +117,41 @@ async fn fetch_post(
|
||||||
.json(crate::objects::post::Note::from_db(&post)))
|
.json(crate::objects::post::Note::from_db(&post)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/apbridge/user/{user}")]
|
||||||
|
async fn fetch_user(
|
||||||
|
path: web::Path<String>,
|
||||||
|
state: web::Data<State>,
|
||||||
|
) -> actix_web::Result<HttpResponse, error::Error> {
|
||||||
|
let db = DB.get().unwrap();
|
||||||
|
|
||||||
|
let user = prelude::User::find()
|
||||||
|
.filter(post::Column::Id.eq(path.as_str()))
|
||||||
|
.one(db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let user = match user {
|
||||||
|
Some(user) => user,
|
||||||
|
None => return Ok(HttpResponse::NotFound().finish()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok()
|
||||||
|
.content_type(FEDERATION_CONTENT_TYPE)
|
||||||
|
.json(crate::objects::person::Person {
|
||||||
|
kind: Default::default(),
|
||||||
|
id: generate_user_id(&API_DOMAIN, &user.id)?.into(),
|
||||||
|
preferred_username: user.username.clone(),
|
||||||
|
name: user.name.clone(),
|
||||||
|
summary: user.summary.clone(),
|
||||||
|
url: Url::parse(user.url.as_str()).unwrap(),
|
||||||
|
inbox: Url::parse(user.inbox.as_str()).unwrap(),
|
||||||
|
public_key: PublicKey {
|
||||||
|
owner: Url::parse(user.url.as_str()).unwrap(),
|
||||||
|
public_key_pem: user.public_key,
|
||||||
|
id: format!("{}#main-key", Url::parse(user.url.as_str()).unwrap()),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
#[get("/apbridge/lysand/object/{post}")]
|
#[get("/apbridge/lysand/object/{post}")]
|
||||||
async fn fetch_lysand_post(
|
async fn fetch_lysand_post(
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use clap::Parser;
|
||||||
use database::Database;
|
use database::Database;
|
||||||
use entities::post;
|
use entities::post;
|
||||||
use http::{http_get_user, http_post_user_inbox, webfinger};
|
use http::{http_get_user, http_post_user_inbox, webfinger};
|
||||||
use lysand::http::{create_activity, fetch_lysand_post, fetch_post, query_post};
|
use lysand::http::{create_activity, fetch_lysand_post, fetch_post, fetch_user, query_post};
|
||||||
use objects::person::DbUser;
|
use objects::person::DbUser;
|
||||||
use sea_orm::{ActiveModelTrait, DatabaseConnection, Set};
|
use sea_orm::{ActiveModelTrait, DatabaseConnection, Set};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
@ -259,6 +259,7 @@ async fn main() -> actix_web::Result<(), anyhow::Error> {
|
||||||
.route("/.well-known/webfinger", web::get().to(webfinger))
|
.route("/.well-known/webfinger", web::get().to(webfinger))
|
||||||
.service(index)
|
.service(index)
|
||||||
.service(fetch_post)
|
.service(fetch_post)
|
||||||
|
.service(fetch_user)
|
||||||
.service(create_activity)
|
.service(create_activity)
|
||||||
.service(query_post)
|
.service(query_post)
|
||||||
.service(fetch_lysand_post)
|
.service(fetch_lysand_post)
|
||||||
|
|
|
||||||
|
|
@ -71,14 +71,14 @@ impl DbUser {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
kind: PersonType,
|
pub kind: PersonType,
|
||||||
preferred_username: String,
|
pub preferred_username: String,
|
||||||
name: String,
|
pub name: String,
|
||||||
summary: Option<String>,
|
pub summary: Option<String>,
|
||||||
url: Url,
|
pub url: Url,
|
||||||
id: ObjectId<user::Model>,
|
pub id: ObjectId<user::Model>,
|
||||||
inbox: Url,
|
pub inbox: Url,
|
||||||
public_key: PublicKey,
|
pub public_key: PublicKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue