I can't login to my restricted areas. I'm following the tutorials on symfony.com.
I'll provide my security.yml file.
# To get started with security, check out the documentation:
# http://symfony.com/doc/current/book/security.html
security:
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
encoders:
Symfony\Component\Security\Core\User\User:
algorithm: bcrypt
cost: 12
AppBundle\Entity\User:
algorithm: bcrypt
cost: 12
providers:
# http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
our_db_provider:
entity:
class: AppBundle:User
# if you're using multiple entity managers
# manager_name: customer
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
http_basic: ~
provider: our_db_provider
anonymous: ~
# activate different ways to authenticate
#http_basic: ~
# http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate
# form_login: ~
# http://symfony.com/doc/current/cookbook/security/form_login_setup.html
logout:
path: /logout
target: /
access_control:
# require ROLE_ADMIN for /admin*
#- { path: ^/admin, roles: ROLE_ADMIN }
Next here is my User class
<?php
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
//use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
/**
* @ORM\Table(name="app_users")
* @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=25, unique=true)
*/
private $username;
/**
* @ORM\Column(type="string", length=64)
*/
private $password;
/**
* @ORM\Column(type="string", length=60, unique=true)
*/
private $email;
/**
* @ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
public function __construct()
{
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid(null, true));
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return $this->isActive;
}
public function getUsername()
{
return $this->username;
}
public function getSalt()
{
// you *may* need a real salt depending on your encoder
// see section on salt below
return null;
}
public function getPassword()
{
return $this->password;
}
public function getRoles()
{
return array('ROLE_USER','ROLE_ADMIN');
}
public function eraseCredentials()
{
}
/** @see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
$this->active
// see section on salt below
// $this->salt,
));
}
/** @see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
$this->active
// see section on salt below
// $this->salt
) = unserialize($serialized);
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* @param string $username
*
* @return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Set password
*
* @param string $password
*
* @return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Set email
*
* @param string $email
*
* @return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set isActive
*
* @param boolean $isActive
*
* @return User
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* @return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
}
and finally the User Repository class
<?php
// src/AppBundle/Repository/UserRepository.php
namespace AppBundle\Repository\Entity;
use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository implements UserLoaderInterface
{
public function loadUserByUsername($username)
{
$user = $this->createQueryBuilder('u')
->where('u.username = :username OR u.email = :email')
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
if (null === $user) {
$message = sprintf(
'Unable to find an active admin AppBundle:User object identified by "%s".',
$username
);
throw new UsernameNotFoundException($message);
}
return $user;
}
}
?>
My route for admin looks like below:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
class DefaultController extends Controller
{
/**
* @Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
// replace this example code with whatever you need
return $this->render('default/index.html.twig', [
'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'),
]);
}
/**
* @Route("/admin")
* @Security("has_role('ROLE_USER') or has_role('ROLE_ADMIN')")
*/
public function adminAction()
{
return new Response('<html><body>Admin page!</body></html>');
}
}
I was trying to follow the instructions here http://symfony.com/doc/current/cookbook/security/entity_provider.html#using-a-custom-query-to-load-the-user so that I could login with the username or email. When I go to /admin however no matter how I type the email or username into the http_basic prompt I can't seem to get in.
I'm guessing it might have something to do with encoding maybe? so in the security.yml file I put both encoder types, but it doesn't work either with just 1 or the other.
Keep in mind /admin has security role user or admin, so since I default return the role in the class of ROLE_USER it should still be able to get in.
If there is an error log somewhere I'm not sure how to find it. Help would be appreciated here since I'm still new to Symphony.
EDIT:
Forgot to mention in the database, I currently have the following::
1 admin $2y$12$qvLb/T2Xs4aWsFU6D4U2f.gmZi/blKYtspXeqiPLrHPFOPxwMaHY. joe@domain.com 1
2 joe $2y$12$M/7nTIEfQ1Ajpr/IhmEVoejskvt0dIb/FfIvT8i9LXdSR95zjT5OS joe@someotherdomain.com 1
The columns are ID, username, password, email, is_active
The encryption I did manually using: bin/console security:encode-password
and then put in the database fields. This worked previously for some other logging in tests I did, but just in case this is the problem putting this here. I tried putting just plaintext in the database also and typing that in to login and did not work either.
Thank you!
The issue was related to my namespace. When using HTTP_BASIC there was no easy way to see the error, but the name space on UserRepository.php should have been namespace AppBundle\Repository;
instead of namespace AppBundle\Repository\Entity;
which in retrospect makes no sense at all now since the file path wasn't even close to that.
I did make some changes to my security.yml file as well which may have helped I'll post it below.
Security.yml
# To get started with security, check out the documentation:
# http://symfony.com/doc/current/book/security.html
security:
encoders:
Symfony\Component\Security\Core\User\User:
algorithm: bcrypt
cost: 12
AppBundle\Entity\User:
algorithm: bcrypt
cost: 12
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
# http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
database:
entity: { class: AppBundle:User }
#property: username
# if you're using multiple entity managers
# manager_name: customer
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: true
# activate different ways to authenticate
http_basic:
provider: database
# http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate
# form_login: ~
# http://symfony.com/doc/current/cookbook/security/form_login_setup.html
#form_login:
#check_path: /login_check
#login_path: /login
#default_target_path: /home
#always_use_default_target_path: true
#provider: database
logout:
path: /logout
target: /
access_control:
# require ROLE_ADMIN for /admin*
#- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/admin, roles: ROLE_USER }
In particular,I gave a provider to http_basic so it knows to use my database as the source for logging in. The reason I had 2 encoders was so I could use the console command bin/console security:encode-password
since that console expects that particular class to work. I just gave it the same algorithm for hashing with the same cost so that when I use it to generate passwords for my users it will work with the AppBundle\Entity\User type as well. When you are creating users manually without a registration form that console command comes in handy to do the bcrypt hashing.
User.php
<?php
// src/AppBundle/Entity/User.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
/**
* @ORM\Table(name="app_users")
* @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
* @UniqueEntity(fields="email", message="Email already taken")
* @UniqueEntity(fields="username", message="Username already taken")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=25, unique=true)
* @Assert\NotBlank()
*/
private $username;
/**
* The below length depends on the "algorithm" you use for encoding
* the password, but this works well with bcrypt
*
* @ORM\Column(type="string", length=64)
*/
private $password;
/**
* @Assert\NotBlank()
* @Assert\Length(max = 4096)
*/
private $plainPassword;
/**
* @ORM\Column(type="string", length=60, unique=true)
* @Assert\NotBlank()
* @Assert\Email()
*/
private $email;
/**
* @ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
public function __construct()
{
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid(null, true));
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return $this->isActive;
}
public function getUsername()
{
return $this->username;
}
public function getSalt()
{
// you *may* need a real salt depending on your encoder
// see section on salt below
return null;
}
public function getPassword()
{
return $this->password;
}
public function getPlainPassword(){
return $this->plainPassword;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
/** @see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
$this->isActive
// see section on salt below
// $this->salt,
));
}
/** @see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
$this->isActive
// see section on salt below
// $this->salt
) = unserialize($serialized);
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* @param string $username
*
* @return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Set password
*
* @param string $password
*
* @return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Set plainPassword
*
* @param string $plainPassword
*
* @return User
*/
public function setPlainPassword($plainPassword)
{
$this->plainPassword = $plainPassword;
return $this;
}
/**
* Set email
*
* @param string $email
*
* @return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set isActive
*
* @param boolean $isActive
*
* @return User
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* @return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
}
UserRepository.php
<?php
// src/AppBundle/Repository/UserRepository.php
namespace AppBundle\Repository;
use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository implements UserLoaderInterface
{
public function loadUserByUsername($username)
{
$user = $this->createQueryBuilder('u')
->where('u.username = :username OR u.email = :email')
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
if (null === $user) {
$message = sprintf(
'Unable to find an active admin AppBundle:User object identified by "%s".',
$username
);
throw new UsernameNotFoundException($message);
}
return $user;
}
}
?>
DefaultController.php
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
class DefaultController extends Controller
{
/**
* @Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
// replace this example code with whatever you need
return $this->render('default/index.html.twig', [
'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'),
]);
}
/**
* @Route("/admin")
*/
public function adminAction()
{
return new Response('<html><body>Admin page!</body></html>');
}
}