Skip to content

Commit

Permalink
feat(server): add support for multiple auth tokens via env vars (#339)
Browse files Browse the repository at this point in the history
* test(fixtures): add the ability to set env variables in the scope of a single fixture

Define the custom_env function in your fixture to set environment variables.

* feat(server): add reading of tokens from file (#338)

Add support for reading a list of newline separated tokens from a file
which path is being supplied through the following environment
variables:

- AUTH_FILE
- DELETE_FILE

* docs(authentication): add new file option to readme (#338)

* test(fixtures): add auth and delete file tests and fix expiring file error

- add fixture for auth file testing
- add fixture for delete file texting
- expiring-file-upload would sometimes fail, so I increased the sleep
duration
  • Loading branch information
nydragon authored Aug 24, 2024
1 parent f0c836a commit 5d71dbd
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 8 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,16 +280,21 @@ 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
$ 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.
Expand Down
7 changes: 6 additions & 1 deletion fixtures/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions fixtures/test-expiring-file-upload/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 6 additions & 2 deletions fixtures/test-fixtures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions fixtures/test-server-auth-token-file/auth_file
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bread
brioche
baguette
naan
8 changes: 8 additions & 0 deletions fixtures/test-server-auth-token-file/config.toml
Original file line number Diff line number Diff line change
@@ -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
38 changes: 38 additions & 0 deletions fixtures/test-server-auth-token-file/test.sh
Original file line number Diff line number Diff line change
@@ -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
}
8 changes: 8 additions & 0 deletions fixtures/test-server-delete-token-file/config.toml
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions fixtures/test-server-delete-token-file/delete_file
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bread
brioche
baguette
naan
29 changes: 29 additions & 0 deletions fixtures/test-server-delete-token-file/test.sh
Original file line number Diff line number Diff line change
@@ -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
}
32 changes: 31 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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 => {
Expand All @@ -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
}
};
Expand Down
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";

0 comments on commit 5d71dbd

Please sign in to comment.