diff --git a/config/authorization.yaml b/config/authorization.yaml index 9aa01420a..a153afe83 100644 --- a/config/authorization.yaml +++ b/config/authorization.yaml @@ -13,5 +13,6 @@ services: tags: [ 'controller.service_arguments' ] - Pimcore\Bundle\StudioBackendBundle\Authorization\Service\TokenServiceInterface: - class: Pimcore\Bundle\StudioBackendBundle\Authorization\Service\TokenService + Pimcore\Bundle\StudioBackendBundle\Authorization\EventSubscriber\LogoutSubscriber: + tags: + - { name: 'kernel.event_subscriber', dispatcher: 'security.event_dispatcher.pimcore_studio' } \ No newline at end of file diff --git a/config/pimcore/config.yaml b/config/pimcore/config.yaml index 59c7e2cd7..95dc438b5 100644 --- a/config/pimcore/config.yaml +++ b/config/pimcore/config.yaml @@ -1,3 +1,7 @@ +imports: + - { resource: security.yaml } + - { resource: firewall.yaml } + pimcore: translations: domains: diff --git a/config/pimcore/firewall.yaml b/config/pimcore/firewall.yaml new file mode 100644 index 000000000..b117c8644 --- /dev/null +++ b/config/pimcore/firewall.yaml @@ -0,0 +1,14 @@ +pimcore_studio_backend: + security_firewall: + pattern: ^/studio/api(/.*)?$ + user_checker: Pimcore\Security\User\UserChecker + context: pimcore_admin + provider: pimcore_studio_backend + stateless: false + login_throttling: + max_attempts: 3 + interval: '5 minutes' + logout: + path: pimcore_studio_api_logout + json_login: + check_path: pimcore_studio_api_login \ No newline at end of file diff --git a/config/pimcore/security.yaml b/config/pimcore/security.yaml new file mode 100644 index 000000000..e4ae3f7b1 --- /dev/null +++ b/config/pimcore/security.yaml @@ -0,0 +1,4 @@ +security: + providers: + pimcore_studio_backend: + id: Pimcore\Security\User\UserProvider \ No newline at end of file diff --git a/doc/01_Installation.md b/doc/01_Installation.md index f6ee5a77d..2d84b53ec 100644 --- a/doc/01_Installation.md +++ b/doc/01_Installation.md @@ -37,4 +37,17 @@ bin/console pimcore:bundle:install PimcoreStudioBackendBundle ## Setting up generic data index Pimcore Studio Backend also requires the installation and setup of the generic data index. The bundle is required by default and also automatically enabled in the bundles. -To install the generic data index refer to [Generic-Data-Index](https://github.com/pimcore/generic-data-index-bundle?tab=readme-ov-file) \ No newline at end of file +To install the generic data index refer to [Generic-Data-Index](https://github.com/pimcore/generic-data-index-bundle?tab=readme-ov-file) + +## Enable Firewall settings + +To enable the firewall settings, add the following configuration to your `config/packages/security.yaml` file: + +```yaml +security: + firewalls: + pimcore_studio: '%pimcore_studio_backend.firewall_settings%' + access_control: + - { path: ^/studio/api/(docs|docs.json|translations)$, roles: PUBLIC_ACCESS } + - { path: ^/studio, roles: ROLE_PIMCORE_USER } +``` diff --git a/src/Asset/Controller/CollectionController.php b/src/Asset/Controller/CollectionController.php index 0c953795c..edb6639a8 100644 --- a/src/Asset/Controller/CollectionController.php +++ b/src/Asset/Controller/CollectionController.php @@ -73,7 +73,6 @@ public function __construct( operationId: 'getAssets', description: 'Get paginated assets', summary: 'Get all assets', - security: self::SECURITY_SCHEME, tags: [Tags::Assets->name] )] #[PageParameter] diff --git a/src/Asset/Controller/CustomSettingsController.php b/src/Asset/Controller/CustomSettingsController.php index e75189420..5d06f123d 100644 --- a/src/Asset/Controller/CustomSettingsController.php +++ b/src/Asset/Controller/CustomSettingsController.php @@ -55,7 +55,6 @@ public function __construct( operationId: 'getAssetCustomSettingsById', description: 'Get custom settings of an asset by its id by path parameter', summary: 'Get custom settings of an asset by id', - security: self::SECURITY_SCHEME, tags: [Tags::Assets->name] )] #[IdParameter(type: 'asset')] diff --git a/src/Asset/Controller/Data/TextController.php b/src/Asset/Controller/Data/TextController.php index a3b2d08c0..24218e4e0 100644 --- a/src/Asset/Controller/Data/TextController.php +++ b/src/Asset/Controller/Data/TextController.php @@ -58,7 +58,6 @@ public function __construct( path: self::API_PATH . '/assets/{id}/text', operationId: 'getAssetDataTextById', summary: 'Get asset data in text UTF8 representation by id', - security: self::SECURITY_SCHEME, tags: [Tags::Assets->name] )] #[IdParameter(type: 'asset')] diff --git a/src/Asset/Controller/GetController.php b/src/Asset/Controller/GetController.php index 93d660e59..2cb35d868 100644 --- a/src/Asset/Controller/GetController.php +++ b/src/Asset/Controller/GetController.php @@ -54,7 +54,6 @@ public function __construct( operationId: 'getAssetById', description: 'Get assets by id by path parameter', summary: 'Get assets by id', - security: self::SECURITY_SCHEME, tags: [Tags::Assets->name] )] #[IdParameter(type: 'asset')] diff --git a/src/Asset/Controller/UpdateController.php b/src/Asset/Controller/UpdateController.php index 14c4a41fe..6b05f9b80 100644 --- a/src/Asset/Controller/UpdateController.php +++ b/src/Asset/Controller/UpdateController.php @@ -54,7 +54,6 @@ public function __construct( operationId: 'updateAssetById', description: 'Update assets by id', summary: 'Update asset', - security: self::SECURITY_SCHEME, tags: [Tags::Assets->name] )] #[IdParameter(type: 'asset')] diff --git a/src/Authorization/Attributes/Request/TokenRequestBody.php b/src/Authorization/Attributes/Response/InvalidCredentialsResponse.php similarity index 66% rename from src/Authorization/Attributes/Request/TokenRequestBody.php rename to src/Authorization/Attributes/Response/InvalidCredentialsResponse.php index fb6c49f06..e725a9e52 100644 --- a/src/Authorization/Attributes/Request/TokenRequestBody.php +++ b/src/Authorization/Attributes/Response/InvalidCredentialsResponse.php @@ -14,21 +14,25 @@ * @license http://www.pimcore.org/license GPLv3 and PCL */ -namespace Pimcore\Bundle\StudioBackendBundle\Authorization\Attributes\Request; +namespace Pimcore\Bundle\StudioBackendBundle\Authorization\Attributes\Response; use Attribute; use OpenApi\Attributes\JsonContent; -use OpenApi\Attributes\RequestBody; -use Pimcore\Bundle\StudioBackendBundle\Authorization\Schema\Refresh; +use OpenApi\Attributes\Response; +use Pimcore\Bundle\StudioBackendBundle\Authorization\Schema\InvalidCredentials; +/** + * @internal + */ #[Attribute(Attribute::TARGET_METHOD)] -final class TokenRequestBody extends RequestBody +final class InvalidCredentialsResponse extends Response { public function __construct() { parent::__construct( - required: true, - content: new JsonContent(ref: Refresh::class) + response: 401, + description: 'Invalid credentials Response', + content: new JsonContent(ref: InvalidCredentials::class) ); } } diff --git a/src/Authorization/Controller/AuthorizationController.php b/src/Authorization/Controller/AuthorizationController.php deleted file mode 100644 index 4798e68d1..000000000 --- a/src/Authorization/Controller/AuthorizationController.php +++ /dev/null @@ -1,111 +0,0 @@ -name] - )] - #[CredentialsRequestBody] - #[SuccessResponse( - description: 'Token, lifetime and user identifier', - content: new JsonContent(ref: Token::class) - )] - #[DefaultResponses([ - HttpResponseCodes::UNAUTHORIZED - ])] - public function login(#[MapRequestPayload] Credentials $credentials): JsonResponse - { - /** @var User $user */ - $user = $this->securityService->authenticateUser($credentials); - - $token = $this->tokenService->generateAndSaveToken($user->getUserIdentifier()); - - return $this->jsonResponse(new Token($token, $this->tokenService->getLifetime(), $user->getUserIdentifier())); - } - - /** - * @throws TokenNotFoundException - */ - #[Route('/refresh', name: 'pimcore_studio_api_refresh', methods: ['POST'])] - #[POST( - path: self::API_PATH . '/refresh', - operationId: 'refresh', - summary: 'Login with user credentials and get access token', - tags: ['Authorization'] - )] - #[TokenRequestBody] - #[SuccessResponse( - description: 'Token, lifetime and user identifier', - content: new JsonContent(ref: Token::class) - )] - #[DefaultResponses([ - HttpResponseCodes::UNAUTHORIZED - ])] - public function refresh(#[MapRequestPayload] Refresh $refresh): JsonResponse - { - $tokenInfo = $this->tokenService->refreshToken($refresh->getToken()); - - return $this->jsonResponse( - new Token( - $tokenInfo->getToken(), - $this->tokenService->getLifetime(), - $tokenInfo->getUsername()) - ); - } -} diff --git a/src/Authorization/Controller/LoginController.php b/src/Authorization/Controller/LoginController.php new file mode 100644 index 000000000..2d31bcff6 --- /dev/null +++ b/src/Authorization/Controller/LoginController.php @@ -0,0 +1,59 @@ +name] + )] + #[CredentialsRequestBody] + #[SuccessResponse( + description: 'Login successful', + content: new JsonContent(ref: UserInformation::class) + )] + #[InvalidCredentialsResponse] + #[DefaultResponses] + public function login(#[CurrentUser] User $user): JsonResponse + { + return $this->jsonResponse([ + 'username' => $user->getUserIdentifier(), + 'roles' => $user->getRoles(), + ]); + } +} diff --git a/src/Authorization/Controller/LogoutController.php b/src/Authorization/Controller/LogoutController.php new file mode 100644 index 000000000..83de04bdf --- /dev/null +++ b/src/Authorization/Controller/LogoutController.php @@ -0,0 +1,45 @@ +name] + )] + #[SuccessResponse( + description: 'Logout successful', + )] + public function logout(): void + { + throw new UnreachableException('Should not be called. Handled by symfony.'); + } +} diff --git a/src/Authorization/EventSubscriber/LogoutSubscriber.php b/src/Authorization/EventSubscriber/LogoutSubscriber.php new file mode 100644 index 000000000..31375522e --- /dev/null +++ b/src/Authorization/EventSubscriber/LogoutSubscriber.php @@ -0,0 +1,40 @@ + 'onLogout', + ]; + } + + public function onLogout(LogoutEvent $event): void + { + $event->setResponse(new Response(null, HttpResponseCodes::SUCCESS->value)); + } +} diff --git a/src/Authorization/Schema/Credentials.php b/src/Authorization/Schema/Credentials.php index e97b25ba0..5f76bc075 100644 --- a/src/Authorization/Schema/Credentials.php +++ b/src/Authorization/Schema/Credentials.php @@ -25,6 +25,7 @@ #[Schema( title: 'Credentials', description: 'Credentials for authentication', + required: ['username', 'password'], type: 'object' )] final readonly class Credentials diff --git a/src/Authorization/Schema/Refresh.php b/src/Authorization/Schema/InvalidCredentials.php similarity index 65% rename from src/Authorization/Schema/Refresh.php rename to src/Authorization/Schema/InvalidCredentials.php index 88d3bc1e5..6835766d0 100644 --- a/src/Authorization/Schema/Refresh.php +++ b/src/Authorization/Schema/InvalidCredentials.php @@ -16,6 +16,7 @@ namespace Pimcore\Bundle\StudioBackendBundle\Authorization\Schema; +use OpenApi\Attributes\Items; use OpenApi\Attributes\Property; use OpenApi\Attributes\Schema; @@ -23,20 +24,20 @@ * @internal */ #[Schema( - title: 'Refresh', - description: 'Token that needs to be refresh', + title: 'Invalid Credentials', + description: 'Invalid credentials after login attempt', + required: ['error'], type: 'object' )] -final readonly class Refresh +final readonly class InvalidCredentials { public function __construct( - #[Property(description: 'Token', type: 'string', example: 'Who you gonna call? Refresh token!')] - private string $token + #[Property(description: 'Error', type: 'string', example: 'Invalid credentials')] + private string $error, ) { } - - public function getToken(): string + public function getError(): string { - return $this->token; + return $this->error; } } diff --git a/src/Authorization/Service/TokenService.php b/src/Authorization/Service/TokenService.php deleted file mode 100644 index 3d046195d..000000000 --- a/src/Authorization/Service/TokenService.php +++ /dev/null @@ -1,111 +0,0 @@ -tokenGenerator->generateToken(); - $entry = $this->tmpStoreResolver->get($token); - } while ($entry !== null); - - $this->saveToken(new Info($token, $userIdentifier)); - - return $token; - } - - /** - * @throws TokenNotFoundException - */ - public function refreshToken(string $token): Info - { - $entry = $this->tmpStoreResolver->get($token); - if($entry === null) { - throw new TokenNotFoundException('Token not found'); - } - - $data = $entry->getData(); - - if(!isset($data['username'])) { - throw new TokenNotFoundException('Token not found'); - } - - $tokenInfo = new Info($token, $data['username']); - $this->saveToken($tokenInfo); - - return $tokenInfo; - } - - public function getLifetime(): int - { - return $this->tokenLifetime; - } - - private function saveToken(Info $tokenInfo): void - { - $this->tmpStoreResolver->set( - $tokenInfo->getToken(), - [ - 'username' => $tokenInfo->getUsername(), - ], - $this->getTmpStoreTag($tokenInfo->getUsername()), - $this->tokenLifetime - ); - } - - public function getCurrentToken(): string - { - $currentRequest = $this->getCurrentRequest($this->requestStack); - - return $this->getAuthToken($currentRequest); - } - - private function getTmpStoreTag(string $userId): string - { - return str_replace( - self::TMP_STORE_TAG_PLACEHOLDER, - $userId, - self::TMP_STORE_TAG - ); - } -} diff --git a/src/Authorization/Service/TokenServiceInterface.php b/src/Authorization/Service/TokenServiceInterface.php deleted file mode 100644 index e987d8d04..000000000 --- a/src/Authorization/Service/TokenServiceInterface.php +++ /dev/null @@ -1,37 +0,0 @@ - []]]; - public function __construct(protected readonly SerializerInterface $serializer) { diff --git a/src/DataObject/Controller/CollectionController.php b/src/DataObject/Controller/CollectionController.php index 8d19b4181..fef85b043 100644 --- a/src/DataObject/Controller/CollectionController.php +++ b/src/DataObject/Controller/CollectionController.php @@ -66,7 +66,6 @@ public function __construct( operationId: 'getDataObjects', description: 'Get paginated data objects', summary: 'Get all DataObjects', - security: self::SECURITY_SCHEME, tags: [Tags::DataObjects->name], )] #[PageParameter] diff --git a/src/DataObject/Controller/GetController.php b/src/DataObject/Controller/GetController.php index 7871d4adc..1dfb87feb 100644 --- a/src/DataObject/Controller/GetController.php +++ b/src/DataObject/Controller/GetController.php @@ -49,7 +49,6 @@ public function __construct( operationId: 'getDataObjectById', description: 'Get data object by id by path parameter', summary: 'Get data object by id', - security: self::SECURITY_SCHEME, tags: [Tags::DataObjects->name] )] #[IdParameter(type: 'data-object')] diff --git a/src/Dependency/Controller/Element/CollectionController.php b/src/Dependency/Controller/Element/CollectionController.php index fa796980c..a9576c200 100644 --- a/src/Dependency/Controller/Element/CollectionController.php +++ b/src/Dependency/Controller/Element/CollectionController.php @@ -63,7 +63,6 @@ public function __construct( Pass dependency mode to get either all elements that depend on the provided element or all dependencies for the provided element.', summary: 'Get all dependencies for provided element.', - security: self::SECURITY_SCHEME, tags: [Tags::Dependencies->name] )] #[ElementTypeParameter] diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index f3d4eccbd..cd11bca7d 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -17,7 +17,6 @@ namespace Pimcore\Bundle\StudioBackendBundle\DependencyInjection; use Pimcore\Bundle\StudioBackendBundle\Exception\InvalidHostException; -use Pimcore\Bundle\StudioBackendBundle\Exception\InvalidPathException; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -43,6 +42,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addOpenApiScanPathsNode($rootNode); $this->addApiTokenNode($rootNode); $this->addAllowedHostsForCorsNode($rootNode); + $this->addSecurityFirewall($rootNode); return $treeBuilder; } @@ -52,22 +52,6 @@ private function addOpenApiScanPathsNode(ArrayNodeDefinition $node): void $node->children() ->arrayNode('open_api_scan_paths') ->prototype('scalar')->end() - ->validate() - ->always( - function ($paths) { - foreach ($paths as $path) { - if (!is_dir($path)) { - throw new InvalidPathException( - sprintf( - 'The path "%s" is not a valid directory.', - $path - ) - ); - } - } - - return $paths; - }) ->end() ->end(); } @@ -111,4 +95,12 @@ private function addAllowedHostsForCorsNode(ArrayNodeDefinition $node): void ->end() ->end(); } + + public function addSecurityFirewall(ArrayNodeDefinition $node): void + { + $node + ->children() + ->variableNode('security_firewall')->end() + ->end(); + } } diff --git a/src/DependencyInjection/PimcoreStudioBackendExtension.php b/src/DependencyInjection/PimcoreStudioBackendExtension.php index 264100c19..e2a62ef8d 100644 --- a/src/DependencyInjection/PimcoreStudioBackendExtension.php +++ b/src/DependencyInjection/PimcoreStudioBackendExtension.php @@ -17,11 +17,13 @@ namespace Pimcore\Bundle\StudioBackendBundle\DependencyInjection; use Exception; -use Pimcore\Bundle\StudioBackendBundle\Authorization\Service\TokenServiceInterface; +use Pimcore\Bundle\CoreBundle\DependencyInjection\ConfigurationHelper; use Pimcore\Bundle\StudioBackendBundle\EventSubscriber\CorsSubscriber; +use Pimcore\Bundle\StudioBackendBundle\Exception\InvalidPathException; use Pimcore\Bundle\StudioBackendBundle\OpenApi\Service\OpenApiServiceInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -34,7 +36,7 @@ /** * @internal */ -class PimcoreStudioBackendExtension extends Extension +class PimcoreStudioBackendExtension extends Extension implements PrependExtensionInterface { /** * {@inheritdoc} @@ -72,13 +74,36 @@ public function load(array $configs, ContainerBuilder $container): void $loader->load('users.yaml'); $loader->load('versions.yaml'); - $definition = $container->getDefinition(TokenServiceInterface::class); - $definition->setArgument('$tokenLifetime', $config['api_token']['lifetime']); - + $this->checkValidOpenApiScanPaths($config['open_api_scan_paths']); $definition = $container->getDefinition(OpenApiServiceInterface::class); $definition->setArgument('$openApiScanPaths', $config['open_api_scan_paths']); $definition = $container->getDefinition(CorsSubscriber::class); $definition->setArgument('$allowedHosts', $config['allowed_hosts_for_cors']); } + + public function prepend(ContainerBuilder $container): void + { + if (!$container->hasParameter('pimcore_studio_backend.firewall_settings')) { + $containerConfig = ConfigurationHelper::getConfigNodeFromSymfonyTree($container, 'pimcore_studio_backend'); + $container->setParameter('pimcore_studio_backend.firewall_settings', $containerConfig['security_firewall']); + } + } + + /** + * @throws InvalidPathException + */ + private function checkValidOpenApiScanPaths(array $config): void + { + foreach ($config as $path) { + if (!is_dir($path)) { + throw new InvalidPathException( + sprintf( + 'The path "%s" is not a valid directory.', + $path + ) + ); + } + } + } } diff --git a/tests/Unit/Dto/Token/RefreshTest.php b/src/Exception/UnreachableException.php similarity index 57% rename from tests/Unit/Dto/Token/RefreshTest.php rename to src/Exception/UnreachableException.php index 96510fbbb..15edd6f27 100644 --- a/tests/Unit/Dto/Token/RefreshTest.php +++ b/src/Exception/UnreachableException.php @@ -14,16 +14,15 @@ * @license http://www.pimcore.org/license GPLv3 and PCL */ -namespace Pimcore\Bundle\StudioBackendBundle\Tests\Unit\Dto\Token; +namespace Pimcore\Bundle\StudioBackendBundle\Exception; -use Codeception\Test\Unit; -use Pimcore\Bundle\StudioBackendBundle\Authorization\Schema\Refresh; - -final class RefreshTest extends Unit +/** + * @internal + */ +final class UnreachableException extends AbstractApiException { - public function testTokenRefresh(): void + public function __construct(string $message) { - $refresh = new Refresh('token'); - $this->assertSame('token', $refresh->getToken()); + parent::__construct(500, $message); } } diff --git a/src/Note/Controller/CollectionController.php b/src/Note/Controller/CollectionController.php index 892f6cf3d..30530ebad 100644 --- a/src/Note/Controller/CollectionController.php +++ b/src/Note/Controller/CollectionController.php @@ -67,7 +67,6 @@ public function __construct( path: self::API_PATH . '/notes', operationId: 'getNotes', summary: 'Get notes', - security: self::SECURITY_SCHEME, tags: [Tags::Notes->name] )] #[PageParameter] diff --git a/src/Note/Controller/DeleteController.php b/src/Note/Controller/DeleteController.php index ea3adcafc..d1dd3a817 100644 --- a/src/Note/Controller/DeleteController.php +++ b/src/Note/Controller/DeleteController.php @@ -54,7 +54,6 @@ public function __construct( path: self::API_PATH . '/notes/{id}', operationId: 'deleteNote', summary: 'Deleting note by id', - security: self::SECURITY_SCHEME, tags: [Tags::Notes->name] )] #[IdParameter] diff --git a/src/Note/Controller/Element/CollectionController.php b/src/Note/Controller/Element/CollectionController.php index 78c606001..f2479a003 100644 --- a/src/Note/Controller/Element/CollectionController.php +++ b/src/Note/Controller/Element/CollectionController.php @@ -69,7 +69,6 @@ public function __construct( path: self::API_PATH . '/notes/{elementType}/{id}', operationId: 'getNotesForElementByTypeAndId', summary: 'Get notes for an element', - security: self::SECURITY_SCHEME, tags: [Tags::NotesForElement->name] )] #[ElementTypeParameter] diff --git a/src/Note/Controller/Element/CreateController.php b/src/Note/Controller/Element/CreateController.php index 8ce71d025..3a2cf6429 100644 --- a/src/Note/Controller/Element/CreateController.php +++ b/src/Note/Controller/Element/CreateController.php @@ -58,7 +58,6 @@ public function __construct( path: self::API_PATH . '/notes/{elementType}/{id}', operationId: 'createNoteForElement', summary: 'Creating new note for element', - security: self::SECURITY_SCHEME, tags: [Tags::NotesForElement->name] )] #[ElementTypeParameter] diff --git a/src/OpenApi/Config/Security.php b/src/OpenApi/Config/Security.php deleted file mode 100644 index d25ff43f0..000000000 --- a/src/OpenApi/Config/Security.php +++ /dev/null @@ -1,37 +0,0 @@ -token; + return $this->roles; } - - public function getLifetime(): int - { - return $this->lifetime; - } - public function getUsername(): string { return $this->username; diff --git a/src/Property/Controller/CollectionController.php b/src/Property/Controller/CollectionController.php index 5b19398e3..f56883ef5 100644 --- a/src/Property/Controller/CollectionController.php +++ b/src/Property/Controller/CollectionController.php @@ -55,7 +55,6 @@ public function __construct( path: self::API_PATH . '/properties', operationId: 'getProperties', summary: 'Get all predefined properties. You can filter by type and query', - security: self::SECURITY_SCHEME, tags: [Tags::Properties->name] )] #[ElementTypeParameter(false, null)] diff --git a/src/Property/Controller/CreateController.php b/src/Property/Controller/CreateController.php index ff65575e9..65d6bc007 100644 --- a/src/Property/Controller/CreateController.php +++ b/src/Property/Controller/CreateController.php @@ -50,7 +50,6 @@ public function __construct( path: self::API_PATH . '/property', operationId: 'createProperty', summary: 'Creating new property with default values', - security: self::SECURITY_SCHEME, tags: [Tags::Properties->name] )] #[SuccessResponse( diff --git a/src/Property/Controller/DeleteController.php b/src/Property/Controller/DeleteController.php index bf2d51b31..ea27cfdb0 100644 --- a/src/Property/Controller/DeleteController.php +++ b/src/Property/Controller/DeleteController.php @@ -52,7 +52,6 @@ public function __construct( path: self::API_PATH . '/properties/{id}', operationId: 'deleteProperty', summary: 'Delete property with given id', - security: self::SECURITY_SCHEME, tags: [Tags::Properties->name] )] #[IdParameter(type: 'property', schema: new Schema(type: 'string', example: 'alpha-numerical'))] diff --git a/src/Property/Controller/Element/CollectionController.php b/src/Property/Controller/Element/CollectionController.php index a69e49872..ae532ec82 100644 --- a/src/Property/Controller/Element/CollectionController.php +++ b/src/Property/Controller/Element/CollectionController.php @@ -53,7 +53,6 @@ public function __construct( path: self::API_PATH . '/properties/{elementType}/{id}', operationId: 'getPropertiesForElementByTypeAndId', summary: 'Get properties for an element based on the element type and the element id', - security: self::SECURITY_SCHEME, tags: [Tags::PropertiesForElement->value] )] #[ElementTypeParameter] diff --git a/src/Property/Controller/UpdateController.php b/src/Property/Controller/UpdateController.php index 92b1f5da2..767d8bc22 100644 --- a/src/Property/Controller/UpdateController.php +++ b/src/Property/Controller/UpdateController.php @@ -55,7 +55,6 @@ public function __construct( path: self::API_PATH . '/properties/{id}', operationId: 'updateProperty', summary: 'Updating a property', - security: self::SECURITY_SCHEME, tags: [Tags::Properties->name] )] #[IdParameter(type: 'property', schema: new Schema(type: 'string', example: 'alpha-numerical'))] diff --git a/src/Schedule/Controller/DeleteController.php b/src/Schedule/Controller/DeleteController.php index ad6108270..905b2cc7c 100644 --- a/src/Schedule/Controller/DeleteController.php +++ b/src/Schedule/Controller/DeleteController.php @@ -53,7 +53,6 @@ public function __construct( path: self::API_PATH . '/schedules/{id}', operationId: 'deleteSchedule', summary: 'Delete schedule with given id', - security: self::SECURITY_SCHEME, tags: [Tags::Schedule->name] )] #[IdParameter(type: 'schedule', schema: new Schema(type: 'integer', example: 123))] diff --git a/src/Schedule/Controller/Element/CollectionController.php b/src/Schedule/Controller/Element/CollectionController.php index eff362184..d59559d7d 100644 --- a/src/Schedule/Controller/Element/CollectionController.php +++ b/src/Schedule/Controller/Element/CollectionController.php @@ -52,7 +52,6 @@ public function __construct( path: self::API_PATH . '/schedules/{elementType}/{id}', operationId: 'getSchedulesForElementByTypeAndId', summary: 'Get schedules for an element', - security: self::SECURITY_SCHEME, tags: [Tags::Schedule->name] )] #[ElementTypeParameter] diff --git a/src/Schedule/Controller/Element/CreateController.php b/src/Schedule/Controller/Element/CreateController.php index bc174a224..31839e56d 100644 --- a/src/Schedule/Controller/Element/CreateController.php +++ b/src/Schedule/Controller/Element/CreateController.php @@ -54,7 +54,6 @@ public function __construct( path: self::API_PATH . '/schedules/{elementType}/{id}', operationId: 'createSchedule', summary: 'Create schedule for element', - security: self::SECURITY_SCHEME, tags: [Tags::Schedule->name] )] #[ElementTypeParameter] diff --git a/src/Schedule/Controller/Element/UpdateController.php b/src/Schedule/Controller/Element/UpdateController.php index 6d682e04c..bb30f2e70 100644 --- a/src/Schedule/Controller/Element/UpdateController.php +++ b/src/Schedule/Controller/Element/UpdateController.php @@ -59,7 +59,6 @@ public function __construct( path: self::API_PATH . '/schedules/{elementType}/{id}', operationId: 'updateSchedulesForElementByTypeAndId', summary: 'Update schedules for an element', - security: self::SECURITY_SCHEME, tags: [Tags::Schedule->name] )] #[ElementTypeParameter] diff --git a/src/Security/Service/SecurityService.php b/src/Security/Service/SecurityService.php index 5ef9ee5ac..1fc29db10 100644 --- a/src/Security/Service/SecurityService.php +++ b/src/Security/Service/SecurityService.php @@ -17,12 +17,10 @@ namespace Pimcore\Bundle\StudioBackendBundle\Security\Service; use Pimcore\Bundle\GenericDataIndexBundle\Service\Permission\ElementPermissionServiceInterface; +use Pimcore\Bundle\StaticResolverBundle\Lib\Tools\Authentication\AuthenticationResolverInterface; use Pimcore\Bundle\StaticResolverBundle\Models\Tool\TmpStoreResolverInterface; -use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolverInterface; use Pimcore\Bundle\StudioBackendBundle\Authorization\Schema\Credentials; -use Pimcore\Bundle\StudioBackendBundle\Authorization\Service\TokenServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Exception\AccessDeniedException; -use Pimcore\Bundle\StudioBackendBundle\Exception\NotAuthorizedException; use Pimcore\Model\Element\ElementInterface; use Pimcore\Model\User; use Pimcore\Model\UserInterface; @@ -38,60 +36,21 @@ { public function __construct( private ElementPermissionServiceInterface $elementPermissionService, - private UserProvider $userProvider, - private UserResolverInterface $userResolver, - private UserPasswordHasherInterface $passwordHasher, - private TmpStoreResolverInterface $tmpStoreResolver, - private TokenServiceInterface $tokenService, + private AuthenticationResolverInterface $authenticationResolver, ) { } /** - * @throws AccessDeniedException - */ - public function authenticateUser(Credentials $credentials): PasswordAuthenticatedUserInterface - { - try { - $user = $this->userProvider->loadUserByIdentifier($credentials->getUsername()); - } catch (UserNotFoundException) { - throw new AccessDeniedException(); - } - - if( - !$user instanceof PasswordAuthenticatedUserInterface || - !$this->passwordHasher->isPasswordValid($user, $credentials->getPassword()) - ) { - throw new AccessDeniedException(); - } - - return $user; - } - - public function checkAuthToken(string $token): bool - { - $entry = $this->tmpStoreResolver->get($token); - - return $entry !== null && $entry->getId() === $token; - } - - /** - * @throws NotAuthorizedException + * @throws UserNotFoundException */ public function getCurrentUser(): UserInterface { - $entry = $this->tmpStoreResolver->get($this->tokenService->getCurrentToken()); - - if($entry === null || !is_array($entry->getData()) || !isset($entry->getData()['username'])) { - throw new NotAuthorizedException(); - } - - $user = $this->userResolver->getByName($entry->getData()['username']); - - if(!$user) { - throw new NotAuthorizedException(); + $pimcoreUser = $this->authenticationResolver->authenticateSession(); + if (!$pimcoreUser instanceof User) { + throw new UserNotFoundException('No pimcore user found'); } - return $user; + return $pimcoreUser; } /** diff --git a/src/Security/Service/SecurityServiceInterface.php b/src/Security/Service/SecurityServiceInterface.php index 29c4df7ea..14b266a97 100644 --- a/src/Security/Service/SecurityServiceInterface.php +++ b/src/Security/Service/SecurityServiceInterface.php @@ -16,12 +16,10 @@ namespace Pimcore\Bundle\StudioBackendBundle\Security\Service; -use Pimcore\Bundle\StudioBackendBundle\Authorization\Schema\Credentials; use Pimcore\Bundle\StudioBackendBundle\Exception\AccessDeniedException; -use Pimcore\Bundle\StudioBackendBundle\Exception\NotAuthorizedException; use Pimcore\Model\Element\ElementInterface; use Pimcore\Model\UserInterface; -use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; +use Symfony\Component\Security\Core\Exception\UserNotFoundException; /** * @internal @@ -29,14 +27,7 @@ interface SecurityServiceInterface { /** - * @throws AccessDeniedException - */ - public function authenticateUser(Credentials $credentials): PasswordAuthenticatedUserInterface; - - public function checkAuthToken(string $token): bool; - - /** - * @throws NotAuthorizedException + * @throws UserNotFoundException */ public function getCurrentUser(): UserInterface; diff --git a/src/Security/Voter/AuthorizationVoter.php b/src/Security/Voter/AuthorizationVoter.php index 9d38c196b..f9c434348 100644 --- a/src/Security/Voter/AuthorizationVoter.php +++ b/src/Security/Voter/AuthorizationVoter.php @@ -16,10 +16,8 @@ namespace Pimcore\Bundle\StudioBackendBundle\Security\Voter; -use Pimcore\Bundle\StudioBackendBundle\Authorization\Service\TokenServiceInterface; use Pimcore\Bundle\StudioBackendBundle\Exception\NoRequestException; use Pimcore\Bundle\StudioBackendBundle\Exception\NotAuthorizedException; -use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; @@ -30,13 +28,6 @@ final class AuthorizationVoter extends Voter { private const SUPPORTED_ATTRIBUTE = 'STUDIO_API'; - public function __construct( - private readonly TokenServiceInterface $tokenService, - private readonly SecurityServiceInterface $securityService - - ) { - } - /** * @inheritDoc */ @@ -50,16 +41,10 @@ protected function supports(string $attribute, mixed $subject): bool */ protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { - if($attribute !== self::SUPPORTED_ATTRIBUTE) { + if ($attribute !== self::SUPPORTED_ATTRIBUTE) { return false; } - $authToken = $this->tokenService->getCurrentToken(); - - if(!$this->securityService->checkAuthToken($authToken)) { - throw new NotAuthorizedException(); - } - return true; } } diff --git a/src/Security/Voter/PublicAuthorizationVoter.php b/src/Security/Voter/PublicAuthorizationVoter.php index b8f9f24c3..84b9dddc5 100644 --- a/src/Security/Voter/PublicAuthorizationVoter.php +++ b/src/Security/Voter/PublicAuthorizationVoter.php @@ -42,7 +42,6 @@ final class PublicAuthorizationVoter extends Voter public function __construct( private readonly RequestStack $requestStack, - private readonly SecurityServiceInterface $securityService ) { } @@ -60,16 +59,6 @@ protected function voteOnAttribute(string $attribute, mixed $subject, TokenInter $request = $this->getCurrentRequest($this->requestStack); $subjectName = $this->getSubjectName($subject); - try { - $authToken = $this->getAuthToken($request); - } catch (NotAuthorizedException) { - return $this->voteOnRequest($request, $subjectName); - } - - if ($this->securityService->checkAuthToken($authToken)) { - return true; - } - return $this->voteOnRequest($request, $subjectName); } diff --git a/src/Setting/Controller/GetController.php b/src/Setting/Controller/GetController.php index 60c39442f..c6fb757e4 100644 --- a/src/Setting/Controller/GetController.php +++ b/src/Setting/Controller/GetController.php @@ -47,7 +47,6 @@ public function __construct( operationId: 'getSystemSettings', description: 'Get system settings', summary: 'Get system settings', - security: self::SECURITY_SCHEME, tags: [Tags::Settings->name] )] #[SuccessResponse( diff --git a/src/Translation/Controller/TranslationController.php b/src/Translation/Controller/TranslationController.php index 6d0d583d8..0cc5429fd 100644 --- a/src/Translation/Controller/TranslationController.php +++ b/src/Translation/Controller/TranslationController.php @@ -56,7 +56,6 @@ public function __construct( operationId: 'getTranslations', description: 'Get translations for given keys and locale', summary: 'Get translations', - security: self::SECURITY_SCHEME, tags: [Tags::Translation->name] )] #[TranslationRequestBody] diff --git a/src/User/Controller/CurrentUserController.php b/src/User/Controller/CurrentUserController.php new file mode 100644 index 000000000..88c1cd75f --- /dev/null +++ b/src/User/Controller/CurrentUserController.php @@ -0,0 +1,58 @@ +value] + )] + #[SuccessResponse( + description: 'Current user informations.', + content: new JsonContent(ref: UserInformation::class) + )] + #[DefaultResponses([ + HttpResponseCodes::UNAUTHORIZED, + ])] + public function login(#[CurrentUser] User $user): JsonResponse + { + return $this->jsonResponse([ + 'username' => $user->getUserIdentifier(), + 'roles' => $user->getRoles(), + ]); + } +} diff --git a/src/Version/Controller/Asset/DownloadController.php b/src/Version/Controller/Asset/DownloadController.php index 1766ea89c..76a1250b6 100644 --- a/src/Version/Controller/Asset/DownloadController.php +++ b/src/Version/Controller/Asset/DownloadController.php @@ -58,7 +58,6 @@ public function __construct( operationId: 'downloadAssetVersionById', description: 'Download asset version based on the version ID', summary: 'Download asset version by ID', - security: self::SECURITY_SCHEME, tags: [Tags::Versions->name] )] #[IdParameter(type: 'version')] diff --git a/src/Version/Controller/Asset/ImageStreamController.php b/src/Version/Controller/Asset/ImageStreamController.php index af2b369e7..2299d7fa5 100644 --- a/src/Version/Controller/Asset/ImageStreamController.php +++ b/src/Version/Controller/Asset/ImageStreamController.php @@ -59,7 +59,6 @@ public function __construct( operationId: 'streamImageVersionById', description: 'Get image version stream based on the version ID', summary: 'Get image version stream by ID', - security: self::SECURITY_SCHEME, tags: [Tags::Versions->name] )] #[IdParameter(type: 'version')] diff --git a/src/Version/Controller/DeleteController.php b/src/Version/Controller/DeleteController.php index 34d13b25c..ccb0bd9e9 100644 --- a/src/Version/Controller/DeleteController.php +++ b/src/Version/Controller/DeleteController.php @@ -57,7 +57,6 @@ public function __construct( operationId: 'deleteVersion', description: 'Delete version based on the version ID', summary: 'Delete version', - security: self::SECURITY_SCHEME, tags: [Tags::Versions->name] )] #[IdParameter(type: 'version')] diff --git a/src/Version/Controller/Element/CleanupController.php b/src/Version/Controller/Element/CleanupController.php index 6c638f009..34b4e991a 100644 --- a/src/Version/Controller/Element/CleanupController.php +++ b/src/Version/Controller/Element/CleanupController.php @@ -62,7 +62,6 @@ public function __construct( operationId: 'cleanupVersion', description: 'Cleanup versions based on the provided parameters', summary: 'Cleanup versions', - security: self::SECURITY_SCHEME, tags: [Tags::Versions->name] )] #[ElementTypeParameter] diff --git a/src/Version/Controller/Element/CollectionController.php b/src/Version/Controller/Element/CollectionController.php index 0dc953aa7..7c30c7668 100644 --- a/src/Version/Controller/Element/CollectionController.php +++ b/src/Version/Controller/Element/CollectionController.php @@ -66,7 +66,6 @@ public function __construct( operationId: 'getVersions', description: 'Get paginated versions', summary: 'Get all versions of element', - security: self::SECURITY_SCHEME, tags: [Tags::Versions->name] )] #[ElementTypeParameter] diff --git a/src/Version/Controller/GetController.php b/src/Version/Controller/GetController.php index 5c706e117..1c3dc1cd7 100644 --- a/src/Version/Controller/GetController.php +++ b/src/Version/Controller/GetController.php @@ -57,7 +57,6 @@ public function __construct( operationId: 'getVersionById', description: 'Get version based on the version ID', summary: 'Get version by ID', - security: self::SECURITY_SCHEME, tags: [Tags::Versions->name] )] #[IdParameter(type: 'version')] diff --git a/src/Version/Controller/PublishController.php b/src/Version/Controller/PublishController.php index 1375a4a35..5c11fc2c6 100644 --- a/src/Version/Controller/PublishController.php +++ b/src/Version/Controller/PublishController.php @@ -62,7 +62,6 @@ public function __construct( operationId: 'publishVersion', description: 'Publish element version data based on the version ID', summary: 'Publish version data', - security: self::SECURITY_SCHEME, tags: [Tags::Versions->name] )] #[IdParameter(type: 'version')] diff --git a/src/Workflow/Controller/DetailsCollectionController.php b/src/Workflow/Controller/DetailsCollectionController.php index 6670dc7e0..ecd53d455 100644 --- a/src/Workflow/Controller/DetailsCollectionController.php +++ b/src/Workflow/Controller/DetailsCollectionController.php @@ -57,7 +57,6 @@ public function __construct( operationId: 'getWorkflowsDetails', description: 'Get details of the element workflows', summary: 'Get all workflow details of an element', - security: self::SECURITY_SCHEME, tags: [Tags::Workflows->name] )] #[IdParameter('ID of the element', 'element')] diff --git a/src/Workflow/Controller/SubmitActionController.php b/src/Workflow/Controller/SubmitActionController.php index fd0a8ec91..2b02395f7 100644 --- a/src/Workflow/Controller/SubmitActionController.php +++ b/src/Workflow/Controller/SubmitActionController.php @@ -56,7 +56,6 @@ public function __construct( operationId: 'submitWorkflowAction', description: 'Submit action based on the workflow name, action name and action type', summary: 'Submit workflow action', - security: self::SECURITY_SCHEME, tags: [Tags::Workflows->name] )] #[WorkflowActionRequestBody] diff --git a/tests/Unit/Dto/Token/CreateTest.php b/tests/Unit/Dto/Token/CreateTest.php deleted file mode 100644 index 53c0e211c..000000000 --- a/tests/Unit/Dto/Token/CreateTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertSame('token', $create->getUsername()); - $this->assertSame('test', $create->getPassword()); - } -} diff --git a/tests/Unit/Dto/Token/InfoTest.php b/tests/Unit/Dto/Token/InfoTest.php deleted file mode 100644 index 2988f8897..000000000 --- a/tests/Unit/Dto/Token/InfoTest.php +++ /dev/null @@ -1,30 +0,0 @@ -assertSame('token', $info->getToken()); - $this->assertSame('test', $info->getUsername()); - } -} diff --git a/tests/Unit/Service/Security/SecurityServiceTest.php b/tests/Unit/Service/Security/SecurityServiceTest.php index 79d1109a0..d9da250a7 100644 --- a/tests/Unit/Service/Security/SecurityServiceTest.php +++ b/tests/Unit/Service/Security/SecurityServiceTest.php @@ -19,20 +19,12 @@ use Codeception\Test\Unit; use Exception; use Pimcore\Bundle\GenericDataIndexBundle\Service\Permission\ElementPermissionServiceInterface; -use Pimcore\Bundle\StaticResolverBundle\Models\Tool\TmpStoreResolverInterface; -use Pimcore\Bundle\StaticResolverBundle\Models\User\UserResolverInterface; -use Pimcore\Bundle\StudioBackendBundle\Authorization\Schema\Credentials; -use Pimcore\Bundle\StudioBackendBundle\Authorization\Service\TokenServiceInterface; +use Pimcore\Bundle\StaticResolverBundle\Lib\Tools\Authentication\AuthenticationResolverInterface; use Pimcore\Bundle\StudioBackendBundle\Exception\AccessDeniedException; -use Pimcore\Bundle\StudioBackendBundle\Exception\NotAuthorizedException; use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityService; use Pimcore\Bundle\StudioBackendBundle\Security\Service\SecurityServiceInterface; use Pimcore\Model\Asset; -use Pimcore\Model\Tool\TmpStore; use Pimcore\Model\User as PimcoreUser; -use Pimcore\Security\User\User; -use Pimcore\Security\User\UserProvider; -use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\Security\Core\Exception\UserNotFoundException; final class SecurityServiceTest extends Unit @@ -40,80 +32,25 @@ final class SecurityServiceTest extends Unit /** * @throws Exception */ - public function testSecurityService(): void - { - $securityService = $this->mockSecurityService(); - $user = $securityService->authenticateUser(new Credentials('test', 'test')); - - $this->assertInstanceOf(User::class, $user); - $this->assertSame('test', $user->getPassword()); - } - - /** - * @throws Exception - */ - public function testInvalidPassword(): void - { - $securityService = $this->mockSecurityService(false); - - $this->expectException(AccessDeniedException::class); - $this->expectExceptionMessage('Bad credentials'); - $securityService->authenticateUser(new Credentials('test', 'test')); - } - - /** - * @throws Exception - */ - public function testUserNotFound(): void - { - $securityService = $this->mockSecurityService(false, false); - - $this->expectException(AccessDeniedException::class); - $this->expectExceptionMessage('Bad credentials'); - $securityService->authenticateUser(new Credentials('test', 'test')); - } - - /** - * @throws Exception - */ - public function testTokenAllowedTrue(): void + public function testGetCurrentUserWithOutValidUser(): void { $securityService = $this->mockSecurityService(false, false); - $this->assertTrue($securityService->checkAuthToken('test')); - } - - /** - * @throws Exception - */ - public function testTokenAllowedFalse(): void - { - $securityService = $this->mockSecurityService(false, false, false); - - $this->assertFalse($securityService->checkAuthToken('test')); - } - - /** - * @throws Exception - */ - public function testGetCurrentUserWithInvalidToken(): void - { - $securityService = $this->mockSecurityService(false, false, false); - - $this->expectException(NotAuthorizedException::class); + $this->expectException(UserNotFoundException::class); $securityService->getCurrentUser(); } /** * @throws Exception */ - public function testGetCurrentUserWithValidToken(): void + public function testGetCurrentUserWithValidUser(): void { - $securityService = $this->mockSecurityService(); + $securityService = $this->mockSecurityService(true, false); $user = $securityService->getCurrentUser(); $this->assertInstanceOf(PimcoreUser::class, $user); + $this->assertSame('test', $user->getUsername()); } /** @@ -122,8 +59,6 @@ public function testGetCurrentUserWithValidToken(): void public function testHasElementPermission(): void { $securityService = $this->mockSecurityService( - true, - true, true, false ); @@ -141,98 +76,29 @@ public function testHasElementPermission(): void * @throws Exception */ private function mockSecurityService( - $validPassword = true, bool $withUser = true, - bool $withTmpStore = true, - bool $hasPermission = true + bool $hasPermission = true, ): SecurityServiceInterface { return new SecurityService( $this->mockElementPermissionService($hasPermission), - $withUser ? $this->mockUserProviderWithUser() : $this->mockUserProviderWithOutUser(), - $this->mockUserResolverService(), - $this->mockPasswordHasher($validPassword), - $this->mockTmpStoreResolver($withTmpStore), - $this->mockTokenService(), + $this->mockAuthenticationResolver($withUser) ); } - /** - * @throws Exception - */ - private function mockUserProviderWithUser(): UserProvider - { - return $this->makeEmpty(UserProvider::class, [ - 'loadUserByIdentifier' => function () { - return $this->makeEmpty(User::class, [ - 'getPassword' => 'test', - ]); - }, - ]); - } - - /** - * @throws Exception - */ - private function mockUserProviderWithOutUser(): UserProvider - { - return $this->makeEmpty(UserProvider::class, [ - 'loadUserByIdentifier' => fn () => throw new UserNotFoundException('User not found'), - ]); - } - - /** - * @throws Exception - */ - private function mockUserResolverService(): UserResolverInterface - { - return $this->makeEmpty(UserResolverInterface::class, [ - 'getByName' => new PimcoreUser(), - ]); - } - - /** - * @throws Exception - */ - private function mockTokenService(): TokenServiceInterface - { - return $this->makeEmpty(TokenServiceInterface::class, [ - 'getCurrentToken' => 'test', - ]); - } - - /** - * @throws Exception - */ - private function mockPasswordHasher($validPassword = true): UserPasswordHasherInterface - { - return $this->makeEmpty(UserPasswordHasherInterface::class, [ - 'isPasswordValid' => $validPassword, - ]); - } - - /** - * @throws Exception - */ - private function mockTmpStoreResolver($withTmpStore = true): TmpStoreResolverInterface + private function mockElementPermissionService(bool $hasPermission): ElementPermissionServiceInterface { - return $this->makeEmpty(TmpStoreResolverInterface::class, [ - 'get' => $withTmpStore ? $this->mockTmpStore() : null, + return $this->makeEmpty(ElementPermissionServiceInterface::class, [ + 'isAllowed' => $hasPermission, ]); } - private function mockTmpStore(): TmpStore + private function mockAuthenticationResolver(bool $withUser): AuthenticationResolverInterface { - $tmpStore = new TmpStore(); - $tmpStore->setId('test'); - $tmpStore->setData(['username' => 'test']); - - return $tmpStore; - } + $user = new PimcoreUser(); + $user->setUsername('test'); - private function mockElementPermissionService(bool $hasPermission): ElementPermissionServiceInterface - { - return $this->makeEmpty(ElementPermissionServiceInterface::class, [ - 'isAllowed' => $hasPermission, + return $this->makeEmpty(AuthenticationResolverInterface::class, [ + 'authenticateSession' => $withUser ? $user : null, ]); } }