J'utilise FOS User Bundle et je dois utiliser une valeur appelée "maxLoginAttempts" à partir d'un enregistrement d'une autre entité / table que j'utilise pour mes paramètres.

L'entité s'appelle Paramètres. Ceci est mon code actuel et je voudrais changer le numéro 5 pour la valeur de la base de données.

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;

/**
 * @ORM\Entity
 * @ORM\Table(name="`user`")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    protected $id;

    ...

    public function isAccountNonLocked()
    {
        if($this->getLoginAttempts() >= 5) {
            return false;
        } else {
            return true;
        }
     }
 }

J'imagine quelque chose comme:

  $this->em->getRepository('AppBundle:Parameters')
        ->findOneBy(['name' => 'maxLoginAttempts']);

Évidemment, pour le moment, je n'ai pas accès au référentiel. Comme je ne suis pas dans le contrôleur, je ne sais pas comment utiliser ces valeurs depuis l'intérieur d'une fonction d'une entité.

0
Ricard Espinàs Llovet 28 nov. 2017 à 12:37

3 réponses

Meilleure réponse

Enfin, la solution était de remplacer UserChecker en utilisant une autre fonction avec la même fonctionnalité.

<?php

namespace AppBundle\Checker;

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Exception\CredentialsExpiredException;
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\Exception\LockedException;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Security\Core\User\UserChecker as BaseUserChecker;
use Symfony\Component\Security\Core\User\UserInterface;

class UserChecker extends BaseUserChecker
{
    private $em;

    public function __construct( EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public function checkPreAuth(UserInterface $user)
    {
        //parent::checkPreAuth($user);
        $maxMinutesLocked = $this->em->getRepository('AppBundle:Parameters')->findOneBy(array('name' => 'maxTimeLocked'))->getValue();

        if (!$user instanceof AdvancedUserInterface) {
            return;
        }

        //So I just added a new function called isAccountLocked() to the User Entity that's a copy from isAccountNonLocked() but I could add a paramater
        if ($user->isAccountLocked($maxMinutesLocked)) {
            $ex = new LockedException('User account is locked.');
            $ex->setUser($user);
            throw $ex;
        }

        if (!$user->isEnabled()) {
            ...
        }
        if (!$user->isAccountNonExpired()) {
            ...
        }
    }

    public function checkPostAuth(UserInterface $user)
    {
         ...
    }
}
0
Ricard Espinàs Llovet 4 déc. 2017 à 14:48

Je peux penser à une manière plus appropriée, à mon humble avis, de résoudre ceci:

L'entité User aura une propriété supplémentaire $ loginAttempts qui sera incrémentée par la méthode incrementLoginAttempts () chaque fois que la connexion échoue. Il sera initialisé à 0 via l'ORM et une méthode isLocked () nous dira si nous avons atteint 5 tentatives.

<?php
// AppBundle/Entity/User.php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;

/**
 * @ORM\Entity
 * @ORM\Table(name="`user`")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    protected $id;
    public function __construct()
    {
        parent::__construct();
    }

    /** 
     * @ORM\Column(type="integer",options={"default"=0}) 
     */
    private $loginAttempts;

    ...
    public function getLoginAttempts()
    {
       return $this->loginAttemps;
    }
    public function incrementLoginAttempts()
    {
       if($this->loginAttempts<5){
             $this->loginAttempts++;
       }
       return $this;
    }
    public function isLocked()
    {
        return ($this->loginAttempts == 5)
    }
    public function resetLoginAttempts()
    {
        $this->loginAttempts =0;
        return $this;
    }

Ensuite, créez un EventSubscriber pour l'événement SecuritySubscriber et déclenchez un incrementLoginAttempts () chaque fois que la connexion échoue; vérifier en même temps si l'utilisateur a déjà été verrouillé ou pas encore

    <?php
// src/AppBundle/EventSubscriber/SecuritySubscriber.php
namespace AppBundle\EventSubscriber;

use AppBundle\Entity\User;  

class SecuritySubscriber implements EventSubscriberInterface  
{

    private $entityManager;
    private $tokenStorage;
    private $authenticationUtils;

    public function __construct(EntityManager $entityManager, TokenStorageInterface $tokenStorage, AuthenticationUtils $authenticationUtils)
    {
        $this->entityManager = $entityManager;
        $this->tokenStorage = $tokenStorage;
        $this->authenticationUtils = $authenticationUtils;
    }

    public static function getSubscribedEvents()
    {
        return array(
            AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthenticationFailure',
        );
    }

    public function onAuthenticationFailure( AuthenticationFailureEvent $event )
    {
        $existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['username' => $username]);

        if ($existingUser) {
            $existingUser->incrementLoginAttempts();
            $this->entityManager->persist($existingUser);
            $this->entityManager->flush();
            if($existingUser->isLocked()){
                // Do your logic here
                // Do not forget to un  $existingUser->resetLoginAttempts() when necessary
            }
        }
    }
}

N'oubliez pas d'enregistrer l'abonné en tant que service

# app/config/services.yml
services:  
    app.security.authentication_event_listener:
        class: AppBundle\EventSubscriber\SecuritySubscriber
        arguments:
            - "@doctrine.orm.entity_manager"
            - "@security.token_storage"
            - "@security.authentication_utils"

P.S: Le code n'a pas été testé.

0
medunes 1 déc. 2017 à 00:41

Vous avez probablement mal compris le concept d'une Entité:

La classe - souvent appelée "entité", c'est-à-dire une classe de base qui contient des données - est simple et permet de répondre aux exigences métier de besoin de produits dans votre application. Cette classe ne peut pas encore être conservée dans une base de données - c'est juste une simple classe PHP

Cela signifie que le Entity n'est que le concept , donc vous ne pouvez pas avoir accès à l'autre Entities ou au EntityManager depuis l'intérieur de la classe.

Si vous souhaitez utiliser quelque chose comme la fonction membre que vous avez décrite. vous devez passer maxLoginAttempts comme argument:

public function isAccountNonLocked($maxLoginAttempts)
{
    if($this->getLoginAttempts() >= maxLoginAttempts) {
        return false;
    } else {
        return true;
    }
}

Dans ce cas, vous devez d'abord obtenir la valeur de maxLoginAttempts à partir de votre configuration Entity, puis l'utiliser sur l ' objet du User que vous souhaitez vérifier:

$maxLoginAttempts = $this->em->getRepository('AppBundle:Parameters')
        ->findOneBy(['name' => 'maxLoginAttempts']);
$user = $this->em->getRepository('AppBundle:User')->find($userId);
if ($user->isAccountNonLocked($maxLoginAttempts)) {
   // do something
}
0
Azuloo 28 nov. 2017 à 11:52
47528187