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

Commit 6d16330

Browse files
committed
Merge branch 'feature/16-rename-username-to-identity'
Close #16 Fixes #14
2 parents cfaaf5c + b635bb5 commit 6d16330

15 files changed

+234
-83
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22

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

5-
## 0.2.1 - TBD
5+
## 0.3.0 - 2018-01-24
66

77
### Added
88

99
- Nothing.
1010

1111
### Changed
1212

13-
- Nothing
13+
- [#14](https://github.com/zendframework/zend-expressive-authentication/issues/14)
14+
renames the method `UserInterface::getUsername()` to
15+
`UserInterface::getIdentity()`.
1416

1517
### Deprecated
1618

docs/book/v1/intro.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ namespace Zend\Expressive\Authentication;
2222
interface UserInterface
2323
{
2424
/**
25-
* Get the username
25+
* Get the unique user identity (id, username, email address or ...)
2626
*
2727
* @return string
2828
*/
29-
public function getUsername(): string;
29+
public function getIdentity(): string;
3030

3131
/**
3232
* Get all user roles
@@ -40,7 +40,7 @@ interface UserInterface
4040
The `UserInterface` attribute in the PSR-7 request can be used for checking
4141
if a user has been authenticated or not, e.g. it can be used to verify the
4242
authorization level of a user (for this scope, it is consumed by
43-
[zend-expressive-authotization](https://github.com/zendframework/zend-expressive-authorization)).
43+
[zend-expressive-authorization](https://github.com/zendframework/zend-expressive-authorization)).
4444

4545
## Usage in the route
4646

docs/book/v1/user-repository.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,82 @@ result will be a `UserInterface` instance, otherwise a null value is returned.
4242
The second function is `getRolesFromUser()` and it specifies how to retrieve
4343
the roles for a user. If a user does not have roles, this function will return
4444
an empty array.
45+
46+
47+
## Configure the user repository
48+
49+
In order to use a user repository adapter, we need to configure it. For instance,
50+
to consume an `htpasswd` file, we need to configure the path to the file.
51+
Such configuration is provided in the `authentication` hierarchy provided to
52+
your [PSR-11](http://www.php-fig.org/psr/psr-11/) container. We demonstrate
53+
examples of such configuration below.
54+
55+
Using [Expressive](https://docs.zendframework.com/zend-expressive/), this
56+
configuration can be stored in a file under the `/config/autoload/` folder. We
57+
suggest to use a `.local.php` suffix — e.g.
58+
`/config/autoload/auth.local.php` — as local configuration is not stored
59+
in the version control system.
60+
61+
You can also provide this configuration using a [ConfigProvider.php](https://github.com/zendframework/zend-expressive-authentication/blob/master/src/ConfigProvider.php)
62+
class. [Read this blog post](https://framework.zend.com/blog/2017-04-20-config-aggregator.html)
63+
for more information on config providers.
64+
65+
## htpasswd configuration
66+
67+
When using the htpasswd user repository implementation, you need only configure
68+
the path to the `htpasswd` file:
69+
70+
```php
71+
return [
72+
'authentication' => [
73+
'htpasswd' => 'insert the path to htpasswd file',
74+
],
75+
];
76+
```
77+
78+
## PDO configuration
79+
80+
When using the PDO user repository adapter, you will need to provide PDO
81+
connection parameters, as well as information on the table, field names, and a
82+
SQL statement for retrieiving user roles:
83+
84+
```php
85+
return [
86+
'authentication' => [
87+
'pdo' => [
88+
'dsn' => '',
89+
'username' => '',
90+
'password' => '',
91+
'table' => 'user table name',
92+
'field' => [
93+
'identity' => 'identity field name',
94+
'password' => 'password field name',
95+
],
96+
'sql_get_roles' => 'SQL to retrieve roles with :identity parameter',
97+
],
98+
],
99+
];
100+
```
101+
102+
The required parameters are `dsn`, `table`, and `field`.
103+
104+
The `dsn` value is the DSN connection string to be used to connect to the database.
105+
For instance, using a SQLite database, a typical value is `sqlite:/path/to/file`.
106+
107+
The `username` and `password` parameters are optional parameters used to connect
108+
to the database. Depending on the database, these parameters may not be required;
109+
e.g. [SQLite](https://sqlite.org/) does not require them.
110+
111+
The `table` value is the name of the table containing the user credentials.
112+
113+
The `field` parameter contains the field name of the `identity` of the user and
114+
the user `password.` The `identity` of the user can be a username, an email, etc.
115+
116+
The `sql_get_roles` setting is an optional parameter that contains the SQL query
117+
for retrieving the user roles. The identity value must be specified using the
118+
placeholder `:identity`. For instance, if a role is stored in a user table, a
119+
typical query might look like the following:
120+
121+
```sql
122+
SELECT role FROM user WHERE username = :identity
123+
```

src/ConfigProvider.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,19 @@ public function getAuthenticationConfig() : array
2727
*
2828
* Example: using htpasswd UserRepositoryInterface implementation:
2929
*
30-
* 'user_register' => [
31-
* 'htpasswd' => 'insert the path to htpasswd file'
30+
* [
31+
* 'htpasswd' => 'insert the path to htpasswd file',
32+
* 'pdo' => [
33+
* 'dsn' => 'DSN for connection',
34+
* 'username' => 'username for database connection, if needed',
35+
* 'password' => 'password for database connection, if needed',
36+
* 'table' => 'user table name',
37+
* 'field' => [
38+
* 'identity' => 'identity field name',
39+
* 'password' => 'password field name',
40+
* ],
41+
* 'sql_get_roles' => 'SQL to retrieve roles by :identity',
42+
* ],
3243
* ]
3344
*/
3445
];

src/UserInterface.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
interface UserInterface
1010
{
1111
/**
12-
* Get the username
12+
* Get the unique user identity (id, username, email address or ...)
1313
*/
14-
public function getUsername() : string;
14+
public function getIdentity() : string;
1515

1616
/**
1717
* Get all user roles

src/UserRepository/Htpasswd.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public function authenticate(string $credential, string $password = null) : ?Use
6666
/**
6767
* {@inheritDoc}
6868
*/
69-
public function getRolesFromUser(string $username) : array
69+
public function getRolesFromUser(string $identity) : array
7070
{
7171
return [];
7272
}

src/UserRepository/HtpasswdFactory.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ class HtpasswdFactory
1717
*/
1818
public function __invoke(ContainerInterface $container) : Htpasswd
1919
{
20-
$config = $container->has('config') ? $container->get('config') : [];
21-
$htpasswd = $config['authentication']['htpasswd'] ?? null;
22-
20+
$htpasswd = $container->get('config')['authentication']['htpasswd'] ?? null;
2321
if (null === $htpasswd) {
2422
throw new Exception\InvalidConfigException(sprintf(
2523
'Config key authentication.htpasswd is not present; cannot create %s user repository adapter',

src/UserRepository/PdoDatabase.php

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
namespace Zend\Expressive\Authentication\UserRepository;
88

99
use PDO;
10+
use PDOException;
1011
use Zend\Expressive\Authentication\Exception;
1112
use Zend\Expressive\Authentication\UserInterface;
1213
use Zend\Expressive\Authentication\UserRepositoryInterface;
@@ -41,14 +42,20 @@ public function __construct(PDO $pdo, array $config)
4142
public function authenticate(string $credential, string $password = null) : ?UserInterface
4243
{
4344
$sql = sprintf(
44-
"SELECT %s FROM %s WHERE %s = :username",
45+
"SELECT %s FROM %s WHERE %s = :identity",
4546
$this->config['field']['password'],
4647
$this->config['table'],
47-
$this->config['field']['username']
48+
$this->config['field']['identity']
4849
);
4950

5051
$stmt = $this->pdo->prepare($sql);
51-
$stmt->bindParam(':username', $credential);
52+
if (false === $stmt) {
53+
throw new Exception\RuntimeException(
54+
'An error occurred when preparing to fetch user details from ' .
55+
'the repository; please verify your configuration'
56+
);
57+
}
58+
$stmt->bindParam(':identity', $credential);
5259
$stmt->execute();
5360

5461
$result = $stmt->fetchObject();
@@ -64,20 +71,32 @@ public function authenticate(string $credential, string $password = null) : ?Use
6471
/**
6572
* {@inheritDoc}
6673
*/
67-
public function getRolesFromUser(string $username) : array
74+
public function getRolesFromUser(string $identity) : array
6875
{
6976
if (! isset($this->config['sql_get_roles'])) {
7077
return [];
7178
}
7279

73-
if (false === strpos($this->config['sql_get_roles'], ':username')) {
80+
if (false === strpos($this->config['sql_get_roles'], ':identity')) {
7481
throw new Exception\InvalidConfigException(
75-
'The sql_get_roles configuration setting must include a :username parameter'
82+
'The sql_get_roles configuration setting must include a :identity parameter'
7683
);
7784
}
7885

79-
$stmt = $this->pdo->prepare($this->config['sql_get_roles']);
80-
$stmt->bindParam(':username', $username);
86+
try {
87+
$stmt = $this->pdo->prepare($this->config['sql_get_roles']);
88+
} catch (PDOException $e) {
89+
throw new Exception\RuntimeException(sprintf(
90+
'Error preparing retrieval of user roles: %s',
91+
$e->getMessage()
92+
));
93+
}
94+
if (false === $stmt) {
95+
throw new Exception\RuntimeException(sprintf(
96+
'Error preparing retrieval of user roles: unknown error'
97+
));
98+
}
99+
$stmt->bindParam(':identity', $identity);
81100

82101
if (! $stmt->execute()) {
83102
return [];

src/UserRepository/PdoDatabaseFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public function __invoke(ContainerInterface $container) : PdoDatabase
3434
'The PDO table name is missing in the configuration'
3535
);
3636
}
37-
if (! isset($pdo['field']['username'])) {
37+
if (! isset($pdo['field']['identity'])) {
3838
throw new Exception\InvalidConfigException(
39-
'The PDO username field is missing in the configuration'
39+
'The PDO identity field is missing in the configuration'
4040
);
4141
}
4242
if (! isset($pdo['field']['password'])) {

src/UserRepository/UserTrait.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@
1212
trait UserTrait
1313
{
1414
/**
15-
* Generate a user from username and list of roles
15+
* Generate a user from identity and list of roles
1616
*/
17-
protected function generateUser(string $username, ?array $roles = null) : UserInterface
17+
protected function generateUser(string $identity, ?array $roles = null) : UserInterface
1818
{
19-
return new class($username, $roles) implements UserInterface {
20-
private $username;
19+
return new class($identity, $roles) implements UserInterface {
20+
private $identity;
2121
private $roles;
2222

23-
public function __construct(string $username, $roles)
23+
public function __construct(string $identity, $roles)
2424
{
25-
$this->username = $username;
25+
$this->identity = $identity;
2626
$this->roles = $roles ?: [];
2727
}
2828

29-
public function getUsername() : string
29+
public function getIdentity() : string
3030
{
31-
return $this->username;
31+
return $this->identity;
3232
}
3333

3434
public function getUserRoles() : array

src/UserRepositoryInterface.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
interface UserRepositoryInterface
1010
{
1111
/**
12-
* Authenticate the credential (username) using a password
13-
* or using only the credential string (e.g. token based credential)
12+
* Authenticate the identity (id, username, email ...) using a password
13+
* or using only a credential string (e.g. token based credential)
1414
* It returns the authenticated user or null.
1515
*
1616
* @param string $credential can be also a token
@@ -20,8 +20,8 @@ public function authenticate(string $credential, string $password = null) : ?Use
2020
/**
2121
* Get the user roles if present.
2222
*
23-
* @param string $username
23+
* @param string $identity
2424
* @return string[]
2525
*/
26-
public function getRolesFromUser(string $username) : array;
26+
public function getRolesFromUser(string $identity) : array;
2727
}

test/UserRepository/HtpasswdFactoryTest.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,17 @@ protected function setUp()
2222

2323
public function testInvokeWithMissingConfig()
2424
{
25-
$this->container->has('config')->willReturn(false);
26-
$this->container->get('config')->shouldNotBeCalled();
25+
// We cannot throw a ContainerExceptionInterface directly; this
26+
// approach simply mimics `get()` throwing _any_ exception, which is
27+
// what will happen if `config` is not defined.
28+
$this->container->get('config')->willThrow(new InvalidConfigException());
2729

2830
$this->expectException(InvalidConfigException::class);
29-
$htpasswd = ($this->factory)($this->container->reveal());
31+
($this->factory)($this->container->reveal());
3032
}
3133

3234
public function testInvokeWithEmptyConfig()
3335
{
34-
$this->container->has('config')->willReturn(true);
3536
$this->container->get('config')->willReturn([]);
3637

3738
$this->expectException(InvalidConfigException::class);

test/UserRepository/HtpasswdTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public function testAuthenticate()
3333

3434
$user = $htpasswd->authenticate('test', 'password');
3535
$this->assertInstanceOf(UserInterface::class, $user);
36-
$this->assertEquals('test', $user->getUsername());
36+
$this->assertEquals('test', $user->getIdentity());
3737
}
3838

3939
public function testAuthenticateInvalidUser()

test/UserRepository/PdoDatabaseFactoryTest.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use PHPUnit\Framework\TestCase;
1010
use Psr\Container\ContainerInterface;
11+
use Zend\Expressive\Authentication\Exception\InvalidConfigException;
1112
use Zend\Expressive\Authentication\UserRepository\PdoDatabase;
1213
use Zend\Expressive\Authentication\UserRepository\PdoDatabaseFactory;
1314

@@ -19,12 +20,22 @@ protected function setUp()
1920
$this->factory = new PdoDatabaseFactory();
2021
}
2122

22-
/**
23-
* @expectedException Zend\Expressive\Authentication\Exception\InvalidConfigException
24-
*/
23+
public function testInvokeWithMissingConfig()
24+
{
25+
// We cannot throw a ContainerExceptionInterface directly; this
26+
// approach simply mimics `get()` throwing _any_ exception, which is
27+
// what will happen if `config` is not defined.
28+
$this->container->get('config')->willThrow(new InvalidConfigException());
29+
30+
$this->expectException(InvalidConfigException::class);
31+
($this->factory)($this->container->reveal());
32+
}
33+
2534
public function testInvokeWithEmptyConfig()
2635
{
2736
$this->container->get('config')->willReturn([]);
37+
38+
$this->expectException(InvalidConfigException::class);
2839
$pdoDatabase = ($this->factory)($this->container->reveal());
2940
}
3041

@@ -48,7 +59,7 @@ public function getPdoConfig()
4859
'dsn' => 'mysql:dbname=testdb;host=127.0.0.1',
4960
'table' => 'test',
5061
'field' => [
51-
'username' => 'email'
62+
'identity' => 'email'
5263
]
5364
]]
5465
];
@@ -74,7 +85,7 @@ public function testInvokeWithValidConfig()
7485
'dsn' => 'sqlite:'. __DIR__ . '/../TestAssets/pdo.sqlite',
7586
'table' => 'user',
7687
'field' => [
77-
'username' => 'username',
88+
'identity' => 'username',
7889
'password' => 'password'
7990
]
8091
]

0 commit comments

Comments
 (0)