rustypaste/src/config.rs

280 lines
9.4 KiB
Rust
Raw Permalink Normal View History

use crate::mime::MimeMatcher;
use crate::random::RandomURLConfig;
use crate::{AUTH_TOKEN_ENV, DELETE_TOKEN_ENV};
use byte_unit::Byte;
use config::{self, ConfigError};
use std::collections::HashSet;
use std::env;
use std::path::{Path, PathBuf};
use std::time::Duration;
/// Configuration values.
2021-07-24 10:47:24 +00:00
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct Config {
/// Configuration settings.
#[serde(rename = "config")]
pub settings: Option<Settings>,
/// Server configuration.
pub server: ServerConfig,
/// Paste configuration.
pub paste: PasteConfig,
/// Landing page configuration.
pub landing_page: Option<LandingPageConfig>,
}
/// General settings for configuration.
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct Settings {
/// Refresh rate of the configuration file.
#[serde(with = "humantime_serde")]
pub refresh_rate: Duration,
}
/// Server configuration.
2021-07-24 10:47:24 +00:00
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct ServerConfig {
/// The socket address to bind.
pub address: String,
/// URL that can be used to access the server externally.
pub url: Option<String>,
/// Number of workers to start.
pub workers: Option<usize>,
/// Maximum content length.
pub max_content_length: Byte,
/// Storage path.
pub upload_path: PathBuf,
/// Request timeout.
#[serde(default, with = "humantime_serde")]
pub timeout: Option<Duration>,
/// Authentication token.
#[deprecated(note = "use [server].auth_tokens instead")]
pub auth_token: Option<String>,
/// Authentication tokens.
pub auth_tokens: Option<HashSet<String>>,
/// Expose version.
pub expose_version: Option<bool>,
/// Landing page text.
#[deprecated(note = "use the [landing_page] table")]
pub landing_page: Option<String>,
/// Landing page content-type.
#[deprecated(note = "use the [landing_page] table")]
pub landing_page_content_type: Option<String>,
feat(server): support handling spaces in filenames (#107) * feat(server): add url encoding - Introduced an option to enable encoding for filenames in the returned URL. - Modified the encoding approach to specifically target whitespaces, replacing them with `%20`. - Included unit tests to validate this encoding approach. - Added fixture tests for broader functional verification. - Removed the `urlencoding` dependency from `Cargo.toml`. * Refactor code and fixtures for better compliance and consistency - Fixed errors in the fixture configuration to ensure tests run as expected. - Reformatted line endings for consistency across the codebase. - Made necessary adjustments to adhere to Clippy's recommendations. * Enhance whitespace handling options for filenames - Implemented a configuration choice for space handling in filenames: encode, replace with underscores or none. - Added corresponding unit and fixture tests. * Remove redundant function call Fixed misplaced line as per @tessus's review feedback, preventing duplicate handling of spaces in filenames. * Delete test file with spaces.txt * Refactor filename space handling - Introduced `SpaceHandling` enum to manage filename spaces. - Updated server.rs and paste.rs to utilize the new method. - Added unit tests for the new filename handling method. - Adjusted fixture tests to reflect these changes. * Move filename processing into `SpaceHandling` enum - Relocated filename processing logic to reside within the `SpaceHandling` enum, enhancing encapsulation. - Updated calls in `server.rs` and `paste.rs` to use the new method structure. - Adjusted unit tests to align with the refactored function location and call pattern. * Refactor based on review suggestions - Incorporated the inline suggestions made by @tessus, removing the unnecessary one-line assignments. - Ensured the code adheres to Rust guidelines by running clippy and fmt. * chore(deps): revert changes in Cargo.lock * chore(config): replace spaces as default * refactor(config): move SpaceHandling to config module --------- Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2023-08-26 12:35:13 +00:00
/// Handle spaces either via encoding or replacing.
pub handle_spaces: Option<SpaceHandlingConfig>,
feat(server): add an endpoint for retrieving a list of files (#94) * Start * Wip * Implement path based JSON index * Remove json_index_path * Return datetime stamp instead of relative time * Add file size to list item * Add auth check when retrieving JSON index * Make json index path hardcoded * Test (currently failing) * Fix test for test_json_list * Clippy fix * Revert cargo to original versions with only needed changes * Add detail about auth guard affecting list route * Change json_index_path to expose_list * Remove unneeded linebreak * Remove unnecessary import * Remove unnecessary space at end of line * Move config check after auth check * Use new auth check syntax, add docs to struct, rename test_json_list to test_list * Replace chrono usage with uts2ts * Check list result in test * Add example to README * Upgrade serde_json to 1.0.103 * Add linebreak * Remove unneeded clone * Remove extra nl * Update README.md * Update README.md Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com> * Update README.md Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com> * Remove serde_json * Set default config to false for expose_list * Apply suggestions from code review Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com> * Check that option is value in test_list * Update Cargo.toml Co-authored-by: Helmut K. C. Tessarek <tessarek@evermeet.cx> * Update cargo.lock * Use expect() to check file name * Remove underscore from list item struct * Keep comma after last line * refactor(server): rename ListItem fields * test(fixtures): add fixture test for listing files * test push * remove file again * chop off ts from filename and minor refactor * update README * docs(readme): fix capitalization * refactor(server): clean up list implementation --------- Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com> Co-authored-by: Helmut K. C. Tessarek <tessarek@evermeet.cx>
2023-08-07 10:55:13 +00:00
/// Path of the JSON index.
pub expose_list: Option<bool>,
/// Authentication tokens for deleting.
pub delete_tokens: Option<HashSet<String>>,
}
feat(server): support handling spaces in filenames (#107) * feat(server): add url encoding - Introduced an option to enable encoding for filenames in the returned URL. - Modified the encoding approach to specifically target whitespaces, replacing them with `%20`. - Included unit tests to validate this encoding approach. - Added fixture tests for broader functional verification. - Removed the `urlencoding` dependency from `Cargo.toml`. * Refactor code and fixtures for better compliance and consistency - Fixed errors in the fixture configuration to ensure tests run as expected. - Reformatted line endings for consistency across the codebase. - Made necessary adjustments to adhere to Clippy's recommendations. * Enhance whitespace handling options for filenames - Implemented a configuration choice for space handling in filenames: encode, replace with underscores or none. - Added corresponding unit and fixture tests. * Remove redundant function call Fixed misplaced line as per @tessus's review feedback, preventing duplicate handling of spaces in filenames. * Delete test file with spaces.txt * Refactor filename space handling - Introduced `SpaceHandling` enum to manage filename spaces. - Updated server.rs and paste.rs to utilize the new method. - Added unit tests for the new filename handling method. - Adjusted fixture tests to reflect these changes. * Move filename processing into `SpaceHandling` enum - Relocated filename processing logic to reside within the `SpaceHandling` enum, enhancing encapsulation. - Updated calls in `server.rs` and `paste.rs` to use the new method structure. - Adjusted unit tests to align with the refactored function location and call pattern. * Refactor based on review suggestions - Incorporated the inline suggestions made by @tessus, removing the unnecessary one-line assignments. - Ensured the code adheres to Rust guidelines by running clippy and fmt. * chore(deps): revert changes in Cargo.lock * chore(config): replace spaces as default * refactor(config): move SpaceHandling to config module --------- Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2023-08-26 12:35:13 +00:00
/// Enum representing different strategies for handling spaces in filenames.
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SpaceHandlingConfig {
/// Represents encoding spaces (e.g., using "%20").
Encode,
/// Represents replacing spaces with underscores.
Replace,
}
impl SpaceHandlingConfig {
/// Processes the given filename based on the specified space handling strategy.
pub fn process_filename(&self, file_name: &str) -> String {
match self {
Self::Encode => file_name.replace(' ', "%20"),
Self::Replace => file_name.replace(' ', "_"),
}
}
}
/// Landing page configuration.
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct LandingPageConfig {
/// Landing page text.
pub text: Option<String>,
/// Landing page file.
pub file: Option<String>,
/// Landing page content-type
pub content_type: Option<String>,
}
/// Paste configuration.
2021-07-24 10:47:24 +00:00
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct PasteConfig {
/// Random URL configuration.
pub random_url: Option<RandomURLConfig>,
/// Default file extension.
pub default_extension: String,
/// Media type override options.
#[serde(default)]
pub mime_override: Vec<MimeMatcher>,
/// Media type blacklist.
#[serde(default)]
pub mime_blacklist: Vec<String>,
/// Allow duplicate uploads.
pub duplicate_files: Option<bool>,
/// Default expiry time.
#[serde(default, with = "humantime_serde")]
pub default_expiry: Option<Duration>,
/// Delete expired files.
pub delete_expired_files: Option<CleanupConfig>,
}
/// Cleanup configuration.
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
pub struct CleanupConfig {
/// Enable cleaning up.
pub enabled: bool,
/// Interval between clean-ups.
#[serde(default, with = "humantime_serde")]
pub interval: Duration,
}
/// Type of access token.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum TokenType {
/// Token for authentication.
Auth,
/// Token for DELETE endpoint.
Delete,
}
impl Config {
/// Parses the config file and returns the values.
pub fn parse(path: &Path) -> Result<Config, ConfigError> {
2022-02-24 21:10:52 +00:00
config::Config::builder()
.add_source(config::File::from(path))
.add_source(config::Environment::default().separator("__"))
.build()?
.try_deserialize()
}
/// Retrieves all configured auth/delete tokens.
pub fn get_tokens(&self, token_type: TokenType) -> Option<HashSet<String>> {
let mut tokens = match token_type {
TokenType::Auth => {
let mut tokens: HashSet<_> = self.server.auth_tokens.clone().unwrap_or_default();
#[allow(deprecated)]
if let Some(token) = &self.server.auth_token {
tokens.insert(token.to_string());
}
if let Ok(env_token) = env::var(AUTH_TOKEN_ENV) {
tokens.insert(env_token);
}
tokens
}
TokenType::Delete => {
let mut tokens: HashSet<_> = self.server.delete_tokens.clone().unwrap_or_default();
if let Ok(env_token) = env::var(DELETE_TOKEN_ENV) {
tokens.insert(env_token);
}
tokens
}
};
// filter out blank tokens
tokens.retain(|v| !v.trim().is_empty());
Some(tokens).filter(|v| !v.is_empty())
}
/// Print deprecation warnings.
#[allow(deprecated)]
pub fn warn_deprecation(&self) {
if self.server.auth_token.is_some() {
warn!("[server].auth_token is deprecated, please use [server].auth_tokens");
}
if self.server.landing_page.is_some() {
warn!("[server].landing_page is deprecated, please use [landing_page].text");
}
if self.server.landing_page_content_type.is_some() {
warn!(
"[server].landing_page_content_type is deprecated, please use [landing_page].content_type"
);
}
if let Some(random_url) = &self.paste.random_url {
if random_url.enabled.is_some() {
warn!(
"[paste].random_url.enabled is deprecated, disable it by commenting out [paste].random_url"
);
}
}
}
}
2021-07-24 10:47:24 +00:00
#[cfg(test)]
mod tests {
2021-07-24 10:47:24 +00:00
use super::*;
use std::env;
#[test]
fn test_parse_config() -> Result<(), ConfigError> {
let config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("config.toml");
env::set_var("SERVER__ADDRESS", "0.0.1.1");
let config = Config::parse(&config_path)?;
2021-07-24 10:47:24 +00:00
assert_eq!("0.0.1.1", config.server.address);
Ok(())
}
#[test]
#[allow(deprecated)]
fn test_parse_deprecated_config() -> Result<(), ConfigError> {
let config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("config.toml");
env::set_var("SERVER__ADDRESS", "0.0.1.1");
let mut config = Config::parse(&config_path)?;
config.paste.random_url = Some(RandomURLConfig {
enabled: Some(true),
..RandomURLConfig::default()
});
assert_eq!("0.0.1.1", config.server.address);
config.warn_deprecation();
Ok(())
}
feat(server): support handling spaces in filenames (#107) * feat(server): add url encoding - Introduced an option to enable encoding for filenames in the returned URL. - Modified the encoding approach to specifically target whitespaces, replacing them with `%20`. - Included unit tests to validate this encoding approach. - Added fixture tests for broader functional verification. - Removed the `urlencoding` dependency from `Cargo.toml`. * Refactor code and fixtures for better compliance and consistency - Fixed errors in the fixture configuration to ensure tests run as expected. - Reformatted line endings for consistency across the codebase. - Made necessary adjustments to adhere to Clippy's recommendations. * Enhance whitespace handling options for filenames - Implemented a configuration choice for space handling in filenames: encode, replace with underscores or none. - Added corresponding unit and fixture tests. * Remove redundant function call Fixed misplaced line as per @tessus's review feedback, preventing duplicate handling of spaces in filenames. * Delete test file with spaces.txt * Refactor filename space handling - Introduced `SpaceHandling` enum to manage filename spaces. - Updated server.rs and paste.rs to utilize the new method. - Added unit tests for the new filename handling method. - Adjusted fixture tests to reflect these changes. * Move filename processing into `SpaceHandling` enum - Relocated filename processing logic to reside within the `SpaceHandling` enum, enhancing encapsulation. - Updated calls in `server.rs` and `paste.rs` to use the new method structure. - Adjusted unit tests to align with the refactored function location and call pattern. * Refactor based on review suggestions - Incorporated the inline suggestions made by @tessus, removing the unnecessary one-line assignments. - Ensured the code adheres to Rust guidelines by running clippy and fmt. * chore(deps): revert changes in Cargo.lock * chore(config): replace spaces as default * refactor(config): move SpaceHandling to config module --------- Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2023-08-26 12:35:13 +00:00
#[test]
fn test_space_handling() {
let processed_filename =
SpaceHandlingConfig::Replace.process_filename("file with spaces.txt");
assert_eq!("file_with_spaces.txt", processed_filename);
let encoded_filename = SpaceHandlingConfig::Encode.process_filename("file with spaces.txt");
assert_eq!("file%20with%20spaces.txt", encoded_filename);
}
#[test]
fn test_get_tokens() -> Result<(), ConfigError> {
let config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("config.toml");
env::set_var("AUTH_TOKEN", "env_auth");
env::set_var("DELETE_TOKEN", "env_delete");
let mut config = Config::parse(&config_path)?;
// empty tokens will be filtered
config.server.auth_tokens =
Some(["may_the_force_be_with_you".to_string(), "".to_string()].into());
config.server.delete_tokens = Some(["i_am_your_father".to_string(), "".to_string()].into());
assert_eq!(
Some(HashSet::from([
"env_auth".to_string(),
"may_the_force_be_with_you".to_string()
])),
config.get_tokens(TokenType::Auth)
);
assert_eq!(
Some(HashSet::from([
"env_delete".to_string(),
"i_am_your_father".to_string()
])),
config.get_tokens(TokenType::Delete)
);
env::remove_var("AUTH_TOKEN");
env::remove_var("DELETE_TOKEN");
// `get_tokens` returns `None` if no tokens are configured
config.server.auth_tokens = Some([" ".to_string()].into());
config.server.delete_tokens = Some(HashSet::new());
assert_eq!(None, config.get_tokens(TokenType::Auth));
assert_eq!(None, config.get_tokens(TokenType::Delete));
Ok(())
}
2021-07-24 10:47:24 +00:00
}