Skip to content

Commit 32cabbe

Browse files
committed
fix(imaging): keep Orthanc PACS credentials in sync
1 parent 8e0fd6f commit 32cabbe

3 files changed

Lines changed: 141 additions & 3 deletions

File tree

backend/app/Services/Imaging/PacsConnectionService.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public function resolveClient(PacsConnection $conn): PendingRequest
179179
$client = Http::timeout(15)
180180
->accept('application/dicom+json');
181181

182-
$credentials = $conn->credentials ?? [];
182+
$credentials = $this->credentialsForConnection($conn);
183183

184184
return match ($conn->auth_type) {
185185
'basic' => $client->withBasicAuth(
@@ -302,7 +302,7 @@ private function orthancRestBase(PacsConnection $conn): string
302302
private function resolveOrthancRestClient(PacsConnection $conn): PendingRequest
303303
{
304304
$client = Http::timeout(30);
305-
$credentials = $conn->credentials ?? [];
305+
$credentials = $this->credentialsForConnection($conn);
306306

307307
return match ($conn->auth_type) {
308308
'basic' => $client->withBasicAuth(
@@ -314,6 +314,24 @@ private function resolveOrthancRestClient(PacsConnection $conn): PendingRequest
314314
};
315315
}
316316

317+
/**
318+
* Resolve connection credentials, using the runtime Orthanc secret for the
319+
* built-in Orthanc service so encrypted DB rows do not drift after rotation.
320+
*
321+
* @return array<string, mixed>
322+
*/
323+
private function credentialsForConnection(PacsConnection $conn): array
324+
{
325+
$credentials = $conn->credentials ?? [];
326+
327+
if ($conn->type === 'orthanc') {
328+
$credentials['username'] = env('ORTHANC_USER', $credentials['username'] ?? '');
329+
$credentials['password'] = env('ORTHANC_PASSWORD', $credentials['password'] ?? '');
330+
}
331+
332+
return $credentials;
333+
}
334+
317335
/**
318336
* Refresh stats for a generic DICOMweb server via QIDO-RS count.
319337
*

backend/database/seeders/PacsConnectionSeeder.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,19 @@ class PacsConnectionSeeder extends Seeder
99
{
1010
public function run(): void
1111
{
12+
$username = env('ORTHANC_USER', 'parthenon');
13+
$password = env('ORTHANC_PASSWORD', '');
14+
1215
PacsConnection::updateOrCreate(
1316
['name' => 'Local Orthanc'],
1417
[
1518
'type' => 'orthanc',
1619
'base_url' => 'http://orthanc:8042/dicom-web',
17-
'auth_type' => 'none',
20+
'auth_type' => 'basic',
21+
'credentials' => [
22+
'username' => $username,
23+
'password' => $password,
24+
],
1825
'is_default' => true,
1926
'is_active' => true,
2027
],
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Unit\Services;
6+
7+
use App\Models\App\PacsConnection;
8+
use App\Services\Imaging\PacsConnectionService;
9+
use Illuminate\Foundation\Testing\RefreshDatabase;
10+
use Illuminate\Http\Client\Request;
11+
use Illuminate\Support\Facades\Http;
12+
use Tests\TestCase;
13+
14+
class PacsConnectionServiceTest extends TestCase
15+
{
16+
use RefreshDatabase;
17+
18+
protected function tearDown(): void
19+
{
20+
$this->unsetEnv('ORTHANC_USER');
21+
$this->unsetEnv('ORTHANC_PASSWORD');
22+
23+
parent::tearDown();
24+
}
25+
26+
public function test_orthanc_connection_test_uses_runtime_env_credentials(): void
27+
{
28+
$this->setEnv('ORTHANC_USER', 'env-user');
29+
$this->setEnv('ORTHANC_PASSWORD', 'env-pass');
30+
31+
$connection = $this->createOrthancConnectionWithStaleCredentials();
32+
$expectedAuth = 'Basic '.base64_encode('env-user:env-pass');
33+
34+
Http::fake(function (Request $request) use ($expectedAuth) {
35+
$this->assertSame($expectedAuth, $request->header('Authorization')[0] ?? null);
36+
$this->assertSame('http://orthanc:8042/dicom-web/studies?limit=1', $request->url());
37+
38+
return Http::response([], 200);
39+
});
40+
41+
$result = app(PacsConnectionService::class)->testConnection($connection);
42+
43+
$this->assertTrue($result['success']);
44+
$this->assertSame('ok', $connection->refresh()->last_health_status);
45+
}
46+
47+
public function test_orthanc_stats_refresh_uses_runtime_env_credentials(): void
48+
{
49+
$this->setEnv('ORTHANC_USER', 'env-user');
50+
$this->setEnv('ORTHANC_PASSWORD', 'env-pass');
51+
52+
$connection = $this->createOrthancConnectionWithStaleCredentials();
53+
$expectedAuth = 'Basic '.base64_encode('env-user:env-pass');
54+
55+
Http::fake(function (Request $request) use ($expectedAuth) {
56+
$this->assertSame($expectedAuth, $request->header('Authorization')[0] ?? null);
57+
58+
if (str_contains($request->url(), '/statistics')) {
59+
return Http::response([
60+
'CountPatients' => 2,
61+
'CountStudies' => 3,
62+
'CountSeries' => 4,
63+
'CountInstances' => 5,
64+
'TotalDiskSizeMB' => 6,
65+
], 200);
66+
}
67+
68+
if (str_contains($request->url(), '/series')) {
69+
return Http::response([
70+
['MainDicomTags' => ['Modality' => 'CT']],
71+
['MainDicomTags' => ['Modality' => 'MR']],
72+
], 200);
73+
}
74+
75+
return Http::response([], 404);
76+
});
77+
78+
$result = app(PacsConnectionService::class)->refreshStats($connection);
79+
80+
$this->assertTrue($result['success']);
81+
$this->assertSame(3, $connection->refresh()->metadata_cache['count_studies']);
82+
$this->assertSame(['CT' => 1, 'MR' => 1], $connection->metadata_cache['modalities']);
83+
}
84+
85+
private function createOrthancConnectionWithStaleCredentials(): PacsConnection
86+
{
87+
return PacsConnection::create([
88+
'name' => 'Local Orthanc',
89+
'type' => 'orthanc',
90+
'base_url' => 'http://orthanc:8042/dicom-web',
91+
'auth_type' => 'basic',
92+
'credentials' => [
93+
'username' => 'env-user',
94+
'password' => 'stale-db-pass',
95+
],
96+
'is_default' => true,
97+
'is_active' => true,
98+
]);
99+
}
100+
101+
private function setEnv(string $key, string $value): void
102+
{
103+
putenv("{$key}={$value}");
104+
$_ENV[$key] = $value;
105+
$_SERVER[$key] = $value;
106+
}
107+
108+
private function unsetEnv(string $key): void
109+
{
110+
putenv($key);
111+
unset($_ENV[$key], $_SERVER[$key]);
112+
}
113+
}

0 commit comments

Comments
 (0)