add request proxying and basic user auth

This commit is contained in:
sam 2024-01-19 16:44:18 +01:00
parent bfb0a1d1b0
commit 274c527ade
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
31 changed files with 541 additions and 36 deletions

View file

@ -1,3 +1,4 @@
use rsa::{RsaPublicKey, RsaPrivateKey};
use sqlx::{Pool, Postgres};
use crate::config::Config;
@ -5,4 +6,6 @@ use crate::config::Config;
pub struct AppState {
pub pool: Pool<Postgres>,
pub config: Config,
pub public_key: RsaPublicKey,
pub private_key: RsaPrivateKey,
}

View file

@ -0,0 +1,40 @@
use std::sync::Arc;
use axum::{Extension, Json};
use foxchat::{
http::ApiError,
model::{http::guild::CreateGuildParams, Guild, user::PartialUser},
FoxError,
};
use ulid::Ulid;
use crate::{app_state::AppState, fed::FoxRequestData, model::user::User};
pub async fn create_guild(
Extension(state): Extension<Arc<AppState>>,
request: FoxRequestData,
Json(params): Json<CreateGuildParams>,
) -> Result<Json<Guild>, ApiError> {
let user_id = request.user_id.ok_or(FoxError::MissingUser)?;
let user = User::get(&state, &request.instance, user_id).await?;
let guild = sqlx::query!(
"insert into guilds (id, owner_id, name) values ($1, $2, $3) returning *",
Ulid::new().to_string(),
user.id,
params.name
)
.fetch_one(&state.pool)
.await?;
Ok(Json(Guild {
id: guild.id,
name: guild.name,
owner: PartialUser {
id: user.id,
username: user.username,
instance: request.instance.domain,
}
}))
}

View file

@ -0,0 +1,8 @@
mod create_guild;
use axum::{Router, routing::post};
pub fn router() -> Router {
Router::new()
.route("/_fox/chat/guilds", post(create_guild::create_guild))
}

7
chat/src/http/api/mod.rs Normal file
View file

@ -0,0 +1,7 @@
use axum::Router;
pub mod guilds;
pub fn router() -> Router {
Router::new().merge(guilds::router())
}

View file

@ -16,17 +16,15 @@ use rsa::{
use tracing::error;
use ulid::Ulid;
use crate::{app_state::AppState, fed::FoxSignatureData, model::instance::Instance};
use crate::{app_state::AppState, fed::FoxSignatureData};
pub async fn post_hello(
Extension(state): Extension<Arc<AppState>>,
signature: FoxSignatureData,
Json(data): Json<HelloRequest>,
) -> Result<Json<HelloResponse>, ApiError> {
let instance = Instance::get(&state.pool).await?;
let node = fed::get::<NodeResponse>(
&instance.private_key,
&state.private_key,
&state.config.domain,
&data.host,
"/_fox/ident/node",
@ -68,7 +66,7 @@ pub async fn post_hello(
.await?;
Ok(Json(HelloResponse {
public_key: instance
public_key: state
.public_key
.to_pkcs1_pem(rsa::pkcs8::LineEnding::CR)
.wrap_err("formatting instance public key")?,

View file

@ -1,16 +1,23 @@
mod api;
mod hello;
use crate::{app_state::AppState, config::Config};
use crate::{app_state::AppState, config::Config, model::instance::Instance};
use axum::{routing::post, Extension, Router};
use sqlx::{Pool, Postgres};
use std::sync::Arc;
use tower_http::trace::TraceLayer;
pub fn new(pool: Pool<Postgres>, config: Config) -> Router {
let app_state = Arc::new(AppState { pool, config });
pub fn new(pool: Pool<Postgres>, config: Config, instance: Instance) -> Router {
let app_state = Arc::new(AppState {
pool,
config,
public_key: instance.public_key,
private_key: instance.private_key,
});
let app = Router::new()
.route("/_fox/chat/hello", post(hello::post_hello))
.merge(api::router())
.layer(TraceLayer::new_for_http())
.layer(Extension(app_state));

View file

@ -5,7 +5,7 @@ mod fed;
mod http;
mod model;
use crate::config::Config;
use crate::{config::Config, model::instance::Instance};
use clap::{Parser, Subcommand};
use eyre::Result;
use std::net::{Ipv4Addr, SocketAddrV4};
@ -64,8 +64,9 @@ async fn main_web(config: Config) -> Result<()> {
db::init_instance(&pool).await?;
info!("Initialized instance data!");
let instance = Instance::get(&pool).await?;
let port = config.port;
let app = http::new(pool, config);
let app = http::new(pool, config, instance);
let listener = TcpListener::bind(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)).await?;

View file

@ -1,2 +1,3 @@
pub mod instance;
pub mod identity_instance;
pub mod user;

60
chat/src/model/user.rs Normal file
View file

@ -0,0 +1,60 @@
use std::sync::Arc;
use eyre::Result;
use foxchat::{fed, model::User as HttpUser};
use ulid::Ulid;
use crate::app_state::AppState;
use super::identity_instance::IdentityInstance;
pub struct User {
pub id: String,
pub instance_id: String,
pub remote_user_id: String,
pub username: String,
pub avatar: Option<String>,
}
impl User {
pub async fn get(
state: &Arc<AppState>,
instance: &IdentityInstance,
remote_id: String,
) -> Result<User> {
if let Some(user) = sqlx::query_as!(
User,
"select * from users where instance_id = $1 and remote_user_id = $2",
instance.id,
remote_id
)
.fetch_optional(&state.pool)
.await?
{
return Ok(user);
}
let http_user = fed::get::<HttpUser>(
&state.private_key,
&state.config.domain,
&instance.domain,
&format!("/_fox/ident/users/{}", remote_id),
None,
)
.await?;
let user = sqlx::query_as!(
User,
"insert into users (id, instance_id, remote_user_id, username, avatar) values ($1, $2, $3, $4, $5) returning *",
Ulid::new().to_string(),
instance.id,
http_user.id,
http_user.username,
http_user.avatar_url
)
.fetch_one(&state.pool)
.await?;
Ok(user)
}
}