Problema con security voter, esperaba un objeto

Buenas tardes.

Uso un security voter para que solo el autor o el usuario con permisos pueda editar los comentarios o las planificaciones. Esto funciona correctamente pero con un botón para crear la planificación y otro para editarla en la misma página, con un aviso flash que te decía que ya estaba planificado ó editado en caso de error.

Visto que no era la manera correcta quiero que muestre el botón de planificar/editar según convenga.

He modificado la consulta para unir los datos de varias tablas, pero en la plantilla twig al llegar al security voter me da un error de que esperaba un objeto y le estoy pasando un array.

¿Como puedo solucionarlo?

use Doctrine\ORM\EntityRepository;
 
class CalendarRepository extends EntityRepository
{
  public function findCommentPlanned($task)
  {
    $conn= $this->getEntityManager()->getConnection();
    $sql = 'SELECT *
              FROM comment AS c
         LEFT JOIN task AS t ON ( t.id = c.task_id )
         LEFT JOIN calendar AS o ON ( o.comment_id = c.id )
         LEFT JOIN fos_user AS u ON ( u.id = c.user_id )
             WHERE c.task_id = :task
          ORDER BY c.publishedAt DESC';
 
    $stmt = $conn->prepare($sql);
    $stmt->bindValue('task', $task);
    $stmt->execute();
    return $stmt;
  }
 
}
<div class="comments-table widget-content">
      <table id="contacts" class="table table-striped table-bordered table-hover">
        <thead>
          <tr>
            <th class="col-md-1">{{ 'table.task_user'|trans({}, 'messages') }}</th>
            <th class="col-md-7">{{ 'table.task_comments'|trans({}, 'messages') }}</th>
            <th class="col-md-1">{{ 'table.task_edited'|trans({}, 'messages') }}</th>
            <th class="col-md-2">{{ 'table.task_planning'|trans({}, 'messages') }}</th>
          </tr>
        </thead>
        <tbody>
          <tr>
          {% for comments in comment %}
              <td>
                {{ comments.username }}<br>
                {{ comments.publishedAt|date('d-m-Y H:i') }}<br>
                  {% if is_granted('MODIFY', comments) %}
                    <a href="{{ path('editComment', { 'id':comments.id, 'task': task.id })  }}" class="btn btn-xs btn-success">
                     <i class="glyphicon glyphicon-edit"></i>
                   </a>
                   <a href="{{ path('deleteComment', { 'id':comments.id, 'task': task.id }) }}" class="btn btn-xs btn-danger">
                     <i class="glyphicon glyphicon-remove"></i>
                   </a>
                 {% if comments.start is not null  %}
                 <a href="{{ path('editPlanningComment', { 'id':comments.id, 'task': task.id }) }}" class="btn btn-xs btn-primary">
                  <i class="glyphicon glyphicon-calendar"></i>
                 </a>
                 {% else %}
                 <a href="{{ path('planningComment', { 'id':comments.id, 'task': task.id }) }}" class="btn btn-xs btn-primary">
                  <i class="glyphicon glyphicon-calendar"></i>
                 </a>
                 {% endif %}
              </td>
              {% endif %}
              <td>
                 {{ comments.content|nl2br }}
              </td>
              <td>
                {% if comments.editedAt is not null %}
                  {% include 'includes/editedComment.html.twig' %}
                {% endif %}
              </td>
              <td>
                 {% if comments.start is not null %}
                   {{ comments.username }}<br>
                   {{ comments.start }}<br>
                   {{ comments.end }}
                 {% endif %}
              </td>
          </tr>
          {% endfor %}
        </tbody>
      </table>
    </div>

Gracias, un saludo.

Respuestas

#1

Para poder ayudarte necesitaríamos ver la línea de código que está dando error dentro del voter. Si no, no sabemos exactamente a qué objeto se está refiriendo.

#2

Pego el contenido del voter

namespace AppBundle\Security;
 
use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
 
class CommentVoter extends AbstractVoter
{
  const ATTRIBUTE_MODIFY = 'MODIFY';
 
  private $container;
 
  public function __construct(Container $container)
  {
    $this->container = $container;
  }
 
  protected function getSupportedClasses()
  {
    return array('AppBundle\Entity\Comment');
  }
 
  protected function getSupportedAttributes()
  {
    return array(self::ATTRIBUTE_MODIFY);
  }
 
  protected function isGranted($attribute, $object, $user = null)
  {
    if(!is_object($user)){
      return false;
    }
 
    if($object->getUser() == $user->getUsername()){
      return true;
    }
 
    $authChecker = $this->container->get('security.authorization_checker');
 
    if($authChecker->isGranted('ROLE_ADMIN')){
      return true;
    }
 
    return false;
  }
}

Se que no es buena practica inyectar el contenedor de dependencias, pero me daba un error de seguridad porque se formaba un bucle y buscando por internet he visto que bastante gente lo inyecta para este caso en concreto.

#3

Creo que ya se cuál puede ser el error. Dentro del método isGranted() haces esta comparación:

if($object->getUser() == $user->getUsername()){
    return true;
}

La variable $object en este caso hace referencia a un comentario. El problema es que en la plantilla Twig llamas al voter de esta manera:

{% for comments in comment %}
    ...
    {% if is_granted('MODIFY', comments) %}
    ...
{% endfor %}

Si el nombre de las variables es correcto, entonces comments es un array de objetos comment. Por lo tanto, el código Twig debería ser:

{% for comment in comments %}
    ...
    {% if is_granted('MODIFY', comment) %}
    ...
{% endfor %}
#4

Ese era el problema, muchas gracias y felices fiestas.