Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit 149b70d

Browse files
committed
Merge branch 'hotfix/9'
Close #9 Fixes #8
2 parents e1ec4ca + 6eff504 commit 149b70d

File tree

3 files changed

+113
-26
lines changed

3 files changed

+113
-26
lines changed

CHANGELOG.md

+26
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
All notable changes to this project will be documented in this file, in reverse chronological order by release.
44

5+
## 0.3.1 - 2018-07-25
6+
7+
### Added
8+
9+
- Nothing.
10+
11+
### Changed
12+
13+
- Nothing.
14+
15+
### Deprecated
16+
17+
- Nothing.
18+
19+
### Removed
20+
21+
- Nothing
22+
23+
### Fixed
24+
25+
- [#9](https://github.com/zendframework/zend-expressive-authentication-basic/pull/9) fixes an issue in PHP 7.2 that occurred when the decoded
26+
authentication string did not contain a colon (`:`). It now correctly
27+
interprets this as a lack of credentials.
28+
29+
- [#9](https://github.com/zendframework/zend-expressive-authentication-basic/pull/9) provides a fix that allows passwords that contain colons.
30+
531
## 0.3.0 - 2018-03-15
632

733
### Added

src/BasicAccess.php

+23-4
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,35 @@ public function __construct(
5252

5353
public function authenticate(ServerRequestInterface $request) : ?UserInterface
5454
{
55-
$authHeader = $request->getHeader('Authorization');
56-
if (empty($authHeader)) {
55+
$authHeaders = $request->getHeader('Authorization');
56+
57+
if (1 !== count($authHeaders)) {
58+
return null;
59+
}
60+
61+
$authHeader = array_shift($authHeaders);
62+
63+
if (! preg_match('/Basic (?P<credentials>.+)/', $authHeader, $match)) {
64+
return null;
65+
}
66+
67+
$decodedCredentials = base64_decode($match['credentials'], true);
68+
69+
if (false === $decodedCredentials) {
70+
return null;
71+
}
72+
73+
$credentialParts = explode(':', $decodedCredentials, 2);
74+
75+
if (false === $credentialParts) {
5776
return null;
5877
}
5978

60-
if (! preg_match('/Basic (?P<credentials>[a-zA-Z0-9\+\/\=]+)/', $authHeader[0], $match)) {
79+
if (2 !== count($credentialParts)) {
6180
return null;
6281
}
6382

64-
[$username, $password] = explode(':', base64_decode($match['credentials']));
83+
[$username, $password] = $credentialParts;
6584

6685
return $this->repository->authenticate($username, $password);
6786
}

test/BasicAccessTest.php

+64-22
Original file line numberDiff line numberDiff line change
@@ -56,49 +56,47 @@ public function testConstructor()
5656
$this->assertInstanceOf(AuthenticationInterface::class, $basicAccess);
5757
}
5858

59-
public function testIsAuthenticatedWithoutHeader()
60-
{
61-
$this->request
62-
->getHeader('Authorization')
63-
->willReturn([]);
64-
65-
$basicAccess = new BasicAccess(
66-
$this->userRepository->reveal(),
67-
'test',
68-
$this->responseFactory
69-
);
70-
$this->assertNull($basicAccess->authenticate($this->request->reveal()));
71-
}
7259

73-
public function testIsAuthenticatedWithoutBasic()
60+
/**
61+
* @param array $authHeader
62+
* @dataProvider provideInvalidAuthenticationHeader
63+
*/
64+
public function testIsAuthenticatedWithInvalidData(array $authHeader)
7465
{
7566
$this->request
7667
->getHeader('Authorization')
77-
->willReturn(['foo']);
68+
->willReturn($authHeader);
69+
70+
$this->userRepository->authenticate(Argument::any(), Argument::any())->shouldNotBeCalled();
7871

7972
$basicAccess = new BasicAccess(
8073
$this->userRepository->reveal(),
8174
'test',
8275
$this->responseFactory
8376
);
84-
8577
$this->assertNull($basicAccess->authenticate($this->request->reveal()));
8678
}
8779

88-
public function testIsAuthenticatedWithValidCredential()
80+
/**
81+
* @param string $username
82+
* @param string $password
83+
* @param array $authHeader
84+
* @dataProvider provideValidAuthentication
85+
*/
86+
public function testIsAuthenticatedWithValidCredential(string $username, string $password, array $authHeader)
8987
{
9088
$this->request
9189
->getHeader('Authorization')
92-
->willReturn(['Basic QWxhZGRpbjpPcGVuU2VzYW1l']);
90+
->willReturn($authHeader);
9391
$this->request
9492
->withAttribute(UserInterface::class, Argument::type(UserInterface::class))
9593
->willReturn($this->request->reveal());
9694

9795
$this->authenticatedUser
9896
->getIdentity()
99-
->willReturn('Aladdin');
97+
->willReturn($username);
10098
$this->userRepository
101-
->authenticate('Aladdin', 'OpenSesame')
99+
->authenticate($username, $password)
102100
->willReturn($this->authenticatedUser->reveal());
103101

104102
$basicAccess = new BasicAccess(
@@ -109,7 +107,6 @@ public function testIsAuthenticatedWithValidCredential()
109107

110108
$user = $basicAccess->authenticate($this->request->reveal());
111109
$this->assertInstanceOf(UserInterface::class, $user);
112-
$this->assertEquals('Aladdin', $user->getIdentity());
113110
}
114111

115112
public function testIsAuthenticatedWithNoCredential()
@@ -151,7 +148,52 @@ public function testGetUnauthenticatedResponse()
151148

152149
$response = $basicAccess->unauthorizedResponse($this->request->reveal());
153150

154-
$this->assertInstanceOf(ResponseInterface::class, $response);
155151
$this->assertEquals(['Basic realm="test"'], $response->getHeader('WWW-Authenticate'));
156152
}
153+
154+
public function provideInvalidAuthenticationHeader(): array
155+
{
156+
return [
157+
'empty-header' => [[]],
158+
'missing-basic-prefix' => [['foo']],
159+
'only-username-without-colon' => [['Basic ' . base64_encode('Aladdin')]],
160+
'base64-encoded-pile-of-poo-emoji' => [['Basic ' . base64_encode('💩')]],
161+
'pile-of-poo-emoji' => [['Basic 💩']],
162+
'only-pile-of-poo-emoji' => [['💩']],
163+
'basic-prefix-without-content' => [['Basic ']],
164+
'only-basic' => [['Basic']],
165+
'multiple-auth-headers' => [
166+
[
167+
['Basic ' . base64_encode('Aladdin:OpenSesame')],
168+
['Basic ' . base64_encode('Aladdin:OpenSesame')],
169+
],
170+
],
171+
];
172+
}
173+
174+
public function provideValidAuthentication(): array
175+
{
176+
return [
177+
'aladdin' => ['Aladdin', 'OpenSesame', ['Basic ' . base64_encode('Aladdin:OpenSesame')]],
178+
'aladdin-with-nonzero-array-index' => [
179+
'Aladdin',
180+
'OpenSesame',
181+
[-200 => 'Basic ' . base64_encode('Aladdin:OpenSesame')]
182+
],
183+
'passwords-with-colon' => ['Aladdin', 'Open:Sesame', ['Basic ' . base64_encode('Aladdin:Open:Sesame')]],
184+
'username-without-password' => ['Aladdin', '', ['Basic ' . base64_encode('Aladdin:')]],
185+
'password-without-username' => ['', 'OpenSesame', ['Basic ' . base64_encode(':OpenSesame')]],
186+
'passwords-with-multiple-colons' => [
187+
'Aladdin',
188+
'::Open:::Sesame::',
189+
['Basic ' . base64_encode('Aladdin:::Open:::Sesame::')]
190+
],
191+
'no-username-or-password' => ['', '', ['Basic ' . base64_encode(':')]],
192+
'no-username-password-only-colons' => ['', '::::::', ['Basic ' . base64_encode(':::::::')]],
193+
'unicode-username-and-password' => [
194+
'thumbsup-emoji-👍',
195+
'thumbsdown-emoji-👎',
196+
['Basic ' . base64_encode('thumbsup-emoji-👍:thumbsdown-emoji-👎')]],
197+
];
198+
}
157199
}

0 commit comments

Comments
 (0)