vendor/shopware/core/Framework/Api/Controller/UserController.php line 81

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Api\Controller;
  3. use League\OAuth2\Server\Exception\OAuthServerException;
  4. use Shopware\Core\Framework\Api\Acl\Role\AclRoleDefinition;
  5. use Shopware\Core\Framework\Api\Context\AdminApiSource;
  6. use Shopware\Core\Framework\Api\Context\Exception\InvalidContextSourceException;
  7. use Shopware\Core\Framework\Api\Controller\Exception\ExpectedUserHttpException;
  8. use Shopware\Core\Framework\Api\Controller\Exception\PermissionDeniedException;
  9. use Shopware\Core\Framework\Api\Exception\MissingPrivilegeException;
  10. use Shopware\Core\Framework\Api\OAuth\Scope\UserVerifiedScope;
  11. use Shopware\Core\Framework\Api\Response\ResponseFactoryInterface;
  12. use Shopware\Core\Framework\Context;
  13. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  15. use Shopware\Core\Framework\Log\Package;
  16. use Shopware\Core\Framework\Routing\Annotation\Acl;
  17. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  18. use Shopware\Core\Framework\Routing\Annotation\Since;
  19. use Shopware\Core\PlatformRequest;
  20. use Shopware\Core\System\User\UserDefinition;
  21. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  22. use Symfony\Component\HttpFoundation\Request;
  23. use Symfony\Component\HttpFoundation\Response;
  24. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  25. use Symfony\Component\Routing\Annotation\Route;
  26. /**
  27.  * @Route(defaults={"_routeScope"={"api"}})
  28.  */
  29. #[Package('system-settings')]
  30. class UserController extends AbstractController
  31. {
  32.     /**
  33.      * @var EntityRepositoryInterface
  34.      */
  35.     private $userRepository;
  36.     /**
  37.      * @var EntityRepositoryInterface
  38.      */
  39.     private $roleRepository;
  40.     /**
  41.      * @var EntityRepositoryInterface
  42.      */
  43.     private $userRoleRepository;
  44.     /**
  45.      * @var UserDefinition
  46.      */
  47.     private $userDefinition;
  48.     /**
  49.      * @var EntityRepositoryInterface
  50.      */
  51.     private $keyRepository;
  52.     /**
  53.      * @internal
  54.      */
  55.     public function __construct(
  56.         EntityRepositoryInterface $userRepository,
  57.         EntityRepositoryInterface $userRoleRepository,
  58.         EntityRepositoryInterface $roleRepository,
  59.         EntityRepositoryInterface $keyRepository,
  60.         UserDefinition $userDefinition
  61.     ) {
  62.         $this->userRepository $userRepository;
  63.         $this->roleRepository $roleRepository;
  64.         $this->userDefinition $userDefinition;
  65.         $this->keyRepository $keyRepository;
  66.         $this->userRoleRepository $userRoleRepository;
  67.     }
  68.     /**
  69.      * @Since("6.0.0.0")
  70.      * @Route("/api/_info/me", name="api.info.me", methods={"GET"})
  71.      */
  72.     public function me(Context $contextRequest $requestResponseFactoryInterface $responseFactory): Response
  73.     {
  74.         if (!$context->getSource() instanceof AdminApiSource) {
  75.             throw new InvalidContextSourceException(AdminApiSource::class, \get_class($context->getSource()));
  76.         }
  77.         $userId $context->getSource()->getUserId();
  78.         if (!$userId) {
  79.             throw new ExpectedUserHttpException();
  80.         }
  81.         $criteria = new Criteria([$userId]);
  82.         $criteria->addAssociation('aclRoles');
  83.         $user $this->userRepository->search($criteria$context)->first();
  84.         if (!$user) {
  85.             throw OAuthServerException::invalidCredentials();
  86.         }
  87.         return $responseFactory->createDetailResponse(new Criteria(), $user$this->userDefinition$request$context);
  88.     }
  89.     /**
  90.      * @Since("6.3.3.0")
  91.      * @Route("/api/_info/me", name="api.change.me", defaults={"auth_required"=true, "_acl"={"user_change_me"}}, methods={"PATCH"})
  92.      */
  93.     public function updateMe(Context $contextRequest $requestResponseFactoryInterface $responseFactory): Response
  94.     {
  95.         if (!$context->getSource() instanceof AdminApiSource) {
  96.             throw new InvalidContextSourceException(AdminApiSource::class, \get_class($context->getSource()));
  97.         }
  98.         $userId $context->getSource()->getUserId();
  99.         if (!$userId) {
  100.             throw new ExpectedUserHttpException();
  101.         }
  102.         $allowedChanges = ['firstName''lastName''username''localeId''email''avatarMedia''avatarId''password'];
  103.         if (!empty(array_diff(array_keys($request->request->all()), $allowedChanges))) {
  104.             throw new MissingPrivilegeException(['user:update']);
  105.         }
  106.         return $this->upsertUser($userId$request$context$responseFactory);
  107.     }
  108.     /**
  109.      * @Since("6.0.0.0")
  110.      * @Route("/api/_info/ping", name="api.info.ping", methods={"GET"})
  111.      */
  112.     public function status(Context $context): Response
  113.     {
  114.         if (!$context->getSource() instanceof AdminApiSource) {
  115.             throw new InvalidContextSourceException(AdminApiSource::class, \get_class($context->getSource()));
  116.         }
  117.         $userId $context->getSource()->getUserId();
  118.         if (!$userId) {
  119.             throw new ExpectedUserHttpException();
  120.         }
  121.         $result $this->userRepository->searchIds(new Criteria([$userId]), $context);
  122.         if ($result->getTotal() === 0) {
  123.             throw OAuthServerException::invalidCredentials();
  124.         }
  125.         return new Response(nullResponse::HTTP_NO_CONTENT);
  126.     }
  127.     /**
  128.      * @Since("6.2.3.0")
  129.      * @Route("/api/user/{userId}", name="api.user.delete", defaults={"auth_required"=true, "_acl"={"user:delete"}}, methods={"DELETE"})
  130.      */
  131.     public function deleteUser(string $userIdRequest $requestContext $contextResponseFactoryInterface $factory): Response
  132.     {
  133.         if (!$this->hasScope($requestUserVerifiedScope::IDENTIFIER)) {
  134.             throw new AccessDeniedHttpException(sprintf('This access token does not have the scope "%s" to process this Request'UserVerifiedScope::IDENTIFIER));
  135.         }
  136.         /** @var AdminApiSource $source */
  137.         $source $context->getSource();
  138.         if (
  139.             !$source->isAllowed('user:update')
  140.             && $source->getUserId() !== $userId
  141.         ) {
  142.             throw new PermissionDeniedException();
  143.         }
  144.         $context->scope(Context::SYSTEM_SCOPE, function (Context $context) use ($userId): void {
  145.             $this->userRepository->delete([['id' => $userId]], $context);
  146.         });
  147.         return $factory->createRedirectResponse($this->userRepository->getDefinition(), $userId$request$context);
  148.     }
  149.     /**
  150.      * @Since("6.3.0.0")
  151.      * @Route("/api/user/{userId}/access-keys/{id}", name="api.user_access_keys.delete", defaults={"auth_required"=true, "_acl"={"user_access_key:delete"}}, methods={"DELETE"})
  152.      */
  153.     public function deleteUserAccessKey(string $idRequest $requestContext $contextResponseFactoryInterface $factory): Response
  154.     {
  155.         if (!$this->hasScope($requestUserVerifiedScope::IDENTIFIER)) {
  156.             throw new AccessDeniedHttpException(sprintf('This access token does not have the scope "%s" to process this Request'UserVerifiedScope::IDENTIFIER));
  157.         }
  158.         $context->scope(Context::SYSTEM_SCOPE, function (Context $context) use ($id): void {
  159.             $this->keyRepository->delete([['id' => $id]], $context);
  160.         });
  161.         return $factory->createRedirectResponse($this->keyRepository->getDefinition(), $id$request$context);
  162.     }
  163.     /**
  164.      * @Since("6.2.3.0")
  165.      * @Route("/api/user", name="api.user.create", defaults={"auth_required"=true, "_acl"={"user:create"}}, methods={"POST"})
  166.      */
  167.     public function upsertUser(?string $userIdRequest $requestContext $contextResponseFactoryInterface $factory): Response
  168.     {
  169.         if (!$this->hasScope($requestUserVerifiedScope::IDENTIFIER)) {
  170.             throw new AccessDeniedHttpException(sprintf('This access token does not have the scope "%s" to process this Request'UserVerifiedScope::IDENTIFIER));
  171.         }
  172.         $data $request->request->all();
  173.         /** @var AdminApiSource $source */
  174.         $source $context->getSource();
  175.         if (!isset($data['id'])) {
  176.             $data['id'] = null;
  177.         }
  178.         $data['id'] = $userId ?: $data['id'];
  179.         if (
  180.             !$source->isAllowed('user:update')
  181.             && $source->getUserId() !== $data['id']
  182.         ) {
  183.             throw new PermissionDeniedException();
  184.         }
  185.         $events $context->scope(Context::SYSTEM_SCOPE, function (Context $context) use ($data) {
  186.             return $this->userRepository->upsert([$data], $context);
  187.         });
  188.         $event $events->getEventByEntityName(UserDefinition::ENTITY_NAME);
  189.         $eventIds $event->getIds();
  190.         $entityId array_pop($eventIds);
  191.         return $factory->createRedirectResponse($this->userRepository->getDefinition(), $entityId$request$context);
  192.     }
  193.     /**
  194.      * @Since("6.3.3.0")
  195.      * @Route("/api/user/{userId}", name="api.user.update", defaults={"auth_required"=true, "_acl"={"user:update"}}, methods={"PATCH"})
  196.      */
  197.     public function updateUser(?string $userIdRequest $requestContext $contextResponseFactoryInterface $factory): Response
  198.     {
  199.         return $this->upsertUser($userId$request$context$factory);
  200.     }
  201.     /**
  202.      * @Since("6.3.2.0")
  203.      * @Route("/api/acl-role", name="api.acl_role.create", defaults={"auth_required"=true, "_acl"={"acl_role:create"}}, methods={"POST"})
  204.      */
  205.     public function upsertRole(?string $roleIdRequest $requestContext $contextResponseFactoryInterface $factory): Response
  206.     {
  207.         if (!$this->hasScope($requestUserVerifiedScope::IDENTIFIER)) {
  208.             throw new AccessDeniedHttpException(sprintf('This access token does not have the scope "%s" to process this Request'UserVerifiedScope::IDENTIFIER));
  209.         }
  210.         $data $request->request->all();
  211.         if (!isset($data['id'])) {
  212.             $data['id'] = $roleId ?? null;
  213.         }
  214.         $events $context->scope(Context::SYSTEM_SCOPE, function (Context $context) use ($data) {
  215.             return $this->roleRepository->upsert([$data], $context);
  216.         });
  217.         $event $events->getEventByEntityName(AclRoleDefinition::ENTITY_NAME);
  218.         $eventIds $event->getIds();
  219.         $entityId array_pop($eventIds);
  220.         return $factory->createRedirectResponse($this->roleRepository->getDefinition(), $entityId$request$context);
  221.     }
  222.     /**
  223.      * @Since("6.3.3.0")
  224.      * @Route("/api/acl-role/{roleId}", name="api.acl_role.update", defaults={"auth_required"=true, "_acl"={"acl_role:update"}}, methods={"PATCH"})
  225.      */
  226.     public function updateRole(?string $roleIdRequest $requestContext $contextResponseFactoryInterface $factory): Response
  227.     {
  228.         return $this->upsertRole($roleId$request$context$factory);
  229.     }
  230.     /**
  231.      * @Since("6.3.3.0")
  232.      * @Route("/api/user/{userId}/acl-roles/{roleId}", name="api.user_role.delete", defaults={"auth_required"=true, "_acl"={"acl_user_role:delete"}}, methods={"DELETE"})
  233.      */
  234.     public function deleteUserRole(string $userIdstring $roleIdRequest $requestContext $contextResponseFactoryInterface $factory): Response
  235.     {
  236.         if (!$this->hasScope($requestUserVerifiedScope::IDENTIFIER)) {
  237.             throw new AccessDeniedHttpException(sprintf('This access token does not have the scope "%s" to process this Request'UserVerifiedScope::IDENTIFIER));
  238.         }
  239.         $context->scope(Context::SYSTEM_SCOPE, function (Context $context) use ($roleId$userId): void {
  240.             $this->userRoleRepository->delete([['userId' => $userId'aclRoleId' => $roleId]], $context);
  241.         });
  242.         return $factory->createRedirectResponse($this->userRoleRepository->getDefinition(), $roleId$request$context);
  243.     }
  244.     /**
  245.      * @Since("6.3.2.0")
  246.      * @Route("/api/acl-role/{roleId}", name="api.acl_role.delete", defaults={"auth_required"=true, "_acl"={"acl_role:delete"}}, methods={"DELETE"})
  247.      */
  248.     public function deleteRole(string $roleIdRequest $requestContext $contextResponseFactoryInterface $factory): Response
  249.     {
  250.         if (!$this->hasScope($requestUserVerifiedScope::IDENTIFIER)) {
  251.             throw new AccessDeniedHttpException(sprintf('This access token does not have the scope "%s" to process this Request'UserVerifiedScope::IDENTIFIER));
  252.         }
  253.         $context->scope(Context::SYSTEM_SCOPE, function (Context $context) use ($roleId): void {
  254.             $this->roleRepository->delete([['id' => $roleId]], $context);
  255.         });
  256.         return $factory->createRedirectResponse($this->roleRepository->getDefinition(), $roleId$request$context);
  257.     }
  258.     private function hasScope(Request $requeststring $scopeIdentifier): bool
  259.     {
  260.         $scopes array_flip($request->attributes->get(PlatformRequest::ATTRIBUTE_OAUTH_SCOPES));
  261.         return isset($scopes[$scopeIdentifier]);
  262.     }
  263. }