diff --git a/README.md b/README.md index 21f918dc..c724c91b 100644 --- a/README.md +++ b/README.md @@ -280,6 +280,8 @@ If the configuration file is not found in the current directory, specify it via $ CONFIG="$HOME/.rustypaste.toml" rustypaste ``` +#### Authentication + To enable basic HTTP auth, set the `AUTH_TOKEN` environment variable (via `.env`): ```sh @@ -287,9 +289,12 @@ $ echo "AUTH_TOKEN=$(openssl rand -base64 16)" > .env $ rustypaste ``` -You can also set multiple auth tokens via the array field `[server].auth_tokens` in your `config.toml`. +There are 2 options for setting multiple auth tokens: + +- Via the array field `[server].auth_tokens` in your `config.toml`. +- Or by writing a newline separated list to a file and passing its path to rustypaste via `AUTH_TOKENS_FILE` and `DELETE_TOKENS_FILE` respectively. -> If neither `AUTH_TOKEN` nor `[server].auth_tokens` are set, the server will not require any authentication. +> If neither `AUTH_TOKEN`, `AUTH_TOKENS_FILE` nor `[server].auth_tokens` are set, the server will not require any authentication. > > Exception is the `DELETE` endpoint, which requires at least one token to be set. See [deleting files from server](#delete-file-from-server) for more information. diff --git a/fixtures/README.md b/fixtures/README.md index 6158d7ae..33313ede 100644 --- a/fixtures/README.md +++ b/fixtures/README.md @@ -7,7 +7,7 @@ This directory contains the [test fixtures](https://en.wikipedia.org/wiki/Test_f 1. Build the project in debug mode: `cargo build` 2. Execute the runner script in this directory: `./test-fixtures.sh` -On `macOS` you need to have [coreutils](https://www.gnu.org/software/coreutils/) installed to run the script. +On `macOS` you need to have [coreutils](https://www.gnu.org/software/coreutils/) installed to run the script. The simplest way is to install it via [Homebrew](https://brew.sh/): `brew install coreutils` ### Adding new fixtures @@ -28,6 +28,11 @@ test-file-upload/ ```sh #!/usr/bin/env bash +# Optional +custom_env() { + # setting environment variables such as AUTH_TOKEN or AUTH_TOKENS_FILE +} + setup() { # preparation } diff --git a/fixtures/test-expiring-file-upload/test.sh b/fixtures/test-expiring-file-upload/test.sh index c998c00f..22a7ed7a 100755 --- a/fixtures/test-expiring-file-upload/test.sh +++ b/fixtures/test-expiring-file-upload/test.sh @@ -3,13 +3,13 @@ content="test data" setup() { - echo "$content" > file + echo "$content" >file } run_test() { file_url=$(curl -s -F "file=@file" -H "expire:1s" localhost:8000) test "$content" = "$(cat upload/file.txt.*)" - sleep 2 + sleep 3 result="$(curl -s $file_url)" test "file is not found or expired :(" = "$result" diff --git a/fixtures/test-fixtures.sh b/fixtures/test-fixtures.sh index 71c40c02..2e582317 100755 --- a/fixtures/test-fixtures.sh +++ b/fixtures/test-fixtures.sh @@ -9,11 +9,13 @@ NC='\033[0m' run_fixture() { cd "$FIXTURE_DIR/$1" || exit 1 source "test.sh" + type custom_env &>/dev/null && custom_env NO_COLOR=1 CONFIG=config.toml "$PROJECT_DIR/target/debug/rustypaste" & SERVER_PID=$! trap 'kill -9 "$SERVER_PID" && wait "$SERVER_PID" 2> /dev/null' RETURN sleep 1 - ( set -e; + ( + set -e setup run_test teardown @@ -24,7 +26,9 @@ run_fixture() { main() { find * -maxdepth 0 -type d -print0 | while IFS= read -r -d '' fixture; do - run_fixture "$fixture" + # Since we are creating a subshell, all environment variables created by custom_env will be lost + # Return code is preserved + (run_fixture "$fixture") exit_status=$? if [ "$exit_status" -eq 0 ]; then echo -e "[${GREEN}ok${NC}] $fixture" diff --git a/fixtures/test-server-auth-token-file/auth_file b/fixtures/test-server-auth-token-file/auth_file new file mode 100644 index 00000000..20cd88cb --- /dev/null +++ b/fixtures/test-server-auth-token-file/auth_file @@ -0,0 +1,4 @@ +bread +brioche +baguette +naan diff --git a/fixtures/test-server-auth-token-file/config.toml b/fixtures/test-server-auth-token-file/config.toml new file mode 100644 index 00000000..13838f84 --- /dev/null +++ b/fixtures/test-server-auth-token-file/config.toml @@ -0,0 +1,8 @@ +[server] +address = "127.0.0.1:8000" +max_content_length = "10MB" +upload_path = "./upload" + +[paste] +default_extension = "txt" +duplicate_files = false diff --git a/fixtures/test-server-auth-token-file/test.sh b/fixtures/test-server-auth-token-file/test.sh new file mode 100644 index 00000000..6a943d92 --- /dev/null +++ b/fixtures/test-server-auth-token-file/test.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +content1="test data" +content2="other test" + +custom_env() { + export AUTH_TOKENS_FILE=./auth_file +} + +setup() { + echo "$content1" >file1 + echo "$content2" >file2 +} + +run_test() { + file_url=$(curl -s -F "file=@file1" -H "Authorization: bread" localhost:8000) + test "$content1" = "$(cat upload/file1.txt)" + sleep 2 + + result="$(curl -s $file_url)" + test "$content1" = "$result" + + file_url=$(curl -s -F "file=@file2" -H "Authorization: naan" localhost:8000) + test "$content2" = "$(cat upload/file2.txt)" + sleep 2 + + result="$(curl -s $file_url)" + test "$content2" = "$result" + + result=$(curl -s -F "file=@file2" -H "Authorization: tomato" localhost:8000) + test "$result" = "unauthorized" +} + +teardown() { + rm file1 + rm file2 + rm -r upload +} diff --git a/fixtures/test-server-delete-token-file/config.toml b/fixtures/test-server-delete-token-file/config.toml new file mode 100644 index 00000000..13838f84 --- /dev/null +++ b/fixtures/test-server-delete-token-file/config.toml @@ -0,0 +1,8 @@ +[server] +address = "127.0.0.1:8000" +max_content_length = "10MB" +upload_path = "./upload" + +[paste] +default_extension = "txt" +duplicate_files = false diff --git a/fixtures/test-server-delete-token-file/delete_file b/fixtures/test-server-delete-token-file/delete_file new file mode 100644 index 00000000..20cd88cb --- /dev/null +++ b/fixtures/test-server-delete-token-file/delete_file @@ -0,0 +1,4 @@ +bread +brioche +baguette +naan diff --git a/fixtures/test-server-delete-token-file/test.sh b/fixtures/test-server-delete-token-file/test.sh new file mode 100644 index 00000000..706dd1a5 --- /dev/null +++ b/fixtures/test-server-delete-token-file/test.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +content1="test data" +del_resp="file deleted" + +custom_env() { + export DELETE_TOKENS_FILE=./delete_file +} + +setup() { + echo "$content" >file +} + +run_test() { + file_url=$(curl -s -F "file=@file" localhost:8000) + test "$del_rep"="$(curl -s -H "Authorization: naan" -X DELETE $file_url)" + + sleep 2 + file_url=$(curl -s -F "file=@file" localhost:8000) + test "$del_re"="$(curl -s -H "Authorization: bread" -X DELETE $file_url)" + + file_url=$(curl -s -F "file=@file" localhost:8000) + test "unauthorized"=$(curl -s -H "Authorization: tomato" -X DELETE $file_url) +} + +teardown() { + rm file + rm -r upload +} diff --git a/src/config.rs b/src/config.rs index a7aac7e2..33429955 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,10 +1,11 @@ use crate::mime::MimeMatcher; use crate::random::RandomURLConfig; -use crate::{AUTH_TOKEN_ENV, DELETE_TOKEN_ENV}; +use crate::{AUTH_TOKENS_FILE_ENV, AUTH_TOKEN_ENV, DELETE_TOKENS_FILE_ENV, DELETE_TOKEN_ENV}; use byte_unit::Byte; use config::{self, ConfigError}; use std::collections::HashSet; use std::env; +use std::fs::read_to_string; use std::path::{Path, PathBuf}; use std::time::Duration; @@ -162,6 +163,21 @@ impl Config { if let Ok(env_token) = env::var(AUTH_TOKEN_ENV) { tokens.insert(env_token); } + if let Ok(env_path) = env::var(AUTH_TOKENS_FILE_ENV) { + match read_to_string(&env_path) { + Ok(s) => { + s.lines().filter(|l| !l.trim().is_empty()).for_each(|l| { + tokens.insert(l.to_string()); + }); + } + Err(e) => { + error!( + "failed to read tokens from authentication file ({env_path}) ({e})" + ); + } + }; + } + tokens } TokenType::Delete => { @@ -170,6 +186,20 @@ impl Config { if let Ok(env_token) = env::var(DELETE_TOKEN_ENV) { tokens.insert(env_token); } + + if let Ok(env_path) = env::var(DELETE_TOKENS_FILE_ENV) { + match read_to_string(&env_path) { + Ok(s) => { + s.lines().filter(|l| !l.trim().is_empty()).for_each(|l| { + tokens.insert(l.to_string()); + }); + } + Err(e) => { + error!("failed to read deletion tokens from file ({env_path}) ({e})"); + } + }; + } + tokens } }; diff --git a/src/lib.rs b/src/lib.rs index 731ad8bd..dfe664b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,5 +41,11 @@ pub const CONFIG_ENV: &str = "CONFIG"; /// Environment variable for setting the authentication token. pub const AUTH_TOKEN_ENV: &str = "AUTH_TOKEN"; +/// Environment variable for the path to a file containing multiple authentication token. +pub const AUTH_TOKENS_FILE_ENV: &str = "AUTH_TOKENS_FILE"; + /// Environment variable for setting the deletion token. pub const DELETE_TOKEN_ENV: &str = "DELETE_TOKEN"; + +/// Environment variable for the path to a file containing multiple deletion token. +pub const DELETE_TOKENS_FILE_ENV: &str = "DELETE_TOKENS_FILE";