title: User Admin login, give users elevated permissions - Symfony 4
body: <p>Please forgive for asking, i have been working on my websites login/registration pages for some time and finally have both working seemingly flawlessly, my next task is access control so i can do some work on my forum without the need to load my SSH and make database changes.</p>
  
  <p>My Composer.JSON for a list of packages i have already</p>
  
  <pre><code>"require": {
      "php": "^7.1.3",
      "ext-iconv": "*",
      "doctrine/doctrine-migrations-bundle": "^2.0",
      "knplabs/knp-markdown-bundle": "^1.7",
      "sensio/framework-extra-bundle": "^5.1",
      "symfony/asset": "^4.0",
      "symfony/console": "^4.0",
      "symfony/flex": "^1.1",
      "symfony/form": "^4.0",
      "symfony/framework-bundle": "^4.0",
      "symfony/maker-bundle": "^1.1",
      "symfony/orm-pack": "^1.0",
      "symfony/profiler-pack": "^1.0",
      "symfony/security-bundle": "^4.0",
      "symfony/translation": "^4.0",
      "symfony/twig-bundle": "^4.0",
      "symfony/validator": "^4.0",
      "symfony/yaml": "^4.0"
  },
  "require-dev": {
      "sensiolabs/security-checker": "^4.1",
      "symfony/dotenv": "^4.0",
      "symfony/web-server-bundle": "^4.0"
  </code></pre>
  
  <p>The problem i have is that my login form auto outputs <code>'ROLE_USER'</code> when the database returns an empty array, however i am unclear on how i can add an object to the database JSON as i need the <code>'ROLE_ADMIN'</code> string.</p>
  
  <p>I understand that i could add an admin user to memory but i would like to depend solely on the database having something typed out in a doc on my root server dosent feel right to me (for some reason)</p>
  
  <p>I have checked the symfony docs and looked around on here and not found anything that jumps out at me as helpful.</p>
  
  <p>I would like to grant a user (me) <code>'ROLE_ADMIN'</code> while all other users still get the <code>'ROLE_USER'</code> that <code>'getRoles()'</code> returns. my firewall already has hierarchy set up Admin inherits user so i shouldnt have trouble locking myself out of other pages.</p>
  
  <p>i already have a few counts of access control implemented as shown bellow</p>
  
  <pre><code>../config/packages/security.yaml
  ...
  access_control:
       - { path: ^/admin, roles: ROLE_ADMIN }
       - { path: ^/profile, roles: ROLE_USER }
  </code></pre>
  
  <p>And</p>
  
  <pre><code>../templates/forum/index.html.twig
  ...
  {%  if is_granted('ROLE_ADMIN') %}
  &lt;li&gt;&lt;a href="{{ path('page_admin') }}"&gt;Admin&lt;/a&gt;&lt;/li&gt;
  {% endif %}
  ...
  </code></pre>
  
  <p>Here i will drop all my controller/entity info for you guys -*all of my registration/authentication files are generated with maker-bundle </p>
  
  <pre><code>../securityController
  namespace App\Controller;
  
  use App\Form\UserType;
  use App\Entity\User;
  use App\Form\RegistrationFormType;
  use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  use Symfony\Component\Routing\Annotation\Route;
  use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  use Symfony\Component\HttpFoundation\Request;
  use Symfony\Component\HttpFoundation\Response;
  use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  
  class SecurityController extends AbstractController
  {
  ...
  Registration function
  ...
  
  /**
   * @Route("/forum/login", name="app_login")
   */
  public function login(AuthenticationUtils $authenticationUtils): Response
  {
      // get the login error if there is one
      $error = $authenticationUtils-&gt;getLastAuthenticationError();
      // last username entered by the user
      $lastUsername = $authenticationUtils-&gt;getLastUsername();
  
      return $this-&gt;render('security/login.html.twig', ['last_username' =&gt; $lastUsername, 'error' =&gt; $error]);
  }
  </code></pre>
  
  <p>Entity</p>
  
  <pre><code>.../Entity/User
  
  namespace App\Entity;
  
  use Doctrine\ORM\Mapping as ORM;
  use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
  use Symfony\Component\Security\Core\User\UserInterface;
  
  /**
   * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
   * @UniqueEntity(fields={"email"}, message="There is already an account with this email")
   */
  class User implements UserInterface
  {
  /**
   * @ORM\Id()
   * @ORM\GeneratedValue()
   * @ORM\Column(type="integer")
   */
  private $id;
  
  /**
   * @ORM\Column(type="string", length=180, unique=true)
   */
  private $email;
  
  /**
   * @ORM\Column(type="json")
   */
  private $roles = [];
  
  /**
   * @var string The hashed password
   * @ORM\Column(type="string")
   */
  private $password;
  
  public function getId(): ?int
  {
      return $this-&gt;id;
  }
  
  public function getEmail(): ?string
  {
      return $this-&gt;email;
  }
  
  public function setEmail(string $email): self
  {
      $this-&gt;email = $email;
  
      return $this;
  }
  
  /**
   * A visual identifier that represents this user.
   *
   * @see UserInterface
   */
  public function getUsername(): string
  {
      return (string) $this-&gt;email;
  }
  
  /**
   * @see UserInterface
   */
  public function getRoles(): array
  {
      $roles = $this-&gt;roles;
      // guarantee every user at least has ROLE_USER
      $roles[] = 'ROLE_USER';
  
      return array_unique($roles);
  }
  
  public function setRoles(array $roles): self
  {
      $this-&gt;roles = $roles;
  
      return $this;
  }
  
  /**
   * @see UserInterface
   */
  public function getPassword(): string
  {
      return (string) $this-&gt;password;
  }
  
  public function setPassword(string $password): self
  {
      $this-&gt;password = $password;
  
      return $this;
  }
  
  /**
   * @see UserInterface
   */
  public function getSalt()
  {
      // not needed when using the "bcrypt" algorithm in security.yaml
  }
  
  /**
   * @see UserInterface
   */
  public function eraseCredentials()
  {
      // If you store any temporary, sensitive data on the user, clear it here
      // $this-&gt;plainPassword = null;
  }
  }
  </code></pre>
  
  <p>Auth</p>
  
  <pre><code>&lt;?php
  
  namespace App\Security;
  
  use App\Entity\User;
  use Doctrine\ORM\EntityManagerInterface;
  use Symfony\Component\HttpFoundation\RedirectResponse;
  use Symfony\Component\HttpFoundation\Request;
  use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
  use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
  use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
  use Symfony\Component\Security\Core\Security;
  use Symfony\Component\Security\Core\User\UserInterface;
  use Symfony\Component\Security\Core\User\UserProviderInterface;
  use Symfony\Component\Security\Csrf\CsrfToken;
  use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
  use Symfony\Component\Security\Http\Util\TargetPathTrait;
  
  class LoginAuthFormAuthenticator extends AbstractFormLoginAuthenticator
  {
  use TargetPathTrait;
  
  private $entityManager;
  private $urlGenerator;
  private $csrfTokenManager;
  private $passwordEncoder;
  
  public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
  {
      $this-&gt;entityManager = $entityManager;
      $this-&gt;urlGenerator = $urlGenerator;
      $this-&gt;csrfTokenManager = $csrfTokenManager;
      $this-&gt;passwordEncoder = $passwordEncoder;
  }
  
  public function supports(Request $request)
  {
      return 'app_login' === $request-&gt;attributes-&gt;get('_route')
          &amp;&amp; $request-&gt;isMethod('POST');
  }
  
  public function getCredentials(Request $request)
  {
      $credentials = [
          'email' =&gt; $request-&gt;request-&gt;get('email'),
          'password' =&gt; $request-&gt;request-&gt;get('password'),
          'csrf_token' =&gt; $request-&gt;request-&gt;get('_csrf_token'),
      ];
      $request-&gt;getSession()-&gt;set(
          Security::LAST_USERNAME,
          $credentials['email']
      );
  
      return $credentials;
  }
  
  public function getUser($credentials, UserProviderInterface $userProvider)
  {
      $token = new CsrfToken('authenticate', $credentials['csrf_token']);
      if (!$this-&gt;csrfTokenManager-&gt;isTokenValid($token)) {
          throw new InvalidCsrfTokenException();
      }
  
      $user = $this-&gt;entityManager-&gt;getRepository(User::class)-&gt;findOneBy(['email' =&gt; $credentials['email']]);
  
      if (!$user) {
          // fail authentication with a custom error
          throw new CustomUserMessageAuthenticationException('Email could not be found.');
      }
  
      return $user;
  }
  
  public function checkCredentials($credentials, UserInterface $user)
  {
      return $this-&gt;passwordEncoder-&gt;isPasswordValid($user, $credentials['password']);
  }
  
  public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
  {
      if ($targetPath = $this-&gt;getTargetPath($request-&gt;getSession(), $providerKey)) {
          return new RedirectResponse($targetPath);
      }
  
      // For example : return new RedirectResponse($this-&gt;urlGenerator-&gt;generate('some_route'));
      return new RedirectResponse($this-&gt;urlGenerator-&gt;generate('page_forum'));
  }
  
  protected function getLoginUrl()
  {
      return $this-&gt;urlGenerator-&gt;generate('app_login');
  }
  }
  </code></pre>
  
  <p>Thanks in advance, any ideas are welcome</p>