Skip to content
Closed
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
6 changes: 4 additions & 2 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ parameters:
env(APP_DEBUG): 0
env(LOCK_DSN): 'flock'
env(TZ): ''
_strava_refresh_token: '%env(string:STRAVA_REFRESH_TOKEN)%'
_strava_client_secret: '%env(string:STRAVA_CLIENT_SECRET)%'

services:
# default configuration for services in *this* file
Expand Down Expand Up @@ -156,11 +158,11 @@ services:

App\Domain\Strava\StravaClientSecret:
factory: [ null, 'fromString' ]
arguments: ['%env(string:STRAVA_CLIENT_SECRET)%']
arguments: ['%env(default:_strava_client_secret:file:STRAVA_CLIENT_SECRET_FILE)%']

App\Domain\Strava\StravaRefreshToken:
factory: [ null, 'fromString' ]
arguments: ['%env(string:STRAVA_REFRESH_TOKEN)%']
arguments: ['%env(default:_strava_refresh_token:file:STRAVA_REFRESH_TOKEN_FILE)%']

App\Domain\Athlete\AthleteBirthDate:
factory: [ null, 'fromString' ]
Expand Down
6 changes: 6 additions & 0 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ TZ=Etc/GMT
# CADDY_LOG_LEVEL=ERROR
```

> [!IMPORTANT] Instead of passing secrets directly via the `STRAVA_CLIENT_SECRET` and `STRAVA_REFRESH_TOKEN` environment variables, you can use [Docker Compose secrets](https://docs.docker.com/compose/how-tos/use-secrets/).
>
> Define `STRAVA_CLIENT_SECRET_FILE` and `STRAVA_REFRESH_TOKEN_FILE` to point to the secret files (typically located in /run/secrets/). When the standard environment variables are not set, the application will automatically read the values from these files.
>
> This approach is recommended when running the application with Docker Compose, as it avoids exposing sensitive values in environment variables.

## config.yaml

[include](../configuration/config-yaml-example.md ':include')
Expand Down
19 changes: 17 additions & 2 deletions src/Console/Debug/DebugEnvironmentConsoleCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
->setRows([
['APP_VERSION', AppVersion::getSemanticVersion()],
['STRAVA_CLIENT_ID', $autoRedactSensitiveInfo ? $redactedString : getenv('STRAVA_CLIENT_ID')],
['STRAVA_CLIENT_SECRET', $autoRedactSensitiveInfo ? $redactedString : getenv('STRAVA_CLIENT_SECRET')],
['STRAVA_REFRESH_TOKEN', $autoRedactSensitiveInfo ? $redactedString : getenv('STRAVA_REFRESH_TOKEN')],
['STRAVA_CLIENT_SECRET', $autoRedactSensitiveInfo ? $redactedString : $this->getenvOrFile('STRAVA_CLIENT_SECRET')],
['STRAVA_REFRESH_TOKEN', $autoRedactSensitiveInfo ? $redactedString : $this->getenvOrFile('STRAVA_REFRESH_TOKEN')],
['TZ', getenv('TZ')],
]);
$table->render();
Expand All @@ -66,4 +66,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int

return Command::SUCCESS;
}

protected function getenvOrFile(string $var): ?string
{
$value = getenv($var);
if (false !== $value && '' !== $value) {
return $value;
}

$file = getenv($var.'_FILE');
if ($file && is_readable($file)) {
return trim((string) file_get_contents($file));
}

return null;
}
}
73 changes: 73 additions & 0 deletions tests/Console/Debug/DebugEnvironmentConsoleCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class DebugEnvironmentConsoleCommandTest extends ConsoleCommandTestCase
use MatchesSnapshots;

private DebugEnvironmentConsoleCommand $debugEnvironmentConsoleCommand;
private array $tempFiles = [];

public function testExecute(): void
{
Expand Down Expand Up @@ -52,4 +53,76 @@ protected function getConsoleCommand(): Command
{
return $this->debugEnvironmentConsoleCommand;
}

private function createTempFile(string $contents): string
{
$tmp = tempnam(sys_get_temp_dir(), 'testenv_');
file_put_contents($tmp, $contents);
$this->tempFiles[] = $tmp;

return $tmp;
}

public function testGetenvOrFilePrefersEnvOverFile(): void
{
// create a file that would contain a different value
$file = $this->createTempFile("file-secret\n");
putenv('STRAVA_CLIENT_SECRET=env-secret');
putenv('STRAVA_CLIENT_SECRET_FILE='.$file);

$command = $this->getCommandInApplication('app:debug:environment');
$commandTester = new CommandTester($command);

$commandTester->execute([
'command' => $command->getName(),
]);

$display = $commandTester->getDisplay();

// env value should be used, file content must NOT be shown
$this->assertStringContainsString('env-secret', $display);
$this->assertStringNotContainsString('file-secret', $display);
}

public function testGetenvOrFileUsesFileWhenEnvMissing(): void
{
// ensure env var is not set
putenv('STRAVA_REFRESH_TOKEN=');

// create file and point *_FILE to it
$file = $this->createTempFile("file-refresh-token\n");
putenv('STRAVA_REFRESH_TOKEN_FILE='.$file);

$command = $this->getCommandInApplication('app:debug:environment');
$commandTester = new CommandTester($command);

$commandTester->execute([
'command' => $command->getName(),
]);

$display = $commandTester->getDisplay();

// file content should appear
$this->assertStringContainsString('file-refresh-token', $display);
}

public function testMissingEnvAndMissingFileShowsNullValue(): void
{
// make sure both env and file are absent
putenv('STRAVA_CLIENT_ID=');
putenv('STRAVA_CLIENT_ID_FILE=');

$command = $this->getCommandInApplication('app:debug:environment');
$commandTester = new CommandTester($command);

$commandTester->execute([
'command' => $command->getName(),
]);

$display = $commandTester->getDisplay();

// When value is null, table will contain an empty or 'NULL' like representation depending on Table rendering.
// We at least assert the APP_VERSION header is present and command runs successfully.
$this->assertStringContainsString('APP_VERSION', $display);
}
}