mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-24 03:04:15 +00:00
fix: actually pass the scope's aud with the service fragment
This commit is contained in:
@@ -293,10 +293,11 @@ async fn proxy_handler(
|
||||
return ApiError::InvalidRequest(format!("Invalid XRPC method: {}", method))
|
||||
.into_response();
|
||||
};
|
||||
let scope_aud = format!("{}#{}", resolved.did, service_id);
|
||||
if let Err(e) = crate::auth::scope_check::check_rpc_scope(
|
||||
&auth_user.auth_source,
|
||||
auth_user.scope.as_deref(),
|
||||
&resolved.did,
|
||||
&scope_aud,
|
||||
&method_nsid,
|
||||
) {
|
||||
return e.into_response();
|
||||
|
||||
@@ -1114,3 +1114,90 @@ async fn test_remembered_scope_later_unregistered_never_reaches_a_token() {
|
||||
refreshed["scope"]
|
||||
);
|
||||
}
|
||||
|
||||
const PROXY_LXM: &str = "io.atcr.getManifest";
|
||||
const PROXY_SERVICE_ID: &str = "test_service";
|
||||
|
||||
async fn setup_mock_proxy_service() -> (MockServer, String) {
|
||||
let server = MockServer::start().await;
|
||||
let uri = server.uri();
|
||||
let host = uri
|
||||
.strip_prefix("http://")
|
||||
.expect("mock uri should be http");
|
||||
let did = format!("did:web:{host}");
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path("/.well-known/did.json"))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
|
||||
"id": did,
|
||||
"service": [{
|
||||
"id": format!("#{PROXY_SERVICE_ID}"),
|
||||
"type": "TestService",
|
||||
"serviceEndpoint": uri,
|
||||
}]
|
||||
})))
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
Mock::given(method("GET"))
|
||||
.and(path(format!("/xrpc/{PROXY_LXM}")))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(json!({ "manifest": [] })))
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
(server, did)
|
||||
}
|
||||
|
||||
async fn proxied_call(access_token: &str, service_did: &str) -> reqwest::Response {
|
||||
client()
|
||||
.get(format!("{}/xrpc/{}", base_url().await, PROXY_LXM))
|
||||
.bearer_auth(access_token)
|
||||
.header("atproto-proxy", format!("{service_did}#{PROXY_SERVICE_ID}"))
|
||||
.send()
|
||||
.await
|
||||
.expect("Proxied request failed")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_rpc_scope_with_service_fragment_allows_proxied_call() {
|
||||
let (_service, service_did) = setup_mock_proxy_service().await;
|
||||
let scope = format!("atproto rpc:{PROXY_LXM}?aud={service_did}#{PROXY_SERVICE_ID}");
|
||||
let (session, _mock) = create_user_and_oauth_session_with_scope(
|
||||
"rpcfrag",
|
||||
"https://example.com/rpc-fragment-callback",
|
||||
&scope,
|
||||
)
|
||||
.await;
|
||||
|
||||
let res = proxied_call(&session.access_token, &service_did).await;
|
||||
let status = res.status();
|
||||
assert_eq!(
|
||||
status,
|
||||
StatusCode::OK,
|
||||
"a scope audienced to did#serviceId must cover a call proxied to that service, got {} {:?}",
|
||||
status,
|
||||
res.text().await
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_rpc_scope_with_bare_did_is_refused_for_proxied_call() {
|
||||
let (_service, service_did) = setup_mock_proxy_service().await;
|
||||
let scope = format!("atproto rpc:{PROXY_LXM}?aud={service_did}");
|
||||
let (session, _mock) = create_user_and_oauth_session_with_scope(
|
||||
"rpcbare",
|
||||
"https://example.com/rpc-bare-callback",
|
||||
&scope,
|
||||
)
|
||||
.await;
|
||||
|
||||
let res = proxied_call(&session.access_token, &service_did).await;
|
||||
let status = res.status();
|
||||
assert_eq!(
|
||||
status,
|
||||
StatusCode::FORBIDDEN,
|
||||
"a bare DID audience must not cover a call proxied to a service on that DID, got {} {:?}",
|
||||
status,
|
||||
res.text().await
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,7 +126,9 @@ mod s3 {
|
||||
.clone()
|
||||
.expect("storage.s3_bucket (S3_BUCKET) must be set");
|
||||
let client = create_s3_client().await;
|
||||
let path = cfg.storage.s3_path
|
||||
let path = cfg
|
||||
.storage
|
||||
.s3_path
|
||||
.trim_start_matches("/")
|
||||
.trim_end_matches("/")
|
||||
.to_string();
|
||||
@@ -139,7 +141,7 @@ mod s3 {
|
||||
|
||||
fn resolve_path(&self, key: &str) -> String {
|
||||
if self.path.is_empty() {
|
||||
return key.to_string()
|
||||
return key.to_string();
|
||||
}
|
||||
|
||||
format!("{}/{}", self.path, key)
|
||||
|
||||
Reference in New Issue
Block a user