feat(homeserver): return 403 Forbidden for insufficient permisisons

This commit is contained in:
nazeh
2024-09-06 14:10:46 +03:00
parent f1b65e9dac
commit a03117114b
2 changed files with 51 additions and 9 deletions

View File

@@ -26,8 +26,8 @@ pub async fn put(
let public_key = pubky.public_key().clone();
let path = path.as_str();
authorize(&mut state, cookies, &public_key, path)?;
verify(path)?;
authorize(&mut state, cookies, &public_key, path)?;
let mut stream = body.into_data_stream();
@@ -134,20 +134,32 @@ pub async fn delete(
Ok(())
}
/// Authorize write (PUT or DELETE) for Public paths.
fn authorize(
state: &mut AppState,
cookies: Cookies,
public_key: &PublicKey,
_: &str,
path: &str,
) -> Result<()> {
// TODO: can we move this logic to the extractor or a layer
// to perform this validation?
let _ = state
let session = state
.db
.get_session(cookies, public_key)?
.ok_or(Error::with_status(StatusCode::UNAUTHORIZED))?;
Ok(())
if session.pubky == *public_key
&& session.capabilities.iter().any(|cap| {
path.starts_with(&cap.scope[1..])
&& cap
.abilities
.contains(&pubky_common::capabilities::Ability::Write)
})
{
return Ok(());
}
Err(Error::with_status(StatusCode::FORBIDDEN))
}
fn verify(path: &str) -> Result<()> {

View File

@@ -256,7 +256,7 @@ mod tests {
use pkarr::{mainline::Testnet, Keypair};
use pubky_common::capabilities::{Capabilities, Capability};
use pubky_homeserver::Homeserver;
use url::Url;
use reqwest::StatusCode;
#[tokio::test]
async fn basic_authn() {
@@ -294,6 +294,7 @@ mod tests {
.unwrap()
.unwrap();
assert_eq!(session.pubky, keypair.public_key());
assert!(session.capabilities.contains(&Capability::root()));
}
}
@@ -304,11 +305,11 @@ mod tests {
let server = Homeserver::start_test(&testnet).await.unwrap();
let keypair = Keypair::random();
let pubky = keypair.public_key();
// Third party app side
let capabilities: Capabilities = "/pub/pubky.app/:rw,/prv/foo.bar/file:rw"
.try_into()
.unwrap();
let capabilities: Capabilities =
"/pub/pubky.app/:rw,/pub/foo.bar/file:r".try_into().unwrap();
let client = PubkyClient::test(&testnet);
let (pubkyauth_url, pubkyauth_response) = client
.auth_request("https://demo.httprelay.io/link", &capabilities)
@@ -328,7 +329,36 @@ mod tests {
let session = pubkyauth_response.await.unwrap().unwrap();
assert_eq!(session.pubky, pubky);
assert_eq!(session.capabilities, capabilities.0);
assert_eq!(session.pubky, keypair.public_key());
// Test access control enforcement
client
.put(format!("pubky://{pubky}/pub/pubky.app/foo").as_str(), &[])
.await
.unwrap();
assert_eq!(
client
.put(format!("pubky://{pubky}/pub/pubky.app").as_str(), &[])
.await
.map_err(|e| match e {
crate::Error::Reqwest(e) => e.status(),
_ => None,
}),
Err(Some(StatusCode::FORBIDDEN))
);
assert_eq!(
client
.put(format!("pubky://{pubky}/pub/foo.bar/file").as_str(), &[])
.await
.map_err(|e| match e {
crate::Error::Reqwest(e) => e.status(),
_ => None,
}),
Err(Some(StatusCode::FORBIDDEN))
);
}
}