mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-08-17 06:46:07 +00:00
pds: compile bsky-specific proxy, CORS, & validation out under bsky features
Lewis: May this revision serve well! <did:plc:3fwecdnvtcscjnrx2p4n7alz>
This commit is contained in:
@@ -164,6 +164,13 @@ pub async fn resolve_signing_key(
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
not(feature = "bsky"),
|
||||
expect(
|
||||
unused_variables,
|
||||
reason = "only the bsky block writes display_name into the default profile record"
|
||||
)
|
||||
)]
|
||||
pub async fn sequence_new_account(
|
||||
state: &AppState,
|
||||
did: &Did,
|
||||
|
||||
@@ -335,27 +335,29 @@ async fn proxy_handler(
|
||||
};
|
||||
|
||||
// BSKY: getFeed must be audienced to the feed generator, not the AppView.
|
||||
let (token_aud, token_lxm) =
|
||||
if cfg!(feature = "bsky-support") && method == "app.bsky.feed.getFeed" {
|
||||
match resolve_feed_generator_did(&resolved.url, query.as_deref()).await {
|
||||
Some(feed_did) => (
|
||||
feed_did,
|
||||
"app.bsky.feed.getFeedSkeleton"
|
||||
.parse::<Nsid>()
|
||||
.expect("getFeedSkeleton is a valid NSID"),
|
||||
),
|
||||
None => {
|
||||
warn!(
|
||||
"getFeed proxy: could not resolve feed generator DID; refusing \
|
||||
to mint an AppView-audienced token"
|
||||
);
|
||||
return ApiError::InvalidRequest("Could not resolve feed".into())
|
||||
.into_response();
|
||||
}
|
||||
#[cfg(feature = "bsky-support")]
|
||||
let (token_aud, token_lxm) = if method == "app.bsky.feed.getFeed" {
|
||||
match resolve_feed_generator_did(&resolved.url, query.as_deref()).await {
|
||||
Some(feed_did) => (
|
||||
feed_did,
|
||||
"app.bsky.feed.getFeedSkeleton"
|
||||
.parse::<Nsid>()
|
||||
.expect("getFeedSkeleton is a valid NSID"),
|
||||
),
|
||||
None => {
|
||||
warn!(
|
||||
"getFeed proxy refuses to mint an AppView-audienced token \
|
||||
because feed generator DID resolution failed"
|
||||
);
|
||||
return ApiError::InvalidRequest("Couldn't resolve feed".into())
|
||||
.into_response();
|
||||
}
|
||||
} else {
|
||||
(resolved.did.clone(), method_nsid.clone())
|
||||
};
|
||||
}
|
||||
} else {
|
||||
(resolved.did.clone(), method_nsid.clone())
|
||||
};
|
||||
#[cfg(not(feature = "bsky-support"))]
|
||||
let (token_aud, token_lxm) = (resolved.did.clone(), method_nsid.clone());
|
||||
|
||||
match crate::auth::create_service_token(
|
||||
&auth_user.did,
|
||||
|
||||
@@ -35,7 +35,7 @@ use serde_json::json;
|
||||
use state::AppState;
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::{
|
||||
cors::{Any, CorsLayer},
|
||||
cors::{AllowHeaders, Any, CorsLayer},
|
||||
services::{ServeDir, ServeFile},
|
||||
};
|
||||
pub use tranquil_db_traits::AccountStatus;
|
||||
@@ -106,17 +106,20 @@ pub fn app_with_routes(state: AppState, external: ExternalRoutes) -> Router {
|
||||
CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
.allow_methods([Method::GET, Method::POST, Method::OPTIONS])
|
||||
.allow_headers([
|
||||
http::header::AUTHORIZATION,
|
||||
http::header::CONTENT_TYPE,
|
||||
http::header::CONTENT_ENCODING,
|
||||
http::header::ACCEPT_ENCODING,
|
||||
http::header::USER_AGENT,
|
||||
util::HEADER_DPOP,
|
||||
util::HEADER_ATPROTO_PROXY,
|
||||
util::HEADER_ATPROTO_ACCEPT_LABELERS,
|
||||
util::HEADER_X_BSKY_TOPICS,
|
||||
])
|
||||
.allow_headers(AllowHeaders::list(
|
||||
[
|
||||
http::header::AUTHORIZATION,
|
||||
http::header::CONTENT_TYPE,
|
||||
http::header::CONTENT_ENCODING,
|
||||
http::header::ACCEPT_ENCODING,
|
||||
http::header::USER_AGENT,
|
||||
util::HEADER_DPOP,
|
||||
util::HEADER_ATPROTO_PROXY,
|
||||
util::HEADER_ATPROTO_ACCEPT_LABELERS,
|
||||
]
|
||||
.into_iter()
|
||||
.chain(util::CORS_BSKY_ALLOW_HEADERS),
|
||||
))
|
||||
.expose_headers([
|
||||
http::header::WWW_AUTHENTICATE,
|
||||
util::HEADER_DPOP_NONCE,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pub use tranquil_types::*;
|
||||
|
||||
#[cfg(feature = "bsky")]
|
||||
use std::sync::LazyLock;
|
||||
|
||||
#[cfg(feature = "bsky")]
|
||||
|
||||
@@ -89,6 +89,10 @@ pub const HEADER_ATPROTO_CONTENT_LABELERS: HeaderName =
|
||||
HeaderName::from_static("atproto-content-labelers");
|
||||
#[cfg(feature = "bsky-support")]
|
||||
pub const HEADER_X_BSKY_TOPICS: HeaderName = HeaderName::from_static("x-bsky-topics");
|
||||
#[cfg(feature = "bsky-support")]
|
||||
pub const CORS_BSKY_ALLOW_HEADERS: [HeaderName; 1] = [HEADER_X_BSKY_TOPICS];
|
||||
#[cfg(not(feature = "bsky-support"))]
|
||||
pub const CORS_BSKY_ALLOW_HEADERS: [HeaderName; 0] = [];
|
||||
|
||||
pub fn get_header_str(
|
||||
headers: &HeaderMap,
|
||||
@@ -250,11 +254,7 @@ pub fn build_full_url(path: &str) -> String {
|
||||
&& (path.starts_with("/com.atproto.")
|
||||
// BSKY: Bluesky requires that the PDS implement some app.bsky.* endpoints so we need to deal with those here too.
|
||||
// TODO: surely we can figure out a way to do this more generically?
|
||||
|| (if cfg!(feature = "bsky-support") {
|
||||
path.starts_with("/app.bsky.")
|
||||
} else {
|
||||
true
|
||||
})
|
||||
|| (cfg!(feature = "bsky-support") && path.starts_with("/app.bsky."))
|
||||
|| path.starts_with("/_"))
|
||||
{
|
||||
format!("/xrpc{path}")
|
||||
@@ -798,7 +798,10 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
build_full_url("/app.bsky.feed.getTimeline"),
|
||||
"https://example.com/xrpc/app.bsky.feed.getTimeline"
|
||||
match cfg!(feature = "bsky-support") {
|
||||
true => "https://example.com/xrpc/app.bsky.feed.getTimeline",
|
||||
false => "https://example.com/app.bsky.feed.getTimeline",
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
build_full_url("/_health"),
|
||||
|
||||
@@ -132,6 +132,10 @@ fn validate_preamble<'a>(
|
||||
Ok((record_type, obj))
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
not(feature = "bsky"),
|
||||
expect(unused_variables, reason = "only bsky record checks read obj and rkey")
|
||||
)]
|
||||
fn check_banned_content(
|
||||
record_type: &str,
|
||||
obj: &serde_json::Map<String, Value>,
|
||||
@@ -211,6 +215,7 @@ fn check_post_banned_content(obj: &serde_json::Map<String, Value>) -> Result<(),
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "bsky")]
|
||||
fn check_string_field(
|
||||
obj: &serde_json::Map<String, Value>,
|
||||
field: &str,
|
||||
|
||||
Reference in New Issue
Block a user