Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/apis/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::apis::resources::auth_key::AuthKey;
use crate::apis::resources::stats::Stats;
use crate::apis::resources::torrent::ListItem;
use crate::protocol::info_hash::InfoHash;
use crate::tracker::auth::KeyId;
use crate::tracker::auth::Key;
use crate::tracker::services::statistics::get_metrics;
use crate::tracker::services::torrent::{get_torrent_info, get_torrents, Pagination};
use crate::tracker::Tracker;
Expand Down Expand Up @@ -101,15 +101,15 @@ pub async fn generate_auth_key_handler(State(tracker): State<Arc<Tracker>>, Path
}

#[derive(Deserialize)]
pub struct KeyIdParam(String);
pub struct KeyParam(String);

pub async fn delete_auth_key_handler(
State(tracker): State<Arc<Tracker>>,
Path(seconds_valid_or_key): Path<KeyIdParam>,
Path(seconds_valid_or_key): Path<KeyParam>,
) -> Response {
match KeyId::from_str(&seconds_valid_or_key.0) {
match Key::from_str(&seconds_valid_or_key.0) {
Err(_) => invalid_auth_key_param_response(&seconds_valid_or_key.0),
Ok(key_id) => match tracker.remove_auth_key(&key_id.to_string()).await {
Ok(key) => match tracker.remove_auth_key(&key.to_string()).await {
Ok(_) => ok_response(),
Err(e) => failed_to_delete_key_response(e),
},
Expand Down
31 changes: 17 additions & 14 deletions src/apis/resources/auth_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,33 @@ use std::convert::From;
use serde::{Deserialize, Serialize};

use crate::protocol::clock::DurationSinceUnixEpoch;
use crate::tracker::auth::{self, KeyId};
use crate::tracker::auth::{self, Key};

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct AuthKey {
pub key: String, // todo: rename to `id`
pub valid_until: Option<u64>,
pub key: String, // todo: rename to `id` (API breaking change!)
pub valid_until: Option<u64>, // todo: `auth::ExpiringKey` has now always a value (API breaking change!)
}

impl From<AuthKey> for auth::ExpiringKey {
fn from(auth_key_resource: AuthKey) -> Self {
let valid_until = match auth_key_resource.valid_until {
Some(valid_until) => DurationSinceUnixEpoch::from_secs(valid_until),
None => DurationSinceUnixEpoch::from_secs(0),
};

auth::ExpiringKey {
id: auth_key_resource.key.parse::<KeyId>().unwrap(),
valid_until: auth_key_resource
.valid_until
.map(|valid_until| DurationSinceUnixEpoch::new(valid_until, 0)),
key: auth_key_resource.key.parse::<Key>().unwrap(),
valid_until,
}
}
}

impl From<auth::ExpiringKey> for AuthKey {
fn from(auth_key: auth::ExpiringKey) -> Self {
AuthKey {
key: auth_key.id.to_string(),
valid_until: auth_key.valid_until.map(|valid_until| valid_until.as_secs()),
key: auth_key.key.to_string(),
valid_until: Some(auth_key.valid_until.as_secs()),
}
}
}
Expand All @@ -37,7 +40,7 @@ mod tests {

use super::AuthKey;
use crate::protocol::clock::{Current, TimeNow};
use crate::tracker::auth::{self, KeyId};
use crate::tracker::auth::{self, Key};

#[test]
fn it_should_be_convertible_into_an_auth_key() {
Expand All @@ -51,8 +54,8 @@ mod tests {
assert_eq!(
auth::ExpiringKey::from(auth_key_resource),
auth::ExpiringKey {
id: "IaWDneuFNZi8IB4MPA3qW1CD0M30EZSM".parse::<KeyId>().unwrap(), // cspell:disable-line
valid_until: Some(Current::add(&Duration::new(duration_in_secs, 0)).unwrap())
key: "IaWDneuFNZi8IB4MPA3qW1CD0M30EZSM".parse::<Key>().unwrap(), // cspell:disable-line
valid_until: Current::add(&Duration::new(duration_in_secs, 0)).unwrap()
}
);
}
Expand All @@ -62,8 +65,8 @@ mod tests {
let duration_in_secs = 60;

let auth_key = auth::ExpiringKey {
id: "IaWDneuFNZi8IB4MPA3qW1CD0M30EZSM".parse::<KeyId>().unwrap(), // cspell:disable-line
valid_until: Some(Current::add(&Duration::new(duration_in_secs, 0)).unwrap()),
key: "IaWDneuFNZi8IB4MPA3qW1CD0M30EZSM".parse::<Key>().unwrap(), // cspell:disable-line
valid_until: Current::add(&Duration::new(duration_in_secs, 0)).unwrap(),
};

assert_eq!(
Expand Down
4 changes: 2 additions & 2 deletions src/databases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ pub trait Database: Sync + Send {

async fn remove_info_hash_from_whitelist(&self, info_hash: InfoHash) -> Result<usize, Error>;

// todo: replace type `&str` with `&KeyId`
// todo: replace type `&str` with `&Key`
async fn get_key_from_keys(&self, key: &str) -> Result<Option<auth::ExpiringKey>, Error>;

async fn add_key_to_keys(&self, auth_key: &auth::ExpiringKey) -> Result<usize, Error>;

// todo: replace type `&str` with `&KeyId`
// todo: replace type `&str` with `&Key`
async fn remove_key_from_keys(&self, key: &str) -> Result<usize, Error>;

async fn is_info_hash_whitelisted(&self, info_hash: &InfoHash) -> Result<bool, Error> {
Expand Down
14 changes: 7 additions & 7 deletions src/databases/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use super::driver::Driver;
use crate::databases::{Database, Error};
use crate::protocol::common::AUTH_KEY_LENGTH;
use crate::protocol::info_hash::InfoHash;
use crate::tracker::auth::{self, KeyId};
use crate::tracker::auth::{self, Key};

const DRIVER: Driver = Driver::MySQL;

Expand Down Expand Up @@ -117,8 +117,8 @@ impl Database for Mysql {
let keys = conn.query_map(
"SELECT `key`, valid_until FROM `keys`",
|(key, valid_until): (String, i64)| auth::ExpiringKey {
id: key.parse::<KeyId>().unwrap(),
valid_until: Some(Duration::from_secs(valid_until.unsigned_abs())),
key: key.parse::<Key>().unwrap(),
valid_until: Duration::from_secs(valid_until.unsigned_abs()),
},
)?;

Expand Down Expand Up @@ -192,16 +192,16 @@ impl Database for Mysql {
let key = query?;

Ok(key.map(|(key, expiry)| auth::ExpiringKey {
id: key.parse::<KeyId>().unwrap(),
valid_until: Some(Duration::from_secs(expiry.unsigned_abs())),
key: key.parse::<Key>().unwrap(),
valid_until: Duration::from_secs(expiry.unsigned_abs()),
}))
}

async fn add_key_to_keys(&self, auth_key: &auth::ExpiringKey) -> Result<usize, Error> {
let mut conn = self.pool.get().map_err(|e| (e, DRIVER))?;

let key = auth_key.id.to_string();
let valid_until = auth_key.valid_until.unwrap_or(Duration::ZERO).as_secs().to_string();
let key = auth_key.key.to_string();
let valid_until = auth_key.valid_until.as_secs().to_string();

conn.exec_drop(
"INSERT INTO `keys` (`key`, valid_until) VALUES (:key, :valid_until)",
Expand Down
12 changes: 6 additions & 6 deletions src/databases/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::driver::Driver;
use crate::databases::{Database, Error};
use crate::protocol::clock::DurationSinceUnixEpoch;
use crate::protocol::info_hash::InfoHash;
use crate::tracker::auth::{self, KeyId};
use crate::tracker::auth::{self, Key};

const DRIVER: Driver = Driver::Sqlite3;

Expand Down Expand Up @@ -112,8 +112,8 @@ impl Database for Sqlite {
let valid_until: i64 = row.get(1)?;

Ok(auth::ExpiringKey {
id: key.parse::<KeyId>().unwrap(),
valid_until: Some(DurationSinceUnixEpoch::from_secs(valid_until.unsigned_abs())),
key: key.parse::<Key>().unwrap(),
valid_until: DurationSinceUnixEpoch::from_secs(valid_until.unsigned_abs()),
})
})?;

Expand Down Expand Up @@ -213,8 +213,8 @@ impl Database for Sqlite {
let expiry: i64 = f.get(1).unwrap();
let id: String = f.get(0).unwrap();
auth::ExpiringKey {
id: id.parse::<KeyId>().unwrap(),
valid_until: Some(DurationSinceUnixEpoch::from_secs(expiry.unsigned_abs())),
key: id.parse::<Key>().unwrap(),
valid_until: DurationSinceUnixEpoch::from_secs(expiry.unsigned_abs()),
}
}))
}
Expand All @@ -224,7 +224,7 @@ impl Database for Sqlite {

let insert = conn.execute(
"INSERT INTO keys (key, valid_until) VALUES (?1, ?2)",
[auth_key.id.to_string(), auth_key.valid_until.unwrap().as_secs().to_string()],
[auth_key.key.to_string(), auth_key.valid_until.as_secs().to_string()],
)?;

if insert == 0 {
Expand Down
16 changes: 8 additions & 8 deletions src/http/axum_implementation/extractors/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,30 @@ use axum::extract::{FromRequestParts, Path};
use axum::http::request::Parts;
use axum::response::{IntoResponse, Response};

use crate::http::axum_implementation::handlers::auth::{self, KeyIdParam};
use crate::http::axum_implementation::handlers::auth::{self, KeyParam};
use crate::http::axum_implementation::responses;
use crate::tracker::auth::KeyId;
use crate::tracker::auth::Key;

pub struct ExtractKeyId(pub KeyId);
pub struct Extract(pub Key);

#[async_trait]
impl<S> FromRequestParts<S> for ExtractKeyId
impl<S> FromRequestParts<S> for Extract
where
S: Send + Sync,
{
type Rejection = Response;

async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
match Path::<KeyIdParam>::from_request_parts(parts, state).await {
Ok(key_id_param) => {
let Ok(key_id) = key_id_param.0.value().parse::<KeyId>() else {
match Path::<KeyParam>::from_request_parts(parts, state).await {
Ok(key_param) => {
let Ok(key) = key_param.0.value().parse::<Key>() else {
return Err(responses::error::Error::from(
auth::Error::InvalidKeyFormat {
location: Location::caller()
})
.into_response())
};
Ok(ExtractKeyId(key_id))
Ok(Extract(key))
}
Err(rejection) => match rejection {
axum::extract::rejection::PathRejection::FailedToDeserializePathParams(_) => {
Expand Down
6 changes: 3 additions & 3 deletions src/http/axum_implementation/handlers/announce.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use axum::response::{IntoResponse, Response};
use log::debug;

use crate::http::axum_implementation::extractors::announce_request::ExtractRequest;
use crate::http::axum_implementation::extractors::key::ExtractKeyId;
use crate::http::axum_implementation::extractors::key::Extract;
use crate::http::axum_implementation::extractors::peer_ip;
use crate::http::axum_implementation::extractors::remote_client_ip::RemoteClientIp;
use crate::http::axum_implementation::handlers::auth;
Expand Down Expand Up @@ -41,12 +41,12 @@ pub async fn handle_without_key(
pub async fn handle_with_key(
State(tracker): State<Arc<Tracker>>,
ExtractRequest(announce_request): ExtractRequest,
ExtractKeyId(key_id): ExtractKeyId,
Extract(key): Extract,
remote_client_ip: RemoteClientIp,
) -> Response {
debug!("http announce request: {:#?}", announce_request);

match tracker.authenticate(&key_id).await {
match tracker.authenticate(&key).await {
Ok(_) => (),
Err(error) => return responses::error::Error::from(error).into_response(),
}
Expand Down
4 changes: 2 additions & 2 deletions src/http/axum_implementation/handlers/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use crate::http::axum_implementation::responses;
use crate::tracker::auth;

#[derive(Deserialize)]
pub struct KeyIdParam(String);
pub struct KeyParam(String);

impl KeyIdParam {
impl KeyParam {
#[must_use]
pub fn value(&self) -> String {
self.0.clone()
Expand Down
6 changes: 3 additions & 3 deletions src/http/axum_implementation/handlers/scrape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use axum::extract::State;
use axum::response::{IntoResponse, Response};
use log::debug;

use crate::http::axum_implementation::extractors::key::ExtractKeyId;
use crate::http::axum_implementation::extractors::key::Extract;
use crate::http::axum_implementation::extractors::peer_ip;
use crate::http::axum_implementation::extractors::remote_client_ip::RemoteClientIp;
use crate::http::axum_implementation::extractors::scrape_request::ExtractRequest;
Expand All @@ -31,12 +31,12 @@ pub async fn handle_without_key(
pub async fn handle_with_key(
State(tracker): State<Arc<Tracker>>,
ExtractRequest(scrape_request): ExtractRequest,
ExtractKeyId(key_id): ExtractKeyId,
Extract(key): Extract,
remote_client_ip: RemoteClientIp,
) -> Response {
debug!("http scrape request: {:#?}", &scrape_request);

match tracker.authenticate(&key_id).await {
match tracker.authenticate(&key).await {
Ok(_) => (),
Err(_) => return handle_fake_scrape(&tracker, &scrape_request, &remote_client_ip).await,
}
Expand Down
10 changes: 5 additions & 5 deletions src/http/warp_implementation/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use super::{request, WebResult};
use crate::http::percent_encoding::{percent_decode_info_hash, percent_decode_peer_id};
use crate::protocol::common::MAX_SCRAPE_TORRENTS;
use crate::protocol::info_hash::InfoHash;
use crate::tracker::auth::KeyId;
use crate::tracker::auth::Key;
use crate::tracker::{self, peer};

/// Pass Arc<tracker::TorrentTracker> along
Expand All @@ -37,16 +37,16 @@ pub fn with_peer_id() -> impl Filter<Extract = (peer::Id,), Error = Rejection> +

/// Pass Arc<tracker::TorrentTracker> along
#[must_use]
pub fn with_auth_key_id() -> impl Filter<Extract = (Option<KeyId>,), Error = Infallible> + Clone {
pub fn with_auth_key() -> impl Filter<Extract = (Option<Key>,), Error = Infallible> + Clone {
warp::path::param::<String>()
.map(|key: String| {
let key_id = KeyId::from_str(&key);
match key_id {
let key = Key::from_str(&key);
match key {
Ok(id) => Some(id),
Err(_) => None,
}
})
.or_else(|_| async { Ok::<(Option<KeyId>,), Infallible>((None,)) })
.or_else(|_| async { Ok::<(Option<Key>,), Infallible>((None,)) })
}

/// Check for `PeerAddress`
Expand Down
14 changes: 7 additions & 7 deletions src/http/warp_implementation/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use super::error::Error;
use super::{request, response, WebResult};
use crate::http::warp_implementation::peer_builder;
use crate::protocol::info_hash::InfoHash;
use crate::tracker::auth::KeyId;
use crate::tracker::auth::Key;
use crate::tracker::{self, auth, peer, statistics, torrent};

/// Authenticate `InfoHash` using optional `auth::Key`
Expand All @@ -22,11 +22,11 @@ use crate::tracker::{self, auth, peer, statistics, torrent};
/// Will return `ServerError` that wraps the `tracker::error::Error` if unable to `authenticate_request`.
pub async fn authenticate(
info_hash: &InfoHash,
auth_key_id: &Option<auth::KeyId>,
auth_key: &Option<auth::Key>,
tracker: Arc<tracker::Tracker>,
) -> Result<(), Error> {
tracker
.authenticate_request(info_hash, auth_key_id)
.authenticate_request(info_hash, auth_key)
.await
.map_err(|e| Error::TrackerError {
source: (Arc::new(e) as Arc<dyn std::error::Error + Send + Sync>).into(),
Expand All @@ -38,15 +38,15 @@ pub async fn authenticate(
/// Will return `warp::Rejection` that wraps the `ServerError` if unable to `send_announce_response`.
pub async fn handle_announce(
announce_request: request::Announce,
auth_key_id: Option<KeyId>,
auth_key: Option<Key>,
tracker: Arc<tracker::Tracker>,
) -> WebResult<impl Reply> {
debug!("http announce request: {:#?}", announce_request);

let info_hash = announce_request.info_hash;
let remote_client_ip = announce_request.peer_addr;

authenticate(&info_hash, &auth_key_id, tracker.clone()).await?;
authenticate(&info_hash, &auth_key, tracker.clone()).await?;

let mut peer = peer_builder::from_request(&announce_request, &remote_client_ip);

Expand Down Expand Up @@ -78,7 +78,7 @@ pub async fn handle_announce(
/// Will return `warp::Rejection` that wraps the `ServerError` if unable to `send_scrape_response`.
pub async fn handle_scrape(
scrape_request: request::Scrape,
auth_key_id: Option<KeyId>,
auth_key: Option<Key>,
tracker: Arc<tracker::Tracker>,
) -> WebResult<impl Reply> {
let mut files: HashMap<InfoHash, response::ScrapeEntry> = HashMap::new();
Expand All @@ -87,7 +87,7 @@ pub async fn handle_scrape(
for info_hash in &scrape_request.info_hashes {
let scrape_entry = match db.get(info_hash) {
Some(torrent_info) => {
if authenticate(info_hash, &auth_key_id, tracker.clone()).await.is_ok() {
if authenticate(info_hash, &auth_key, tracker.clone()).await.is_ok() {
let (seeders, completed, leechers) = torrent_info.get_stats();
response::ScrapeEntry {
complete: seeders,
Expand Down
Loading