147 lines
3.2 KiB
Rust
147 lines
3.2 KiB
Rust
use std::{
|
|
sync::Arc,
|
|
};
|
|
|
|
use hyper::{
|
|
Body,
|
|
Request,
|
|
Response,
|
|
StatusCode,
|
|
};
|
|
use tracing::{
|
|
error,
|
|
instrument,
|
|
};
|
|
|
|
use crate::{
|
|
RequestError,
|
|
error_reply,
|
|
key_validity::KeyValidity,
|
|
prefix_match,
|
|
relay_state::RelayState,
|
|
};
|
|
|
|
#[instrument (level = "trace", skip (req, state))]
|
|
pub async fn handle_scraper_api_v1 (
|
|
req: Request <Body>,
|
|
state: Arc <RelayState>,
|
|
path_rest: &str
|
|
)
|
|
-> Result <Response <Body>, RequestError>
|
|
{
|
|
let api_key = req.headers ().get ("X-ApiKey");
|
|
|
|
let api_key = match api_key {
|
|
None => return Ok (error_reply (StatusCode::FORBIDDEN, "Can't run scraper without an API key")?),
|
|
Some (x) => x,
|
|
};
|
|
|
|
let bad_key = || error_reply (StatusCode::FORBIDDEN, "403 Forbidden");
|
|
|
|
{
|
|
let config = state.config.read ().await;
|
|
|
|
let dev_mode = match &config.iso.dev_mode {
|
|
None => return Ok (bad_key ()?),
|
|
Some (x) => x,
|
|
};
|
|
|
|
let expected_key = match &dev_mode.scraper_key {
|
|
None => return Ok (bad_key ()?),
|
|
Some (x) => x,
|
|
};
|
|
|
|
let now = chrono::Utc::now ();
|
|
|
|
match expected_key.is_valid (now, api_key.as_bytes ()) {
|
|
KeyValidity::Valid => (),
|
|
KeyValidity::WrongKey (bad_hash) => {
|
|
error! ("Bad scraper key with hash {:?}", bad_hash);
|
|
return Ok (bad_key ()?);
|
|
}
|
|
err => {
|
|
error! ("Bad scraper key {:?}", err);
|
|
return Ok (bad_key ()?);
|
|
},
|
|
}
|
|
}
|
|
|
|
if path_rest == "test" {
|
|
Ok (error_reply (StatusCode::OK, "You're valid!")?)
|
|
}
|
|
else {
|
|
Ok (error_reply (StatusCode::NOT_FOUND, "Unknown API endpoint")?)
|
|
}
|
|
}
|
|
|
|
#[instrument (level = "trace", skip (req, state))]
|
|
pub async fn handle_scraper_api (
|
|
req: Request <Body>,
|
|
state: Arc <RelayState>,
|
|
path_rest: &str
|
|
)
|
|
-> Result <Response <Body>, RequestError>
|
|
{
|
|
{
|
|
if ! state.config.read ().await.iso.enable_scraper_auth {
|
|
return Ok (error_reply (StatusCode::FORBIDDEN, "Scraper API disabled")?);
|
|
}
|
|
}
|
|
|
|
if let Some (rest) = prefix_match ("v1/", path_rest) {
|
|
handle_scraper_api_v1 (req, state, rest).await
|
|
}
|
|
else if let Some (rest) = prefix_match ("api/", path_rest) {
|
|
handle_scraper_api_v1 (req, state, rest).await
|
|
}
|
|
else {
|
|
Ok (error_reply (StatusCode::NOT_FOUND, "Unknown scraper API version")?)
|
|
}
|
|
}
|
|
|
|
#[cfg (test)]
|
|
mod tests {
|
|
use std::{
|
|
convert::TryFrom,
|
|
};
|
|
|
|
use tokio::runtime::Runtime;
|
|
use crate::{
|
|
config,
|
|
key_validity,
|
|
};
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn auth () {
|
|
let input = Request::builder ()
|
|
.method ("GET")
|
|
.uri ("http://127.0.0.1:4000/scraper/v1/test")
|
|
.header ("X-ApiKey", "bogus")
|
|
.body (Body::empty ())
|
|
.unwrap ();
|
|
|
|
let config_file = config::file::Config {
|
|
port: Some (4000),
|
|
servers: vec! [],
|
|
iso: config::file::Isomorphic {
|
|
enable_scraper_auth: true,
|
|
dev_mode: Some (config::file::DevMode {
|
|
scraper_key: Some (key_validity::ScraperKey::new (b"bogus")),
|
|
}),
|
|
},
|
|
};
|
|
|
|
let config = config::Config::try_from (config_file).expect ("Can't load config");
|
|
|
|
let relay_state = Arc::new (RelayState::try_from (config).expect ("Can't create relay state"));
|
|
|
|
let mut rt = Runtime::new ().expect ("Can't create runtime for testing");
|
|
|
|
rt.block_on (async move {
|
|
let actual = handle_scraper_api (input, relay_state, "").await;
|
|
let actual = actual.expect ("Relay didn't respond");
|
|
});
|
|
}
|
|
}
|