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
1 change: 1 addition & 0 deletions packages/configuration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//!
//! The current version for configuration is [`v2`].
pub mod v2;
pub mod validator;

use std::collections::HashMap;
use std::env;
Expand Down
50 changes: 50 additions & 0 deletions packages/configuration/src/v2/core.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use derive_more::{Constructor, Display};
use serde::{Deserialize, Serialize};

use super::network::Network;
use crate::v2::database::Database;
use crate::validator::{SemanticValidationError, Validator};
use crate::{AnnouncePolicy, TrackerPolicy};

#[allow(clippy::struct_excessive_bools)]
Expand Down Expand Up @@ -32,6 +34,10 @@ pub struct Core {
#[serde(default = "Core::default_private")]
pub private: bool,

// Configuration specific when the tracker is running in private mode.
#[serde(default = "Core::default_private_mode")]
pub private_mode: Option<PrivateMode>,

// Tracker policy configuration.
#[serde(default = "Core::default_tracker_policy")]
pub tracker_policy: TrackerPolicy,
Expand All @@ -54,6 +60,7 @@ impl Default for Core {
listed: Self::default_listed(),
net: Self::default_network(),
private: Self::default_private(),
private_mode: Self::default_private_mode(),
tracker_policy: Self::default_tracker_policy(),
tracker_usage_statistics: Self::default_tracker_usage_statistics(),
}
Expand Down Expand Up @@ -85,10 +92,53 @@ impl Core {
false
}

fn default_private_mode() -> Option<PrivateMode> {
if Self::default_private() {
Some(PrivateMode::default())
} else {
None
}
}

fn default_tracker_policy() -> TrackerPolicy {
TrackerPolicy::default()
}
fn default_tracker_usage_statistics() -> bool {
true
}
}

/// Configuration specific when the tracker is running in private mode.
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, Constructor, Display)]
pub struct PrivateMode {
/// A flag to disable expiration date for peer keys.
///
/// When true, if the keys is not permanent the expiration date will be
/// ignored. The key will be accepted even if it has expired.
#[serde(default = "PrivateMode::default_check_keys_expiration")]
pub check_keys_expiration: bool,
}

impl Default for PrivateMode {
fn default() -> Self {
Self {
check_keys_expiration: Self::default_check_keys_expiration(),
}
}
}

impl PrivateMode {
fn default_check_keys_expiration() -> bool {
true
}
}

impl Validator for Core {
fn validate(&self) -> Result<(), SemanticValidationError> {
if self.private_mode.is_some() && !self.private {
return Err(SemanticValidationError::UselessPrivateModeSection);
}

Ok(())
}
}
7 changes: 7 additions & 0 deletions packages/configuration/src/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ use self::health_check_api::HealthCheckApi;
use self::http_tracker::HttpTracker;
use self::tracker_api::HttpApi;
use self::udp_tracker::UdpTracker;
use crate::validator::{SemanticValidationError, Validator};
use crate::{Error, Info, Metadata, Version};

/// This configuration version
Expand Down Expand Up @@ -394,6 +395,12 @@ impl Configuration {
}
}

impl Validator for Configuration {
fn validate(&self) -> Result<(), SemanticValidationError> {
self.core.validate()
}
}

#[cfg(test)]
mod tests {

Expand Down
19 changes: 19 additions & 0 deletions packages/configuration/src/validator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Trait to validate semantic errors.
//!
//! Errors could involve more than one configuration option. Some configuration
//! combinations can be incompatible.
use thiserror::Error;

/// Errors that can occur validating the configuration.
#[derive(Error, Debug)]
pub enum SemanticValidationError {
#[error("Private mode section in configuration can only be included when the tracker is running in private mode.")]
UselessPrivateModeSection,
}

pub trait Validator {
/// # Errors
///
/// Will return an error if the configuration is invalid.
fn validate(&self) -> Result<(), SemanticValidationError>;
}
9 changes: 9 additions & 0 deletions src/bootstrap/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use std::sync::Arc;

use torrust_tracker_clock::static_time;
use torrust_tracker_configuration::validator::Validator;
use torrust_tracker_configuration::Configuration;
use tracing::info;

Expand All @@ -24,10 +25,18 @@ use crate::core::Tracker;
use crate::shared::crypto::ephemeral_instance_keys;

/// It loads the configuration from the environment and builds the main domain [`Tracker`] struct.
///
/// # Panics
///
/// Setup can file if the configuration is invalid.
#[must_use]
pub fn setup() -> (Configuration, Arc<Tracker>) {
let configuration = initialize_configuration();

if let Err(e) = configuration.validate() {
panic!("Configuration error: {e}");
}

let tracker = initialize_with_configuration(&configuration);

info!("Configuration:\n{}", configuration.clone().mask_secrets().to_json());
Expand Down
12 changes: 6 additions & 6 deletions src/core/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//! Tracker keys are tokens used to authenticate the tracker clients when the tracker runs
//! in `private` or `private_listed` modes.
//!
//! There are services to [`generate_key`] and [`verify_key`] authentication keys.
//! There are services to [`generate_key`] and [`verify_key_expiration`] authentication keys.
//!
//! Authentication keys are used only by [`HTTP`](crate::servers::http) trackers. All keys have an expiration time, that means
//! they are only valid during a period of time. After that time the expiring key will no longer be valid.
Expand Down Expand Up @@ -33,7 +33,7 @@
//!
//! // And you can later verify it with:
//!
//! assert!(auth::verify_key(&expiring_key).is_ok());
//! assert!(auth::verify_key_expiration(&expiring_key).is_ok());
//! ```

use std::panic::Location;
Expand Down Expand Up @@ -106,7 +106,7 @@ pub fn generate_key(lifetime: Option<Duration>) -> PeerKey {
///
/// - `Error::KeyExpired` if `auth_key.valid_until` is past the `current_time`.
/// - `Error::KeyInvalid` if `auth_key.valid_until` is past the `None`.
pub fn verify_key(auth_key: &PeerKey) -> Result<(), Error> {
pub fn verify_key_expiration(auth_key: &PeerKey) -> Result<(), Error> {
let current_time: DurationSinceUnixEpoch = CurrentClock::now();

match auth_key.valid_until {
Expand Down Expand Up @@ -322,7 +322,7 @@ mod tests {
fn should_be_generated_with_a_expiration_time() {
let expiring_key = auth::generate_key(Some(Duration::new(9999, 0)));

assert!(auth::verify_key(&expiring_key).is_ok());
assert!(auth::verify_key_expiration(&expiring_key).is_ok());
}

#[test]
Expand All @@ -336,12 +336,12 @@ mod tests {
// Mock the time has passed 10 sec.
clock::Stopped::local_add(&Duration::from_secs(10)).unwrap();

assert!(auth::verify_key(&expiring_key).is_ok());
assert!(auth::verify_key_expiration(&expiring_key).is_ok());

// Mock the time has passed another 10 sec.
clock::Stopped::local_add(&Duration::from_secs(10)).unwrap();

assert!(auth::verify_key(&expiring_key).is_err());
assert!(auth::verify_key_expiration(&expiring_key).is_err());
}
}
}
32 changes: 30 additions & 2 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,16 @@ impl Tracker {
location: Location::caller(),
key: Box::new(key.clone()),
}),
Some(key) => auth::verify_key(key),
Some(key) => match self.config.private_mode {
Some(private_mode) => {
if private_mode.check_keys_expiration {
return auth::verify_key_expiration(key);
}

Ok(())
}
None => auth::verify_key_expiration(key),
},
}
}

Expand Down Expand Up @@ -1779,8 +1788,9 @@ mod tests {
use std::time::Duration;

use torrust_tracker_clock::clock::Time;
use torrust_tracker_configuration::v2::core::PrivateMode;

use crate::core::auth;
use crate::core::auth::{self, Key};
use crate::core::tests::the_tracker::private_tracker;
use crate::CurrentClock;

Expand Down Expand Up @@ -1829,6 +1839,24 @@ mod tests {
assert!(tracker.verify_auth_key(&expiring_key.key()).await.is_ok());
}

#[tokio::test]
async fn it_should_accept_an_expired_key_when_checking_expiration_is_disabled_in_configuration() {
let mut tracker = private_tracker();

tracker.config.private_mode = Some(PrivateMode {
check_keys_expiration: false,
});

let past_time = Some(Duration::ZERO);

let expiring_key = tracker
.add_auth_key(Key::new("YZSl4lMZupRuOpSRC3krIKR5BPB14nrJ").unwrap(), past_time)
.await
.unwrap();

assert!(tracker.authenticate(&expiring_key.key()).await.is_ok());
}

#[tokio::test]
async fn it_should_fail_verifying_an_unregistered_authentication_key() {
let tracker = private_tracker();
Expand Down