mirror of
https://github.com/aljazceru/pubky-core.git
synced 2025-12-31 12:54:35 +01:00
feat(homeserver): return 403 Forbidden for insufficient permisisons
This commit is contained in:
@@ -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<()> {
|
||||
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user