I have an app that manages all of its users inside one entity (USER), implementing the UserInterface
, as recommended.
Linked to it, I got the CUSTOMER and the EMPLOYEE entities: each one storing specific infos about the user.
You may ask why I’m doing like this: an EMPLOYEE can be a CUSTOMER in many WORKPLACES and may work in many WORKPLACES. In order to avoid repetition, the common info got centralized in USER and the attribute Roles
in USER is always blank in db.
The goal here is to load my roles based on specific data stored in another entity (WORKPLACE) when an EMPLOYEE logs in.
My great question may live in the UserRepository
: when symfony calls the repository looking for the user entered on the login form, doctrine delivers it with no roles. I put my roles and return the User
to the Authenticator (the vanilla one, I havent touched it yet). After the end of every requery, Symfony checks for the user but then doctrine loads the ROLE_CLIENT
(in other words – no privileges).
What I already tried and failed
- Make another authenticator supporting the routes that need those specific rules (as seen here https://symfony.com/doc/current/security/guard_authentication.html )
- Use the
UserLoaderInterface
insideUserRepository
(as seen here https://symfony.com/doc/master/security/user_provider.html#using-a-custom-query-to-load-the-user ) - Voters (I didnt see a way to code them to my needs)
The entities
class User implements UserInterface { /** * @ORMId() */ private $cpf; /** * @ORMColumn * */ private $email; /** * @ORMColumn */ private $nome; /** * @var string * * @ORMColumn */ private $telefone; /** * @var DateTime|null * * @ORMColumn */ private $nascimento; /** * @var DateTime|null * * @ORMColumn */ private $ultimoLogin; /** * @var string|null * * @ORMColumn */ private $endereco; /** * @var string|null * * @ORMColumn */ private $cidade; /** * @var string|null * * @ORMColumn */ private $uf; /** * @var int|null * * @ORMColumn */ private $cep; /** * @ORMColumn(name="Roles", type="json") */ private $roles = []; /** * @var string The hashed password * @ORMColumn(name="password", type="string") */ private $password; //vanilla getters and setters
Workplace entity
The booleans store the privileges i want to get
class LocalTrabalho { /** * @var Configuracao * * @ORMManyToOne(targetEntity=Configuracao::class, inversedBy="localTrabalho") * @ORMJoinColumn(name="CNPJ", referencedColumnName="CNPJ", nullable=false) * @ORMId * the company unique code */ private $cnpj; /** * @var Funcionario * * @ORMManyToOne(targetEntity=Funcionario::class, inversedBy="localTrabalho") * @ORMJoinColumn(name="CPF_Funcionario", referencedColumnName="CPF", nullable=false) * @ORMId * the user-employee unique code */ private $cpfFuncionario; /** * @var bool * * @ORMColumn * is this employee is active? */ private $ativo = 1; /** * @var bool * * @ORMColumn */ private $privilegioCaixa = 0; /** * @var bool * * @ORMColumn */ private $privilegioPrestador = 1; /** * @var bool * * @ORMColumn */ private $privilegioRecepcao = 0; /** * @var bool * * @ORMColumn */ private $privilegioAdministracao = 0;
Employee Entity
class Funcionario { /** * @var int * * @ORMColumn * @ORMId * @ORMGeneratedValue(strategy="NONE") */ private $cpf; /** * @var string|null * * @ORMColumn */ private $ctps; /** * @var string|null * * @ORMColumn */ private $foto;
An example USER tuple from db
# CodPFis, email, password, Roles, Nome, Telefone, Nascimento, Ultimo_login, Endereco, Cidade, UF, CEP '89038252099', 'sophiejenniferteresinhaoliveira__sophiejenniferteresinhaoliveira@grupomozue.com.br', '$argon2id$v=19$m=65536,t=4,p=1$WEn7b64I9744kRJICEpaLA$jcYLDvh2bZsZPakMDGsncpbfIZwR6lN0QcgJOOSerK0', NULL, 'João da Silva', '', NULL, NULL, NULL, NULL, NULL, NULL
My last resource is asking here, after some long and wasted work hours, what should I do (or what i did wrong)?
I’ve seen some other threads asking similar questions, but they’re outdated (im using the version 5): Dynamic roles in symfony Symfony 2 – Loading roles from database Symfony2 – Dynamic Role Management How to update roles in security token in Symfony 4 without re-logging in Symfony User Logout After Role Change
PS: Someone tried this approach (https://github.com/symfony/symfony/issues/12025#issuecomment-562004005) and got success in symfony5?
Advertisement
Answer
Well, turns out that the best solution for my case was manipulate the User
Repository. Doctrine uses them to operate the db so I just coded my rules inside and let the framework do the rest.
The algorithm is something like the shown below:
- Find the user using the provided
$id
- If he/she works somewhere, load the
ROLE
constants into theUser
entity - If he/she does not have a job, load the profile as a common user
public function find($id, $lockmode=null, $lockversion = null){ $us = $this->createQueryBuilder('u') ->andWhere('u.cpf = :cpf') ->setParameter('cpf', $id['cpf']) ->getQuery()->getOneOrNullResult(); $lt = $this->_em->createQuery( 'SELECT t FROM AppEntityLocalTrabalho t WHERE t.cpfFuncionario = :cpf' ) ->setParameters(['cpf' => $us->getCpf()]) ->getResult(); if (count($lt) > 0){ $regras = ['ROLE_FUNCIONARIO']; if ($lt[0]->getPrivilegioCaixa()) $regras[] = 'ROLE_CAIXA'; if ($lt[0]->getPrivilegioPrestador()) $regras[] = 'ROLE_PRESTADOR'; if ($lt[0]->getPrivilegioRecepcao()) $regras[] = 'ROLE_RECEPCAO'; if ($lt[0]->getPrivilegioAdministracao()) { $regras = ['ROLE_ADMIN']; } $us->setRoles($regras); } return $us; }