I am trying to make my own User entity which extends the SuluUser entity (so I can add some properties). After following the instructions from the Sulu documentation: Extend Entities I then created my own symfony authentication for the (front) website however, when I try to authenticate I get the following error.
Edit: added the imports and annotations for the user entity
An exception occurred while executing 'SELECT t1.username AS username_2, t1.password AS password_3, t1.locale AS locale_4, t1.salt AS salt_5, t1.locked AS locked_6, t1.enabled AS enabled_7, t1.lastLogin AS lastLogin_8, t1.confirmationKey AS confirmationKey_9, t1.passwordResetToken AS passwordResetToken_10, t1.passwordResetTokenExpiresAt AS passwordResetTokenExpiresAt_11, t1.passwordResetTokenEmailsSent AS passwordResetTokenEmailsSent_12, t1.privateKey AS privateKey_13, t1.apiKey AS apiKey_14, t1.email AS email_15, t1.id AS id_16, t1.firstname AS firstname_17, t1.lastname AS lastname_18, t1.phonenumber AS phonenumber_19, t1.gender AS gender_20, t1.password_changed_date AS password_changed_date_21, t1.confirmation_token AS confirmation_token_22, t1.idContacts AS idContacts_23 FROM user t1 WHERE t0.email = ? LIMIT 1' with params ["test@test.com"]:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 't0.email' in 'where clause'
I'm not sure why it's using t0.email when the rest of the query uses t1 as an alias, but this breaks the login from the (front) website. Admin can login just fine for the sulu backend. I believe it has something to do with the inheritance with the SuluUser that my User entity extends. Any help would be very much appreciated. I have read about doctrine inheritance here but I don't think that applies to me as I cannot (should not) change the class in Sulu\Bundle\SecurityBundle\Entity\User. I have configured the following
App\Entity\User
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Sulu\Bundle\SecurityBundle\Entity\User as SuluUser;
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
* @UniqueEntity(fields={"email"})
*/
class User extends SuluUser
{
/**
* @Groups({"get", "post", "put", "get-comment-with-author", "get-blog-post-with-author"})
* @ORM\Column(type="string", length=25)
* @Assert\NotBlank(groups={"post"})
* @Assert\Length(min=4, max="100")
*/
private $Firstname;
/**
* @Groups({"get", "post", "put"})
* @ORM\Column(type="string", length=25)
* @Assert\NotBlank(groups={"post"})
* @Assert\Length(min=4, max="100")
*/
private $Lastname;
/**
* @ORM\Column(type="string", length=10, nullable=true)
*
* @Groups({"get", "post", "put"})
*/
private $phonenumber;
/**
* @ORM\Column(type="string", nullable=true)
* @Groups({"get", "post", "put"})
* @Assert\Collection()
*/
private $gender;
/**
* @Groups({"post"})
* @Assert\NotBlank(groups={"post"})
* @Assert\Expression(
* "this.getPassword() === this.getRetypedPassword()",
* message="Passwords do not match"
* )
*/
private $retypedPassword;
/**
* @Assert\Length(min=10, max="100")
* @Assert\NotBlank(groups={"put-reset-password"})
* @Groups({"put-reset-password"})
* @Assert\Regex(
* pattern="/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{7,}/",
* message="Your password needs to be at least 10 characters long and contain the folloiwing"
* )
*/
private $newPassword;
/**
* @Groups({"put-reset-password"})
* @Assert\NotBlank(groups={"put-reset-password"})
* @Assert\Expression(
* "this.getNewPassword() === this.getNewRetypedPassword()",
* message="Passwords does not match",
* groups={"put-reset-password"}
* )
*/
private $newRetypedPassword;
/**
* @Groups({"put-reset-password"})
* @Assert\NotBlank(groups={"put-reset-password"})
* @UserPassword(groups={"put-reset-password"})
*/
private $oldPassword;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $passwordChangedDate;
/**
* @ORM\Column(type="string", length=40, nullable=true)
*/
private $confirmationToken;
public function __construct()
{
$this->confirmationToken = null;
}
public function getFirstname(): ?string
{
return $this->Firstname;
}
public function setFirstname( $Firstname): self
{
$this->Firstname = $Firstname;
return $this;
}
public function getLastname(): ?string
{
return $this->Lastname;
}
public function setLastname( $Lastname): self
{
$this->Lastname = $Lastname;
return $this;
}
public function getPhonenumber(): ?string
{
return $this->phonenumber;
}
public function setPhonenumber(?string $phonenumber): self
{
$this->phonenumber = $phonenumber;
return $this;
}
/**
* @return mixed
*/
public function getGender()
{
return $this->gender;
}
/**
* @param mixed $gender
*/
public function setGender($gender): void
{
$this->gender = $gender;
}
/**
* @return mixed
*/
public function getRetypedPassword()
{
return $this->retypedPassword;
}
/**
* @param mixed $retypedPassword
*/
public function setRetypedPassword($retypedPassword): void
{
$this->retypedPassword = $retypedPassword;
}
public function getNewPassword(): ?string
{
return $this->newPassword;
}
public function setNewPassword($newPassword): void
{
$this->newPassword = $newPassword;
}
public function getNewRetypedPassword(): ?string
{
return $this->newRetypedPassword;
}
public function setNewRetypedPassword($newRetypedPassword): void
{
$this->newRetypedPassword = $newRetypedPassword;
}
public function getOldPassword(): ?string
{
return $this->oldPassword;
}
public function setOldPassword($oldPassword): void
{
$this->oldPassword = $oldPassword;
}
public function getPasswordChangedDate()
{
return $this->passwordChangedDate;
}
public function setPasswordChangedDate($passwordChangedDate): void
{
$this->passwordChangedDate = $passwordChangedDate;
}
public function getConfirmationToken()
{
return $this->confirmationToken;
}
public function setConfirmationToken($confirmationToken): void
{
$this->confirmationToken = $confirmationToken;
}
public function __toString(): string
{
return $this->Firstname . ' ' . $this->Lastname;
}
}
App\config\packges\security_website.yaml
security:
encoders:
App\Entity\User:
algorithm: auto
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: false
lazy: true
provider: app_user_provider
guard:
authenticators:
- App\Security\AppAuthenticator
logout:
path: app_logout
# where to redirect after logout
target: home
App\Security\AppAuthenticator
class AppAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app.login';
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
}
public function supports(Request $request)
{
return self::LOGIN_ROUTE === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['email']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $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->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function getPassword($credentials): ?string
{
return $credentials['password'];
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
$role = $token->getUser()->getRoles();
// For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
// throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
// return new RedirectResponse('admin');
return new RedirectResponse('/');
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}