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

Commit 3d657a8

Browse files
committed
Imported HTTP basic authentication adapter classes from zend-expressive-authentication
1 parent fed2390 commit 3d657a8

File tree

6 files changed

+318
-1
lines changed

6 files changed

+318
-1
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
[![Build Status](https://secure.travis-ci.org/zendframework/zend-expressive-authentication-basic.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-expressive-authentication-basic)
44
[![Coverage Status](https://coveralls.io/repos/github/zendframework/zend-expressive-authentication-basic/badge.svg?branch=master)](https://coveralls.io/github/zendframework/zend-expressive-authentication-basic?branch=master)
55

6-
This library provides ...
6+
This library provides an HTTP Basic authentication adapter for
7+
[zend-expressive-authentication](https://docs.zendframework.com/zend-expressive-authentication),
8+
and thus PSR-7 middleware applications.
79

810
## Installation
911

src/BasicAccess.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-expressive-authentication-basic for the canonical source repository
4+
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com)
5+
* @license https://github.com/zendframework/zend-expressive-authentication-basic/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
namespace Zend\Expressive\Authentication\Basic;
9+
10+
use Psr\Http\Message\ResponseInterface;
11+
use Psr\Http\Message\ServerRequestInterface;
12+
use Zend\Expressive\Authentication\AuthenticationInterface;
13+
use Zend\Expressive\Authentication\UserInterface;
14+
use Zend\Expressive\Authentication\UserRepositoryInterface;
15+
16+
class BasicAccess implements AuthenticationInterface
17+
{
18+
/**
19+
* @var UserRepositoryInterface
20+
*/
21+
protected $repository;
22+
23+
/**
24+
* @var string
25+
*/
26+
protected $realm;
27+
28+
/**
29+
* @var ResponseInterface
30+
*/
31+
protected $responsePrototype;
32+
33+
/**
34+
* Constructor
35+
*
36+
* @param UserRepositoryInterface $repository
37+
* @param string $realm
38+
* @param ResponseInterface $responsePrototype
39+
*/
40+
public function __construct(
41+
UserRepositoryInterface $repository,
42+
string $realm,
43+
ResponseInterface $responsePrototype
44+
) {
45+
$this->repository = $repository;
46+
$this->realm = $realm;
47+
$this->responsePrototype = $responsePrototype;
48+
}
49+
50+
/**
51+
* {@inheritDoc}
52+
*/
53+
public function authenticate(ServerRequestInterface $request): ?UserInterface
54+
{
55+
$authHeader = $request->getHeader('Authorization');
56+
if (empty($authHeader)) {
57+
return null;
58+
}
59+
if (! preg_match('/Basic ([a-zA-Z0-9\+\/\=]+)/', $authHeader[0], $match)) {
60+
return null;
61+
}
62+
[$username, $password] = explode(':', base64_decode($match[1]));
63+
64+
return $this->repository->authenticate($username, $password);
65+
}
66+
67+
/**
68+
* {@inheritDoc}
69+
*/
70+
public function unauthorizedResponse(ServerRequestInterface $request): ResponseInterface
71+
{
72+
return $this->responsePrototype->withHeader(
73+
'WWW-Authenticate',
74+
sprintf("Basic realm=\"%s\"", $this->realm)
75+
)->withStatus(401);
76+
}
77+
}

src/BasicAccessFactory.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-expressive-authentication-basic for the canonical source repository
4+
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com)
5+
* @license https://github.com/zendframework/zend-expressive-authentication-basic/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
namespace Zend\Expressive\Authentication\Basic;
9+
10+
use Psr\Container\ContainerInterface;
11+
use Zend\Expressive\Authentication\Exception;
12+
use Zend\Expressive\Authentication\ResponsePrototypeTrait;
13+
use Zend\Expressive\Authentication\UserRepositoryInterface;
14+
15+
class BasicAccessFactory
16+
{
17+
use ResponsePrototypeTrait;
18+
19+
public function __invoke(ContainerInterface $container): BasicAccess
20+
{
21+
$userRegister = $container->has(UserRepositoryInterface::class) ?
22+
$container->get(UserRepositoryInterface::class) :
23+
null;
24+
if (null === $userRegister) {
25+
throw new Exception\InvalidConfigException(
26+
'UserRepositoryInterface service is missing for authentication'
27+
);
28+
}
29+
$realm = $container->get('config')['authentication']['realm'] ?? null;
30+
if (null === $realm) {
31+
throw new Exception\InvalidConfigException(
32+
'Realm value is not present in authentication config'
33+
);
34+
}
35+
36+
return new BasicAccess(
37+
$userRegister,
38+
$realm,
39+
$this->getResponsePrototype($container)
40+
);
41+
}
42+
}

src/ConfigProvider.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ public function __invoke() : array
1919
public function getDependencies() : array
2020
{
2121
return [
22+
'factories' => [
23+
BasicAccess::class => BasicAccessFactory::class,
24+
],
2225
];
2326
}
2427
}

test/BasicAccessFactoryTest.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-expressive-authentication-basic for the canonical source repository
4+
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com)
5+
* @license https://github.com/zendframework/zend-expressive-authentication-basic/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
namespace ZendTest\Expressive\Authentication\Basic;
9+
10+
use PHPUnit\Framework\TestCase;
11+
use Prophecy\Argument;
12+
use Psr\Container\ContainerInterface;
13+
use Psr\Http\Message\ResponseInterface;
14+
use Zend\Expressive\Authentication\Basic\BasicAccess;
15+
use Zend\Expressive\Authentication\Basic\BasicAccessFactory;
16+
use Zend\Expressive\Authentication\UserRepositoryInterface;
17+
18+
class BasicAccessFactoryTest extends TestCase
19+
{
20+
protected function setUp()
21+
{
22+
$this->container = $this->prophesize(ContainerInterface::class);
23+
$this->factory = new BasicAccessFactory();
24+
$this->userRegister = $this->prophesize(UserRepositoryInterface::class);
25+
$this->responsePrototype = $this->prophesize(ResponseInterface::class);
26+
}
27+
28+
/**
29+
* @expectedException Zend\Expressive\Authentication\Exception\InvalidConfigException
30+
*/
31+
public function testInvokeWithEmptyContainer()
32+
{
33+
$basicAccess = ($this->factory)($this->container->reveal());
34+
}
35+
36+
/**
37+
* @expectedException Zend\Expressive\Authentication\Exception\InvalidConfigException
38+
*/
39+
public function testInvokeWithContainerEmptyConfig()
40+
{
41+
$this->container->has(UserRepositoryInterface::class)
42+
->willReturn(true);
43+
$this->container->get(UserRepositoryInterface::class)
44+
->willReturn($this->userRegister->reveal());
45+
$this->container->has(ResponseInterface::class)
46+
->willReturn(true);
47+
$this->container->get(ResponseInterface::class)
48+
->willReturn($this->responsePrototype->reveal());
49+
$this->container->get('config')
50+
->willReturn([]);
51+
52+
$basicAccess = ($this->factory)($this->container->reveal());
53+
}
54+
55+
public function testInvokeWithContainerAndConfig()
56+
{
57+
$this->container->has(UserRepositoryInterface::class)
58+
->willReturn(true);
59+
$this->container->get(UserRepositoryInterface::class)
60+
->willReturn($this->userRegister->reveal());
61+
$this->container->has(ResponseInterface::class)
62+
->willReturn(true);
63+
$this->container->get(ResponseInterface::class)
64+
->willReturn($this->responsePrototype->reveal());
65+
$this->container->get('config')
66+
->willReturn([
67+
'authentication' => ['realm' => 'My page']
68+
]);
69+
70+
$basicAccess = ($this->factory)($this->container->reveal());
71+
$this->assertInstanceOf(BasicAccess::class, $basicAccess);
72+
}
73+
}

test/BasicAccessTest.php

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-expressive-authentication-basic for the canonical source repository
4+
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (http://www.zend.com)
5+
* @license https://github.com/zendframework/zend-expressive-authentication-basic/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
namespace ZendTest\Expressive\Authentication\Basic;
9+
10+
use PHPUnit\Framework\TestCase;
11+
use Prophecy\Argument;
12+
use Psr\Http\Message\ServerRequestInterface;
13+
use Psr\Http\Message\ResponseInterface;
14+
use Zend\Expressive\Authentication\AuthenticationInterface;
15+
use Zend\Expressive\Authentication\Basic\BasicAccess;
16+
use Zend\Expressive\Authentication\UserInterface;
17+
use Zend\Expressive\Authentication\UserRepositoryInterface;
18+
19+
class BasicAccessTest extends TestCase
20+
{
21+
protected function setUp()
22+
{
23+
$this->request = $this->prophesize(ServerRequestInterface::class);
24+
$this->userRepository = $this->prophesize(UserRepositoryInterface::class);
25+
$this->authenticatedUser = $this->prophesize(UserInterface::class);
26+
$this->responsePrototype = $this->prophesize(ResponseInterface::class);
27+
}
28+
29+
public function testConstructor()
30+
{
31+
$basicAccess = new BasicAccess(
32+
$this->userRepository->reveal(),
33+
'test',
34+
$this->responsePrototype->reveal()
35+
);
36+
$this->assertInstanceOf(AuthenticationInterface::class, $basicAccess);
37+
}
38+
39+
public function testIsAuthenticatedWithoutHeader()
40+
{
41+
$this->request->getHeader('Authorization')
42+
->willReturn([]);
43+
44+
$basicAccess = new BasicAccess(
45+
$this->userRepository->reveal(),
46+
'test',
47+
$this->responsePrototype->reveal()
48+
);
49+
$this->assertNull($basicAccess->authenticate($this->request->reveal()));
50+
}
51+
52+
public function testIsAuthenticatedWithoutBasic()
53+
{
54+
$this->request->getHeader('Authorization')
55+
->willReturn(['foo']);
56+
57+
$basicAccess = new BasicAccess(
58+
$this->userRepository->reveal(),
59+
'test',
60+
$this->responsePrototype->reveal()
61+
);
62+
$this->assertNull($basicAccess->authenticate($this->request->reveal()));
63+
}
64+
65+
public function testIsAuthenticatedWithValidCredential()
66+
{
67+
$this->request->getHeader('Authorization')
68+
->willReturn(['Basic QWxhZGRpbjpPcGVuU2VzYW1l']);
69+
$this->request->withAttribute(UserInterface::class, Argument::type(UserInterface::class))
70+
->willReturn($this->request->reveal());
71+
72+
$this->authenticatedUser->getUsername()->willReturn('Aladdin');
73+
$this->userRepository->authenticate('Aladdin', 'OpenSesame')
74+
->willReturn($this->authenticatedUser->reveal());
75+
76+
$basicAccess = new BasicAccess(
77+
$this->userRepository->reveal(),
78+
'test',
79+
$this->responsePrototype->reveal()
80+
);
81+
$user = $basicAccess->authenticate($this->request->reveal());
82+
$this->assertInstanceOf(UserInterface::class, $user);
83+
$this->assertEquals('Aladdin', $user->getUsername());
84+
}
85+
86+
public function testIsAuthenticatedWithNoCredential()
87+
{
88+
$this->request->getHeader('Authorization')
89+
->willReturn(['Basic QWxhZGRpbjpPcGVuU2VzYW1l']);
90+
91+
$this->userRepository->authenticate('Aladdin', 'OpenSesame')
92+
->willReturn(null);
93+
94+
$basicAccess = new BasicAccess(
95+
$this->userRepository->reveal(),
96+
'test',
97+
$this->responsePrototype->reveal()
98+
);
99+
$this->assertNull($basicAccess->authenticate($this->request->reveal()));
100+
}
101+
102+
public function testGetUnauthenticatedResponse()
103+
{
104+
$this->responsePrototype->getHeader('WWW-Authenticate')
105+
->willReturn(['Basic realm="test"']);
106+
$this->responsePrototype->withHeader('WWW-Authenticate', 'Basic realm="test"')
107+
->willReturn($this->responsePrototype->reveal());
108+
$this->responsePrototype->withStatus(401)
109+
->willReturn($this->responsePrototype->reveal());
110+
$basicAccess = new BasicAccess(
111+
$this->userRepository->reveal(),
112+
'test',
113+
$this->responsePrototype->reveal()
114+
);
115+
$response = $basicAccess->unauthorizedResponse($this->request->reveal());
116+
117+
$this->assertInstanceOf(ResponseInterface::class, $response);
118+
$this->assertEquals(['Basic realm="test"'], $response->getHeader('WWW-Authenticate'));
119+
}
120+
}

0 commit comments

Comments
 (0)