Skip to content

Commit aad15d8

Browse files
authored
Merge pull request #91 from alexandre-daubois/auth-header-process
Better authorization header handling in `AddAuthenticationHeader`
2 parents 746b30d + 57ffebe commit aad15d8

File tree

2 files changed

+58
-22
lines changed

2 files changed

+58
-22
lines changed

src/Downloading/AddAuthenticationHeader.php

+9-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
use Psr\Http\Message\RequestInterface;
1010
use RuntimeException;
1111

12+
use function array_map;
1213
use function array_walk;
14+
use function count;
1315
use function explode;
1416
use function sprintf;
15-
use function trim;
1617

1718
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
1819
final class AddAuthenticationHeader
@@ -27,9 +28,13 @@ public static function withAuthHeaderFromComposer(RequestInterface $request, Pac
2728
array_walk(
2829
$authHeaders,
2930
static function (string $v) use (&$request): void {
30-
// @todo probably process this better
31-
$headerParts = explode(':', $v);
32-
$request = $request->withHeader(trim($headerParts[0]), trim($headerParts[1]));
31+
$headerParts = array_map('trim', explode(':', $v, 2));
32+
33+
if (count($headerParts) !== 2 || ! $headerParts[0] || ! $headerParts[1]) {
34+
throw new RuntimeException('Authorization header is malformed, it should contain a non-empty key and a non-empty value.');
35+
}
36+
37+
$request = $request->withHeader($headerParts[0], $headerParts[1]);
3338
},
3439
);
3540

test/unit/Downloading/AddAuthenticationHeaderTest.php

+49-18
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
namespace Php\PieUnitTest\Downloading;
66

77
use Composer\Util\AuthHelper;
8+
use Generator;
89
use GuzzleHttp\Psr7\Request;
910
use Php\Pie\DependencyResolver\Package;
1011
use Php\Pie\Downloading\AddAuthenticationHeader;
1112
use Php\Pie\ExtensionName;
1213
use Php\Pie\ExtensionType;
1314
use PHPUnit\Framework\Attributes\CoversClass;
15+
use PHPUnit\Framework\Attributes\DataProvider;
1416
use PHPUnit\Framework\TestCase;
1517
use RuntimeException;
1618

@@ -33,24 +35,48 @@ public function testAuthorizationHeaderIsAdded(): void
3335

3436
$requestWithAuthHeader = (new AddAuthenticationHeader())->withAuthHeaderFromComposer(
3537
$request,
36-
new Package(
37-
ExtensionType::PhpModule,
38-
ExtensionName::normaliseFromString('foo'),
39-
'foo/bar',
40-
'1.2.3',
41-
$downloadUrl,
42-
[],
43-
null,
44-
'1.2.3.0',
45-
true,
46-
true,
47-
),
38+
$this->createDummyPackage($downloadUrl),
4839
$authHelper,
4940
);
5041

5142
self::assertSame('whatever ABC123', $requestWithAuthHeader->getHeaderLine('Authorization'));
5243
}
5344

45+
#[DataProvider('provideInvalidAuthorizationHeaders')]
46+
public function testEmptyValueInAuthorizationHeaderThrowsException(string $rawHeader): void
47+
{
48+
$downloadUrl = 'http://test-uri/' . uniqid('path', true);
49+
50+
$request = new Request('GET', $downloadUrl);
51+
52+
$authHelper = $this->createMock(AuthHelper::class);
53+
$authHelper->expects(self::once())
54+
->method('addAuthenticationHeader')
55+
->with([], 'github.com', $downloadUrl)
56+
->willReturn([$rawHeader]);
57+
58+
$addAuthenticationHeader = new AddAuthenticationHeader();
59+
60+
$this->expectException(RuntimeException::class);
61+
$this->expectExceptionMessage('Authorization header is malformed, it should contain a non-empty key and a non-empty value.');
62+
$addAuthenticationHeader->withAuthHeaderFromComposer($request, $this->createDummyPackage($downloadUrl), $authHelper);
63+
}
64+
65+
/**
66+
* @return Generator<string[]>
67+
*
68+
* @psalm-suppress PossiblyUnusedMethod https://github.com/psalm/psalm-plugin-phpunit/issues/131
69+
*/
70+
public static function provideInvalidAuthorizationHeaders(): Generator
71+
{
72+
yield ['Authorization:'];
73+
yield [': Bearer'];
74+
yield [' : Bearer'];
75+
yield ['Authorization: '];
76+
yield [':'];
77+
yield ['Authorization MyToken'];
78+
}
79+
5480
public function testExceptionIsThrownWhenPackageDoesNotHaveDownloadUrl(): void
5581
{
5682
$downloadUrl = 'http://test-uri/' . uniqid('path', true);
@@ -60,21 +86,26 @@ public function testExceptionIsThrownWhenPackageDoesNotHaveDownloadUrl(): void
6086
$authHelper = $this->createMock(AuthHelper::class);
6187

6288
$addAuthenticationHeader = new AddAuthenticationHeader();
63-
$package = new Package(
89+
$package = $this->createDummyPackage();
90+
91+
$this->expectException(RuntimeException::class);
92+
$this->expectExceptionMessage('The package foo/bar does not have a download URL');
93+
$addAuthenticationHeader->withAuthHeaderFromComposer($request, $package, $authHelper);
94+
}
95+
96+
private function createDummyPackage(string|null $downloadUrl = null): Package
97+
{
98+
return new Package(
6499
ExtensionType::PhpModule,
65100
ExtensionName::normaliseFromString('foo'),
66101
'foo/bar',
67102
'1.2.3',
68-
null,
103+
$downloadUrl,
69104
[],
70105
null,
71106
'1.2.3.0',
72107
true,
73108
true,
74109
);
75-
76-
$this->expectException(RuntimeException::class);
77-
$this->expectExceptionMessage('The package foo/bar does not have a download URL');
78-
$addAuthenticationHeader->withAuthHeaderFromComposer($request, $package, $authHelper);
79110
}
80111
}

0 commit comments

Comments
 (0)