use axum::Router;
use axum::http::StatusCode;
use axum::routing::get;
use super::*;
async fn forge(status: StatusCode, body: &'static str) -> String {
let app = Router::new().route(
"/repos/{org}/{repo}",
get(move || async move { (status, body) }),
);
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
format!("http://{addr}")
}
async fn asked(status: StatusCode, body: &'static str) -> Result<Permission, Error> {
crate::tls::install_crypto_provider();
let api = forge(status, body).await;
let ns = Namespace::new("FerrLabs", "Blastlands").unwrap();
permission(&reqwest::Client::new(), &api, "a-token", &ns).await
}
#[tokio::test]
async fn a_permissions_block_decides_the_level() {
for (body, expected) in [
(
r#"{"permissions":{"admin":true,"push":true,"pull":true}}"#,
Permission::Admin,
),
(
r#"{"permissions":{"admin":false,"push":true,"pull":true}}"#,
Permission::Write,
),
(
r#"{"permissions":{"admin":false,"push":false,"pull":true}}"#,
Permission::Read,
),
] {
assert_eq!(asked(StatusCode::OK, body).await.unwrap(), expected);
}
}
#[tokio::test]
async fn the_block_an_installation_token_receives_still_grants_read() {
let granted = asked(
StatusCode::OK,
r#"{"private":true,"permissions":{"admin":false,"maintain":false,"push":false,"triage":false,"pull":false}}"#,
)
.await;
assert_eq!(
granted.unwrap(),
Permission::Read,
"the forge answers 404 to a token that cannot see the repository, so a body at all is proof of read"
);
}
#[tokio::test]
async fn the_block_an_installation_token_receives_never_grants_write() {
let granted = asked(
StatusCode::OK,
r#"{"private":true,"permissions":{"admin":false,"push":false,"pull":false}}"#,
)
.await
.unwrap();
assert!(granted.require_write().is_err(), "{granted:?}");
}
#[tokio::test]
async fn a_payload_with_no_permissions_block_is_read() {
let granted = asked(StatusCode::OK, r#"{"private":true}"#).await;
assert_eq!(
granted.unwrap(),
Permission::Read,
"the forge answers 404 to a token that cannot see the repository, so a body at all is proof of read"
);
}
#[tokio::test]
async fn no_permissions_block_never_grants_write() {
let granted = asked(StatusCode::OK, r#"{"private":true}"#).await.unwrap();
assert!(granted.require_write().is_err(), "{granted:?}");
}
#[tokio::test]
async fn a_repository_the_token_cannot_see_is_refused() {
let refused = asked(StatusCode::NOT_FOUND, r#"{"message":"Not Found"}"#).await;
assert!(matches!(refused, Err(Error::Forbidden)), "{refused:?}");
}
#[derive(Clone, Default)]
struct Captured(std::sync::Arc<std::sync::Mutex<Vec<u8>>>);
impl std::io::Write for Captured {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.0.lock().unwrap().extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for Captured {
type Writer = Self;
fn make_writer(&'a self) -> Self::Writer {
self.clone()
}
}
#[tokio::test]
async fn a_refusal_says_why_in_the_message_and_not_only_in_a_field() {
crate::tls::install_crypto_provider();
let captured = Captured::default();
let logged = {
let _guard = tracing::subscriber::set_default(
tracing_subscriber::fmt()
.with_writer(captured.clone())
.with_max_level(tracing::Level::INFO)
.finish(),
);
asked(StatusCode::NOT_FOUND, r#"{"message":"Not Found"}"#)
.await
.ok();
String::from_utf8(captured.0.lock().unwrap().clone()).unwrap()
};
assert!(
logged.contains("the forge will not admit this repository to this token"),
"the refusal has to be the event's message, not a structured field \
nothing reads: {logged}"
);
}