Skip to content

Commit

Permalink
EZP-31079: Added way to use login or email (or both) during authentic…
Browse files Browse the repository at this point in the history
…ation
  • Loading branch information
Nattfarinn committed Mar 16, 2020
1 parent 4d471dc commit 3d47417
Show file tree
Hide file tree
Showing 20 changed files with 527 additions and 313 deletions.
6 changes: 5 additions & 1 deletion doc/bc/changes-8.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,13 +242,17 @@ Changes affecting version compatibility with former or future versions.

* Deprecated `viewLocation` and `embedLocation` actions of `ViewController` have been dropped, along with
related route `_ezpublishLocation`. As stated in controller, use `viewAction` in place of `viewLocation` and
`embedAction` in place od `embedLocation`.
`embedAction` in place of `embedLocation`.

* Obsolete DeferredLegacy Content Type Update handler
(`eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler\DeferredLegacy`) and its optional
Symfony Container Service (`ezpublish.persistence.legacy.content_type.update_handler.deferred`)
have been removed. Subscribe to eZ Platform Symfony Events to handle deferring of updating
of Content items after their Content Type update instead.

* Deprecated `UserService::loadUserByCredentials` method has been dropped. From now on, we rely on
Security package of Symfony framework to provide authenticated user, as it may happen there are
different ways of providing users configured in the application (ref.: https://symfony.com/doc/current/security/user_provider.html).

## Deprecated features

Expand Down
12 changes: 10 additions & 2 deletions eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
services:
ezpublish.security.user_provider:
class: eZ\Publish\Core\MVC\Symfony\Security\User\Provider
ezpublish.security.user_provider.username:
class: eZ\Publish\Core\MVC\Symfony\Security\User\UsernameProvider
arguments:
- '@eZ\Publish\API\Repository\UserService'
- '@eZ\Publish\API\Repository\PermissionResolver'

ezpublish.security.user_provider.email:
class: eZ\Publish\Core\MVC\Symfony\Security\User\EmailProvider
arguments:
- '@eZ\Publish\API\Repository\UserService'
- '@eZ\Publish\API\Repository\PermissionResolver'
Expand Down Expand Up @@ -40,3 +46,5 @@ services:
- "%fragment.path%"
tags:
- { name: kernel.event_subscriber }

ezpublish.security.user_provider: '@ezpublish.security.user_provider.username'
140 changes: 13 additions & 127 deletions eZ/Publish/API/Repository/Tests/UserServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1252,12 +1252,10 @@ public function testLoadUserThrowsNotFoundException()
}

/**
* Test for the loadUserByCredentials() method.
*
* @see \eZ\Publish\API\Repository\UserService::loadUserByCredentials()
* @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser
* @see \eZ\Publish\API\Repository\UserService::checkUserCredentials()
* @depends \eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser
*/
public function testLoadUserByCredentials()
public function testCheckUserCredentialsValid(): void
{
$repository = $this->getRepository();

Expand All @@ -1266,103 +1264,31 @@ public function testLoadUserByCredentials()
/* BEGIN: Use Case */
$user = $this->createUserVersion1();

// Load the newly created user
$userReloaded = $userService->loadUserByCredentials('user', 'secret', Language::ALL);
// Load the newly created user credentials
$credentialsValid = $userService->loadUserByCredentials($user, 'secret');
/* END: Use Case */

$this->assertEquals($user, $userReloaded);
}

/**
* Test for the loadUserByCredentials() method.
*
* @see \eZ\Publish\API\Repository\UserService::loadUserByCredentials()
* @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserByCredentials
*/
public function testLoadUserByCredentialsThrowsNotFoundExceptionForUnknownPassword()
{
$this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);

$repository = $this->getRepository();

$userService = $repository->getUserService();

/* BEGIN: Use Case */
$this->createUserVersion1();

// This call will fail with a "NotFoundException", because the given
// login/password combination does not exist.
$userService->loadUserByCredentials('user', 'SeCrEt');
/* END: Use Case */
$this->assertTrue($credentialsValid);
}

/**
* Test for the loadUserByCredentials() method.
*
* @see \eZ\Publish\API\Repository\UserService::loadUserByCredentials()
* @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserByCredentials
* @see \eZ\Publish\API\Repository\UserService::checkUserCredentials()
* @depends \eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser
*/
public function testLoadUserByCredentialsThrowsNotFoundExceptionForUnknownPasswordEmtpy()
public function testCheckUserCredentialsInvalid(): void
{
$this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);

$repository = $this->getRepository();

$userService = $repository->getUserService();

/* BEGIN: Use Case */
$this->createUserVersion1();
$user = $this->createUserVersion1();

// This call will fail with a "NotFoundException", because the given
// login/password combination does not exist.
$userService->loadUserByCredentials('user', '');
// Load the newly created user credentials
$credentialsValid = $userService->loadUserByCredentials($user, '1234');
/* END: Use Case */
}

/**
* Test for the loadUserByCredentials() method.
*
* @see \eZ\Publish\API\Repository\UserService::loadUserByCredentials()
* @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserByCredentials
*/
public function testLoadUserByCredentialsThrowsNotFoundExceptionForUnknownLogin()
{
$this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class);

$repository = $this->getRepository();

$userService = $repository->getUserService();

/* BEGIN: Use Case */
$this->createUserVersion1();

// This call will fail with a "NotFoundException", because the given
// login/password combination does not exist.
$userService->loadUserByCredentials('üser', 'secret');
/* END: Use Case */
}

/**
* Test for the loadUserByCredentials() method.
*
* @see \eZ\Publish\API\Repository\UserService::loadUserByCredentials()
* @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserByCredentials
*/
public function testLoadUserByCredentialsThrowsInvalidArgumentValueForEmptyLogin()
{
$this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue::class);

$repository = $this->getRepository();

$userService = $repository->getUserService();

/* BEGIN: Use Case */
$this->createUserVersion1();

// This call will fail with a "InvalidArgumentValue", because the given
// login is empty.
$userService->loadUserByCredentials('', 'secret');
/* END: Use Case */
$this->assertFalse($credentialsValid);
}

/**
Expand Down Expand Up @@ -2441,46 +2367,6 @@ public function testLoadUserByLoginWithPrioritizedLanguagesList(
}
}

/**
* Test that multi-language logic for the loadUserByCredentials method respects
* prioritized language list.
*
* @covers \eZ\Publish\API\Repository\UserService::loadUserByCredentials
* @dataProvider getPrioritizedLanguageList
* @param string[] $prioritizedLanguages
* @param string|null $expectedLanguageCode language code of expected translation
*/
public function testLoadUserByCredentialsWithPrioritizedLanguagesList(
array $prioritizedLanguages,
$expectedLanguageCode
) {
$repository = $this->getRepository();
$userService = $repository->getUserService();
$user = $this->createMultiLanguageUser();

// load, with prioritized languages, the newly created user
$loadedUser = $userService->loadUserByCredentials(
$user->login,
'secret',
$prioritizedLanguages
);
if ($expectedLanguageCode === null) {
$expectedLanguageCode = $loadedUser->contentInfo->mainLanguageCode;
}

self::assertEquals(
$loadedUser->getName($expectedLanguageCode),
$loadedUser->getName()
);

foreach (['first_name', 'last_name', 'signature'] as $fieldIdentifier) {
self::assertEquals(
$loadedUser->getFieldValue($fieldIdentifier, $expectedLanguageCode),
$loadedUser->getFieldValue($fieldIdentifier)
);
}
}

/**
* Test that multi-language logic for the loadUsersByEmail method respects
* prioritized language list.
Expand Down
27 changes: 12 additions & 15 deletions eZ/Publish/API/Repository/UserService.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,24 +142,22 @@ public function createUser(UserCreateStruct $userCreateStruct, array $parentGrou
public function loadUser($userId, array $prioritizedLanguages = []);

/**
* Loads a user for the given login and password.
* Loads a user for the given login.
*
* Since 6.1 login is case-insensitive across all storage engines and database backends, however if login
* is part of the password hash this method will essentially be case sensitive.
* Since 6.1 login is case-insensitive across all storage engines and database backends, like was the case
* with mysql before in eZ Publish 3.x/4.x/5.x.
*
* @deprecated since eZ Platform 2.5, will be dropped in the next major version as authentication
* may depend on various user providers. Use UserService::checkUserCredentials() instead.
*
* @param string $login
* @param string $password the plain password
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
*
* @return \eZ\Publish\API\Repository\Values\User\User
*
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if credentials are invalid
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given credentials was not found
*/
public function loadUserByCredentials($login, $password, array $prioritizedLanguages = []);
public function loadUserByLogin($login, array $prioritizedLanguages = []);

/**
* Checks if credentials are valid for provided User.
Expand All @@ -172,22 +170,19 @@ public function loadUserByCredentials($login, $password, array $prioritizedLangu
public function checkUserCredentials(User $user, string $credentials): bool;

/**
* Loads a user for the given login.
*
* Since 6.1 login is case-insensitive across all storage engines and database backends, like was the case
* with mysql before in eZ Publish 3.x/4.x/5.x.
* Loads a user for the given email.
*
* @param string $login
* @param string $email
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
*
* @return \eZ\Publish\API\Repository\Values\User\User
*
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given credentials was not found
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
*/
public function loadUserByLogin($login, array $prioritizedLanguages = []);
public function loadUserByEmail(string $email, array $prioritizedLanguages = []): User;

/**
* Loads a user for the given email.
* Loads a users for the given email.
*
* Note: This method loads user by $email where $email might be case-insensitive on certain storage engines!
*
Expand All @@ -198,8 +193,10 @@ public function loadUserByLogin($login, array $prioritizedLanguages = []);
* @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object.
*
* @return \eZ\Publish\API\Repository\Values\User\User[]
*
* @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException
*/
public function loadUsersByEmail($email, array $prioritizedLanguages = []);
public function loadUsersByEmail(string $email, array $prioritizedLanguages = []): array;

/**
* Loads a user with user hash key.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public function testCheckAuthentication()
$password = 'foo';
$token = new UsernamePasswordToken($userName, $password, 'bar');

$apiUser = $this->getMockForAbstractClass(APIUser::class);
$apiUser = $this->createMock(APIUser::class);
$user = $this->createMock(User::class);
$user->method('getAPIUser')
->willReturn($apiUser);
Expand Down
Loading

0 comments on commit 3d47417

Please sign in to comment.