Почему Symfony4 предоставляет анонимному пользователю этот запрос ajax? - PullRequest
0 голосов
/ 06 ноября 2019

Я использую Datatables.net в своем приложении Symfony4. Он отправляет ajax-запрос с кучей переменных POST в контроллер: UserController-> searchApi (). Вызываемый URL-адрес https://127.0.0.1:8000/users-search. Однако запрашиваемая страница перенаправляется на страницу / login, потому что пользователь Symfony является анонимным.

Итак, для целей отладки я отключил перенаправление и открыл целевую страницу через Firefox devTools. Затем я могу получить доступ к панели инструментов отладки, предоставив полезную информацию:

  • Тест 1: меня перенаправляют, когда я открываю запрос POST на https://127.0.0.1:8000/users-search, отправленный Datatables.
  • Тест 2: я не перенаправляется, и контроллер правильно вызывается при достижении страницы с помощью GET
  • Тест 3: я не перенаправляется, и контроллер правильно вызывается, когда я захожу на страницу по POST при использовании формы на странице, вызываемой через GET
  • URL-адрес строго идентичен в этих 3 случаях использования
  • «Основной» брандмауэр используется на перенаправленной странице, согласно панели отладки Symfony. Пользователь по-прежнему анонимный.
  • Файл cookie PHPSESSID присутствует и точен на перенаправленной странице, а также на странице, к которой осуществляется доступ.

Похоже, что значения POSTed от Datatables являются единственным различием между тестами 1 и 3. Поэтому я использовал Firefox devTools для редактирования запроса, отправленного со страницы источника (с Datatables на нем), и удалилпараметры, а затем повторно отправил запрос. Ну, это ломает сервер Symfony без предупреждения, и я должен перезапустить его. Странно, но я думаю, что не связано.

Вот security.yml:

security:
  encoders:
    App\Entity\User:
      algorithm: auto

  role_hierarchy:
    ROLE_ADMIN:       ROLE_USER
    ROLE_SUPER_ADMIN: ROLE_ADMIN

  # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
  providers:
    # used to reload user from session & other features (e.g. switch_user)
    app_user_provider:
      entity:
        class: App\Entity\User
        property: email

  firewalls:
    dev:
      pattern: ^/(_(profiler|wdt)|css|images|js)/
      security: false
    main:
      pattern: ^/
      form_login:
        csrf_token_generator: security.csrf.token_manager

      logout:       true
      anonymous:    true
      guard:
        authenticators:
          - App\Security\LoginFormAuthenticator

      remember_me:
        secret:   '%kernel.secret%'
        lifetime: 3024000 # 5 weeks
        path:     /

  # Easy way to control access for large sections of your site
  # Note: Only the *first* access control that matches will be used
  access_control:
    - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/forgottenPassword$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/resetPassword/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/, role: IS_AUTHENTICATED_REMEMBERED }

Соответствующие части контроллеров


    /**
     * @Route("/users", name="app_user_search")
     */
    public function search(UserService $userService)
    {
        $this->denyAccessUnlessGranted('searchUsers');

        return $this->render('user/search.html.twig', []);
    }


    /**
     * @Route("/users-search", name="app_user_search_api")
     */
    public function searchApi(Request $request, EntityManagerInterface $em)
    {

        echo '<form method="post"><input type="submit"></form>';
        die();
    }

И запрос от Firefox Devtool

Request URL:https://127.0.0.1:8000/users-search
Request Method:POST
Remote Address:127.0.0.1:8000
Status Code:
200
Version:HTTP/2.0
Referrer Policy:no-referrer-when-downgrade

С параметрами (довольно случайно)

draw=1&columns%5B0%5D%5Bdata%5D=0&columns%5B0%5D%5Bname%5D=&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=true&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=1&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=true&columns%5B1%5D%5Borderable%5D=true&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B1%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B2%5D%5Bdata%5D=2&columns%5B2%5D%5Bname%5D=&columns%5B2%5D%5Bsearchable%5D=true&columns%5B2%5D%5Borderable%5D=true&columns%5B2%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B2%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B3%5D%5Bdata%5D=3&columns%5B3%5D%5Bname%5D=&columns%5B3%5D%5Bsearchable%5D=true&columns%5B3%5D%5Borderable%5D=false&columns%5B3%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B3%5D%5Bsearch%5D%5Bregex%5D=false&start=0&length=25&search%5Bvalue%5D=&search%5Bregex%5D=false

Редактировать: избиратель

<?php

namespace App\Security\Voter;

use App\Entity\User;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;

class UserVoter extends Voter
{
    private $security;

    public function __construct(Security $security)
    {
        $this->security = $security;
    }

    protected function supports($attribute, $subject)
    {
        // By entity
        if($subject instanceof \App\Entity\User)
            return in_array($attribute, [
                'read',
                'update',
                'delete',
            ]);

        // Not entity-related
        return in_array($attribute, [
            'searchUsers',
            'createUser',
        ]);
    }

    /**
     * @param string $attribute
     * @param User $user
     * @param TokenInterface $token
     * @return bool|mixed
     */
    protected function voteOnAttribute($attribute, $user, TokenInterface $token)
    {
        $currentUser = $this->security->getUser();

        // if the user is anonymous, do not grant access
        if (!$currentUser instanceof UserInterface)
            return false;

        switch ($attribute) {
            case 'createUser':
            case 'searchUsers':
                return $this->security->isGranted("ROLE_ADMIN")
                    ;
            case 'read':
                return  $user->getCreatedBy() === $currentUser
                    ||  $user === $currentUser
                    ;
            case 'update':
                return  $this->security->isGranted("ROLE_ADMIN")
                    ||  $user === $currentUser
                    ;
            case 'delete':
                return  $this->security->isGranted("ROLE_ADMIN")
                    &&  $user !== $currentUser
                    ;
        }

        return false;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...