Skip to content

Commit 733539d

Browse files
committed
✨ Allow to use STRAVA_CLIENT_SECRET_FILE and STRAVA_REFRESH_TOKEN_FILE instead of STRAVA_CLIENT_SECRET and STRAVA_REFRESH_TOKEN
1 parent 280ef2e commit 733539d

File tree

4 files changed

+100
-4
lines changed

4 files changed

+100
-4
lines changed

config/services.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ parameters:
77
env(APP_DEBUG): 0
88
env(LOCK_DSN): 'flock'
99
env(TZ): ''
10+
_strava_refresh_token: '%env(string:STRAVA_REFRESH_TOKEN)%'
11+
_strava_client_secret: '%env(string:STRAVA_CLIENT_SECRET)%'
1012

1113
services:
1214
# default configuration for services in *this* file
@@ -156,11 +158,11 @@ services:
156158

157159
App\Domain\Strava\StravaClientSecret:
158160
factory: [ null, 'fromString' ]
159-
arguments: ['%env(string:STRAVA_CLIENT_SECRET)%']
161+
arguments: ['%env(default:_strava_client_secret:file:STRAVA_CLIENT_SECRET_FILE)%']
160162

161163
App\Domain\Strava\StravaRefreshToken:
162164
factory: [ null, 'fromString' ]
163-
arguments: ['%env(string:STRAVA_REFRESH_TOKEN)%']
165+
arguments: ['%env(default:_strava_refresh_token:file:STRAVA_REFRESH_TOKEN_FILE)%']
164166

165167
App\Domain\Athlete\AthleteBirthDate:
166168
factory: [ null, 'fromString' ]

docs/getting-started/installation.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,12 @@ TZ=Etc/GMT
109109
# CADDY_LOG_LEVEL=ERROR
110110
```
111111

112+
> [!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/).
113+
>
114+
> 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.
115+
>
116+
> This approach is recommended when running the application with Docker Compose, as it avoids exposing sensitive values in environment variables.
117+
112118
## config.yaml
113119

114120
[include](../configuration/config-yaml-example.md ':include')

src/Console/Debug/DebugEnvironmentConsoleCommand.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
4545
->setRows([
4646
['APP_VERSION', AppVersion::getSemanticVersion()],
4747
['STRAVA_CLIENT_ID', $autoRedactSensitiveInfo ? $redactedString : getenv('STRAVA_CLIENT_ID')],
48-
['STRAVA_CLIENT_SECRET', $autoRedactSensitiveInfo ? $redactedString : getenv('STRAVA_CLIENT_SECRET')],
49-
['STRAVA_REFRESH_TOKEN', $autoRedactSensitiveInfo ? $redactedString : getenv('STRAVA_REFRESH_TOKEN')],
48+
['STRAVA_CLIENT_SECRET', $autoRedactSensitiveInfo ? $redactedString : $this->getenvOrFile('STRAVA_CLIENT_SECRET')],
49+
['STRAVA_REFRESH_TOKEN', $autoRedactSensitiveInfo ? $redactedString : $this->getenvOrFile('STRAVA_REFRESH_TOKEN')],
5050
['TZ', getenv('TZ')],
5151
]);
5252
$table->render();
@@ -66,4 +66,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6666

6767
return Command::SUCCESS;
6868
}
69+
70+
protected function getenvOrFile(string $var): ?string
71+
{
72+
$value = getenv($var);
73+
if (false !== $value && '' !== $value) {
74+
return $value;
75+
}
76+
77+
$file = getenv($var.'_FILE');
78+
if ($file && is_readable($file)) {
79+
return trim((string) file_get_contents($file));
80+
}
81+
82+
return null;
83+
}
6984
}

tests/Console/Debug/DebugEnvironmentConsoleCommandTest.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class DebugEnvironmentConsoleCommandTest extends ConsoleCommandTestCase
1313
use MatchesSnapshots;
1414

1515
private DebugEnvironmentConsoleCommand $debugEnvironmentConsoleCommand;
16+
private array $tempFiles = [];
1617

1718
public function testExecute(): void
1819
{
@@ -52,4 +53,76 @@ protected function getConsoleCommand(): Command
5253
{
5354
return $this->debugEnvironmentConsoleCommand;
5455
}
56+
57+
private function createTempFile(string $contents): string
58+
{
59+
$tmp = tempnam(sys_get_temp_dir(), 'testenv_');
60+
file_put_contents($tmp, $contents);
61+
$this->tempFiles[] = $tmp;
62+
63+
return $tmp;
64+
}
65+
66+
public function testGetenvOrFilePrefersEnvOverFile(): void
67+
{
68+
// create a file that would contain a different value
69+
$file = $this->createTempFile("file-secret\n");
70+
putenv('STRAVA_CLIENT_SECRET=env-secret');
71+
putenv('STRAVA_CLIENT_SECRET_FILE='.$file);
72+
73+
$command = $this->getCommandInApplication('app:debug:environment');
74+
$commandTester = new CommandTester($command);
75+
76+
$commandTester->execute([
77+
'command' => $command->getName(),
78+
]);
79+
80+
$display = $commandTester->getDisplay();
81+
82+
// env value should be used, file content must NOT be shown
83+
$this->assertStringContainsString('env-secret', $display);
84+
$this->assertStringNotContainsString('file-secret', $display);
85+
}
86+
87+
public function testGetenvOrFileUsesFileWhenEnvMissing(): void
88+
{
89+
// ensure env var is not set
90+
putenv('STRAVA_REFRESH_TOKEN=');
91+
92+
// create file and point *_FILE to it
93+
$file = $this->createTempFile("file-refresh-token\n");
94+
putenv('STRAVA_REFRESH_TOKEN_FILE='.$file);
95+
96+
$command = $this->getCommandInApplication('app:debug:environment');
97+
$commandTester = new CommandTester($command);
98+
99+
$commandTester->execute([
100+
'command' => $command->getName(),
101+
]);
102+
103+
$display = $commandTester->getDisplay();
104+
105+
// file content should appear
106+
$this->assertStringContainsString('file-refresh-token', $display);
107+
}
108+
109+
public function testMissingEnvAndMissingFileShowsNullValue(): void
110+
{
111+
// make sure both env and file are absent
112+
putenv('STRAVA_CLIENT_ID=');
113+
putenv('STRAVA_CLIENT_ID_FILE=');
114+
115+
$command = $this->getCommandInApplication('app:debug:environment');
116+
$commandTester = new CommandTester($command);
117+
118+
$commandTester->execute([
119+
'command' => $command->getName(),
120+
]);
121+
122+
$display = $commandTester->getDisplay();
123+
124+
// When value is null, table will contain an empty or 'NULL' like representation depending on Table rendering.
125+
// We at least assert the APP_VERSION header is present and command runs successfully.
126+
$this->assertStringContainsString('APP_VERSION', $display);
127+
}
55128
}

0 commit comments

Comments
 (0)