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

Commit 486cb20

Browse files
committed
Merge branch 'feature/28-user-interface-and-factory'
Close #28 Close #25 Fixes #24
2 parents 2fea552 + 84c1079 commit 486cb20

24 files changed

+663
-171
lines changed

CHANGELOG.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,59 @@
22

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

5+
## 0.5.0 - 2018-05-23
6+
7+
### Added
8+
9+
- [#28](https://github.com/zendframework/zend-expressive-authentication/pull/28) adds the final class `DefaultUser`, which provides an immutable version of `UserInterface`
10+
that can be used in most situations.
11+
12+
- [#28](https://github.com/zendframework/zend-expressive-authentication/pull/28) adds the service factory `DefaultUserFactory`, which returns a PHP `callable`
13+
capable of producing a `DefaultUser` instance from the provided `$identity`,
14+
`$roles`, and `$details` arguments.
15+
16+
### Changed
17+
18+
- [#28](https://github.com/zendframework/zend-expressive-authentication/pull/28) updates the `PdoDatabase` user repository to accept an additional
19+
configuration item, `sql_get_details`. This value should be a SQL statement
20+
that may be used to retrieve additional user details to provide in the
21+
`UserInterface` instance returned by the repository on successful
22+
authentication.
23+
24+
- [#28](https://github.com/zendframework/zend-expressive-authentication/pull/28) updates `UserRepositoryInterface` to remove the method `getRolesFromUser()`;
25+
this method is not needed, as `UserInterface` already provides access to user roles.
26+
27+
- [#28](https://github.com/zendframework/zend-expressive-authentication/pull/28) modifies each of the `Htpasswd` and `PdoDatabase` user repository
28+
implementations to accept a new constructor argument, a callable
29+
`$userFactory`. This factory should implement the following signature:
30+
31+
```php
32+
function (string $identity, array $roles = [], array $details = []) : UserInterface
33+
```
34+
35+
This factory will be called by the repository in order to produce a
36+
`UserInterface` instance on successful authentication. You may provide the
37+
factory via the service `Zend\Expressive\Authentication\UserInterface` if you
38+
wish to use one other than the one returned by the provided
39+
`DefaultUserFactory` class.
40+
41+
- [#28](https://github.com/zendframework/zend-expressive-authentication/pull/28) modifies `UserInterface` as follows:
42+
- Renames `getUserRoles()` to `getRoles()`
43+
- Adds `getDetail(string $name, mixed $default)`
44+
- Adds `getDetails() : array`
45+
46+
### Deprecated
47+
48+
- Nothing.
49+
50+
### Removed
51+
52+
- [#28](https://github.com/zendframework/zend-expressive-authentication/pull/28) removes `UserTrait` in favor of the `DefaultUser` implementation.
53+
54+
### Fixed
55+
56+
- Nothing.
57+
558
## 0.4.0 - 2018-03-15
659

760
### Added

docs/book/v1/intro.md

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,26 @@ namespace Zend\Expressive\Authentication;
2222
interface UserInterface
2323
{
2424
/**
25-
* Get the unique user identity (id, username, email address or ...)
26-
*
27-
* @return string
25+
* Get the unique user identity (id, username, email address, etc.).
2826
*/
29-
public function getIdentity(): string;
27+
public function getIdentity() : string;
3028

3129
/**
32-
* Get all user roles
30+
* Get all user roles.
3331
*
3432
* @return string[]
3533
*/
36-
public function getUserRoles() : array;
34+
public function getRoles() : array;
35+
36+
/**
37+
* Get the detail named $name if present; return $default otherwise.
38+
*/
39+
public function getDetail(string $name, $default = null);
40+
41+
/**
42+
* Get all additional user details, if any.
43+
*/
44+
public function getDetails() : array;
3745
}
3846
```
3947

@@ -42,6 +50,48 @@ if a user has been authenticated or not, e.g. it can be used to verify the
4250
authorization level of a user (for this scope, it is consumed by
4351
[zend-expressive-authorization](https://github.com/zendframework/zend-expressive-authorization)).
4452

53+
## Default User class
54+
55+
We provide a default implementation of `UserInterface` via the class
56+
`Zend\Expressive\Authentication\DefaultUser`. The class is final and immutable,
57+
in order to prevent runtime changes.
58+
59+
Repositories will fetch user information based on the identity, including any
60+
associated roles, and optionally any additional details (full name, email,
61+
profile information, etc.). Often, user data and the objects representing them
62+
are unique to the application. As such, the default repository implementations
63+
we provide allow you to inject a _factory_ for producing the user. This factory
64+
should be a PHP callable with the following signature:
65+
66+
```php
67+
function (string $identity, array $roles = [], array $details = []) : UserInterface
68+
```
69+
70+
In order to notify the package to use your custom factory, you will need to
71+
create a service factory that returns it, and map it to the
72+
`Zend\Expressive\Authentication\UserInterface` service.
73+
74+
We provide a service factory named `Zend\Expressive\Authentication\DefaultUserFactory`
75+
that returns a user factory that produces a `DefaultUser` instance from the
76+
arguments provided. This is mapped as follows in the service configuration:
77+
78+
```php
79+
use Zend\Expressive\Authentication\DefaultUserFactory;
80+
use Zend\Expressive\Authentication\UserInterface;
81+
82+
return [
83+
// ...
84+
'dependencies' => [
85+
'factories' => [
86+
// ...
87+
// Change the DefaultUserFactory::class with your custom service
88+
// factory that produces a user factory:
89+
UserInterface::class => DefaultUserFactory::class
90+
]
91+
]
92+
];
93+
```
94+
4595
## Usage in the route
4696

4797
The `AuthenticationMiddleware` can be used to authenticate a route. You just
@@ -75,26 +125,21 @@ Basic Access Authentication* adapter and the *htpasswd* file as the user
75125
repository.
76126

77127
```php
78-
// ConfigProvider.php
79-
80128
use Zend\Expressive\Authentication\AuthenticationInterface;
129+
use Zend\Expressive\Authentication\Basic;
130+
use Zend\Expressive\Authentication\UserRepository;
81131
use Zend\Expressive\Authentication\UserRepositoryInterface;
82132

83-
class ConfigProvider
84-
{
133+
return [
85134
// ...
86-
87-
public function getDependencies() : array
88-
{
89-
return [
90-
'aliases' => [
91-
AuthenticationInterface::class => Basic\BasicAccess::class,
92-
UserRepositoryInterface::class => UserRepository\Htpasswd::class
93-
],
135+
'dependencies' => [
136+
// ...
137+
'aliases' => [
94138
// ...
95-
];
96-
}
139+
AuthenticationInterface::class => Basic\BasicAccess::class,
140+
UserRepositoryInterface::class => UserRepository\Htpasswd::class
141+
]
142+
]
143+
];
97144

98-
// ...
99-
}
100145
```

docs/book/v1/user-repository.md

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,12 @@ interface UserRepositoryInterface
2424
* @param string $credential can be also a token
2525
*/
2626
public function authenticate(string $credential, string $password = null) : ?UserInterface;
27-
28-
/**
29-
* Get the user roles if present.
30-
*
31-
* @param string $username
32-
* @return string[]
33-
*/
34-
public function getRolesFromUser(string $username) : array;
3527
}
3628
```
3729

38-
It contains two functions: `authenticate()` and `getRolesFromUser()`. The first
39-
is used to authenticate using the user's credential. If authenticated, the
40-
result will be a `UserInterface` instance, otherwise a null value is returned.
41-
42-
The second function is `getRolesFromUser()` and it specifies how to retrieve
43-
the roles for a user. If a user does not have roles, this function will return
44-
an empty array.
45-
30+
It contains only the `authenticate()` function, to authenticate the user's
31+
credential. If authenticated, the result will be a `UserInterface` instance;
32+
otherwise, a `null` value is returned.
4633

4734
## Configure the user repository
4835

@@ -93,7 +80,8 @@ return [
9380
'identity' => 'identity field name',
9481
'password' => 'password field name',
9582
],
96-
'sql_get_roles' => 'SQL to retrieve roles with :identity parameter',
83+
'sql_get_roles' => 'SQL to retrieve roles with :identity parameter',
84+
'sql_get_details' => 'SQL to retrieve user details by :identity',
9785
],
9886
],
9987
];
@@ -121,3 +109,13 @@ typical query might look like the following:
121109
```sql
122110
SELECT role FROM user WHERE username = :identity
123111
```
112+
113+
The `sql_get_details` parameter is similar to `sql_get_roles`: it specifies the
114+
SQL query for retrieving the user's additional details, if any.
115+
116+
For instance, if a user has an email field this can be returned as additional
117+
detail using the following query:
118+
119+
```sql
120+
SELECT email FROM user WHERE username = :identity
121+
```

src/ConfigProvider.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
33
* @see https://github.com/zendframework/zend-expressive-authentication for the canonical source repository
4-
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (https://www.zend.com)
4+
* @copyright Copyright (c) 2017-2018 Zend Technologies USA Inc. (https://www.zend.com)
55
* @license https://github.com/zendframework/zend-expressive-authentication/blob/master/LICENSE.md New BSD License
66
*/
77

@@ -25,7 +25,8 @@ public function __invoke() : array
2525
public function getAuthenticationConfig() : array
2626
{
2727
return [
28-
/* Values will depend on user repository and/or adapter.
28+
/*
29+
* Values will depend on user repository and/or adapter.
2930
*
3031
* Example: using htpasswd UserRepositoryInterface implementation:
3132
*
@@ -40,7 +41,8 @@ public function getAuthenticationConfig() : array
4041
* 'identity' => 'identity field name',
4142
* 'password' => 'password field name',
4243
* ],
43-
* 'sql_get_roles' => 'SQL to retrieve roles by :identity',
44+
* 'sql_get_roles' => 'SQL to retrieve user roles by :identity',
45+
* 'sql_get_details' => 'SQL to retrieve user details by :identity',
4446
* ],
4547
* ]
4648
*/
@@ -62,8 +64,9 @@ public function getDependencies() : array
6264
'factories' => [
6365
AuthenticationMiddleware::class => AuthenticationMiddlewareFactory::class,
6466
UserRepository\Htpasswd::class => UserRepository\HtpasswdFactory::class,
65-
UserRepository\PdoDatabase::class => UserRepository\PdoDatabaseFactory::class
66-
]
67+
UserRepository\PdoDatabase::class => UserRepository\PdoDatabaseFactory::class,
68+
UserInterface::class => DefaultUserFactory::class,
69+
],
6770
];
6871
}
6972
}

src/DefaultUser.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-expressive-authentication for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-expressive-authentication/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Zend\Expressive\Authentication;
11+
12+
/**
13+
* Default implementation of UserInterface.
14+
*
15+
* This implementation is modeled as immutable, to prevent propagation of
16+
* user state changes.
17+
*
18+
* We recommend that any details injected are serializable.
19+
*/
20+
final class DefaultUser implements UserInterface
21+
{
22+
/**
23+
* @var string
24+
*/
25+
private $identity;
26+
27+
/**
28+
* @var string[]
29+
*/
30+
private $roles;
31+
32+
/**
33+
* @var array
34+
*/
35+
private $details;
36+
37+
public function __construct(string $identity, array $roles = [], array $details = [])
38+
{
39+
$this->identity = $identity;
40+
$this->roles = $roles;
41+
$this->details = $details;
42+
}
43+
44+
public function getIdentity() : string
45+
{
46+
return $this->identity;
47+
}
48+
49+
public function getRoles() : array
50+
{
51+
return $this->roles;
52+
}
53+
54+
public function getDetails() : array
55+
{
56+
return $this->details;
57+
}
58+
59+
/**
60+
* @param mixed $default Default value to return if no detail matching
61+
* $name is discovered.
62+
* @return mixed
63+
*/
64+
public function getDetail(string $name, $default = null)
65+
{
66+
return $this->details[$name] ?? $default;
67+
}
68+
}

src/DefaultUserFactory.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-expressive-authentication for the canonical source repository
4+
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-expressive-authentication/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Zend\Expressive\Authentication;
11+
12+
use Psr\Container\ContainerInterface;
13+
14+
/**
15+
* Produces a callable factory capable of itself producing a UserInterface
16+
* instance; this approach is used to allow substituting alternative user
17+
* implementations without requiring extensions to existing repositories.
18+
*/
19+
class DefaultUserFactory
20+
{
21+
public function __invoke(ContainerInterface $container) : callable
22+
{
23+
return function (string $identity, array $roles = [], array $details = []) : UserInterface {
24+
return new DefaultUser($identity, $roles, $details);
25+
};
26+
}
27+
}

src/UserInterface.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
33
* @see https://github.com/zendframework/zend-expressive-authentication for the canonical source repository
4-
* @copyright Copyright (c) 2017 Zend Technologies USA Inc. (https://www.zend.com)
4+
* @copyright Copyright (c) 2017-2018 Zend Technologies USA Inc. (https://www.zend.com)
55
* @license https://github.com/zendframework/zend-expressive-authentication/blob/master/LICENSE.md New BSD License
66
*/
77

@@ -21,5 +21,15 @@ public function getIdentity() : string;
2121
*
2222
* @return string[]
2323
*/
24-
public function getUserRoles() : array;
24+
public function getRoles() : array;
25+
26+
/**
27+
* Get a detail $name if present, $default otherwise
28+
*/
29+
public function getDetail(string $name, $default = null);
30+
31+
/**
32+
* Get all the details, if any
33+
*/
34+
public function getDetails() : array;
2535
}

0 commit comments

Comments
 (0)