i was learning OAuth2 in order to login an account with Google+ , Facebook, etc. on a web site.
I have followed a tutorial about it, and i have an issue when i clicked on my button "Connect with Google+" :
In the tutorial, he created a User.php but i had already one with FOS User.
Here is my code :
User.php :
<?php
// src/AppBundle/Entity/User.php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser implements UserInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string")
*/
protected $nom;
/**
* #ORM\Column(type="string")
*/
protected $prenom;
/**
* #ORM\Column(type="string")
*/
protected $nationalite;
/**
* #ORM\Column(type="datetime")
*/
protected $birthday;
/**
* #ORM\Column(type="string")
*/
protected $sexe;
/**
* #ORM\OneToOne(targetEntity="App\Entity\AnnonceColocation", mappedBy="User", cascade={"persist", "remove"})
*/
private $annonceColocation;
/**
* #ORM\OneToOne(targetEntity="App\Entity\AnnonceColocataire", mappedBy="User", cascade={"persist", "remove"})
*/
private $annonceColocataire;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Message", mappedBy="expediteur", orphanRemoval=true)
*/
private $sendMessage;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Message", mappedBy="destinataire", orphanRemoval=true)
*/
private $receivedMessage;
public function __construct()
{
parent::__construct();
$this->sendMessage = new ArrayCollection();
$this->receivedMessage = new ArrayCollection();
// your own logic
}
/**
* #return mixed
*/
public function getNom()
{
return $this->nom;
}
/**
* #param mixed $nom
*/
public function setNom($nom)
{
$this->nom = $nom;
}
/**
* #return mixed
*/
public function getPrenom()
{
return $this->prenom;
}
/**
* #param mixed $prenom
*/
public function setPrenom($prenom)
{
$this->prenom = $prenom;
}
public function getNationalite()
{
return $this->nationalite;
}
/**
* #param mixed $nationalite
*/
public function setNationalite($nationalite)
{
$this->nationalite = $nationalite;
}
public function getBirthday()
{
return $this->birthday;
}
/**
* #param mixed $birthday
*/
public function setBirthday($birthday)
{
$this->birthday = $birthday;
}
public function getSexe()
{
return $this->sexe;
}
/**
* #param mixed $sexe
*/
public function setSexe($sexe)
{
$this->sexe = $sexe;
}
public function getPicture()
{
return $this->picture;
}
/**
* #param mixed $picture
*/
public function setPicture($picture)
{
$this->picture = $picture;
}
public function getAnnonceColocation(): ?AnnonceColocation
{
return $this->annonceColocation;
}
public function setAnnonceColocation(AnnonceColocation $annonceColocation): self
{
$this->annonceColocation = $annonceColocation;
// set (or unset) the owning side of the relation if necessary
$newUser = $annonceColocation === null ? null : $this;
if ($newUser !== $annonceColocation->getUser()) {
$annonceColocation->setUser($newUser);
}
return $this;
}
public function getAnnonceColocataire(): ?AnnonceColocataire
{
return $this->annonceColocataire;
}
public function setAnnonceColocataire(?AnnonceColocataire $annonceColocataire): self
{
$this->annonceColocataire = $annonceColocataire;
// set (or unset) the owning side of the relation if necessary
$newUser = $annonceColocataire === null ? null : $this;
if ($newUser !== $annonceColocataire->getUser()) {
$annonceColocataire->setUser($newUser);
}
return $this;
}
/**
* #return Collection|Message[]
*/
public function getSendMessage(): Collection
{
return $this->sendMessage;
}
public function addSendMessage(Message $sendMessage): self
{
if (!$this->sendMessage->contains($sendMessage)) {
$this->sendMessage[] = $sendMessage;
$sendMessage->setExpediteur($this);
}
return $this;
}
public function removeSendMessage(Message $sendMessage): self
{
if ($this->sendMessage->contains($sendMessage)) {
$this->sendMessage->removeElement($sendMessage);
// set the owning side to null (unless already changed)
if ($sendMessage->getExpediteur() === $this) {
$sendMessage->setExpediteur(null);
}
}
return $this;
}
/**
* #return Collection|Message[]
*/
public function getReceivedMessage(): Collection
{
return $this->receivedMessage;
}
public function addReceivedMessage(Message $receivedMessage): self
{
if (!$this->receivedMessage->contains($receivedMessage)) {
$this->receivedMessage[] = $receivedMessage;
$receivedMessage->setDestinataire($this);
}
return $this;
}
public function removeReceivedMessage(Message $receivedMessage): self
{
if ($this->receivedMessage->contains($receivedMessage)) {
$this->receivedMessage->removeElement($receivedMessage);
// set the owning side to null (unless already changed)
if ($receivedMessage->getDestinataire() === $this) {
$receivedMessage->setDestinataire(null);
}
}
return $this;
}
public function getRoles()
{
return array('ROLE_USER');
}
/**
* Returns the password used to authenticate the user.
*
* This should be the encoded password. On authentication, a plain-text
* password will be salted, encoded, and then compared to this value.
*
* #return string The password
*/
public function getPassword()
{
return null;
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* #return string|null The salt
*/
public function getSalt()
{
return null;
}
/**
* Returns the username used to authenticate the user.
*
* #return string The username
*/
public function getUsername()
{
return $this->email;
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
return null;
}
}
UserProvider.php :
<?php
/**
* Created by IntelliJ IDEA.
* User: mert
* Date: 12/18/17
* Time: 12:58 PM
*/
namespace App\Security;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class UserProvider implements UserProviderInterface
{
private $entityManager;
/**
* UserProvider constructor.
* #param EntityManagerInterface $entityManager
* #internal param Client $httpClient
* #internal param UserOptionService $userOptionService
* #internal param ProjectService $projectService
* #internal param SessionService $sessionService
* #internal param Session $session
* #internal param UserOptionService $userOptionsService
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* Loads the user for the given username.
*
* This method must throw UsernameNotFoundException if the user is not
* found.
*
* #param string $username The username
*
* #return UserInterface
*
* #throws \Doctrine\ORM\NonUniqueResultException
*/
public function loadUserByUsername($username)
{
return $this->entityManager->createQueryBuilder('u')
->where('u.email = :email')
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
}
/**
* Refreshes the user.
*
* It is up to the implementation to decide if the user data should be
* totally reloaded (e.g. from the database), or if the UserInterface
* object can just be merged into some internal array of users / identity
* map.
*
* #param UserInterface $user
* #return UserInterface
*
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $user;
}
/**
* Whether this provider supports the given user class.
*
* #param string $class
*
* #return bool
*/
public function supportsClass($class)
{
return $class === 'App\Security\User';
}
}
GoogleController.php :
<?php
namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class GoogleController extends AbstractController
{
/**
* Link to this controller to start the "connect" process
*
* #Route("/connect/google", name="connect_google")
* #param ClientRegistry $clientRegistry
* #return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function connectAction(ClientRegistry $clientRegistry)
{
return $clientRegistry
->getClient('google')
->redirect();
}
/**
* Facebook redirects to back here afterwards
*
* #Route("/connect/google/check", name="connect_google_check")
* #param Request $request
* #return JsonResponse|\Symfony\Component\HttpFoundation\RedirectResponse
*/
public function connectCheckAction(Request $request)
{
if (!$this->getUser()) {
return new JsonResponse(array('status' => false, 'message' => "User not found!"));
} else {
return $this->redirectToRoute('default');
}
}
}
and my GoogleAuthenticator.php :
<?php
namespace App\Security;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use League\OAuth2\Client\Provider\GoogleUser;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
* Created by IntelliJ IDEA.
* User: mert
* Date: 12/18/17
* Time: 12:00 PM
*/
class GoogleAuthenticator extends SocialAuthenticator
{
private $clientRegistry;
private $em;
private $router;
public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $em, RouterInterface $router)
{
$this->clientRegistry = $clientRegistry;
$this->em = $em;
$this->router = $router;
}
public function supports(Request $request)
{
return $request->getPathInfo() == '/connect/google/check' && $request->isMethod('GET');
}
public function getCredentials(Request $request)
{
return $this->fetchAccessToken($this->getGoogleClient());
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
/** #var GoogleUser $googleUser */
$googleUser = $this->getGoogleClient()
->fetchUserFromToken($credentials);
$email = $googleUser->getEmail();
$user = $this->em->getRepository('App:User')
->findOneBy(['email' => $email]);
if (!$user) {
$user = new User();
$user->setEmail($googleUser->getEmail());
$user->setFullname($googleUser->getName());
$user->setCreatedAt(new \DateTime(date('Y-m-d H:i:s')));
$this->em->persist($user);
$this->em->flush();
}
return $user;
}
/**
* #return \KnpU\OAuth2ClientBundle\Client\OAuth2Client
*/
private function getGoogleClient()
{
return $this->clientRegistry
->getClient('google');
}
/**
* Returns a response that directs the user to authenticate.
*
* This is called when an anonymous request accesses a resource that
* requires authentication. The job of this method is to return some
* response that "helps" the user start into the authentication process.
*
* Examples:
* A) For a form login, you might redirect to the login page
* return new RedirectResponse('/login');
* B) For an API token authentication system, you return a 401 response
* return new Response('Auth header required', 401);
*
* #param Request $request The request that resulted in an AuthenticationException
* #param \Symfony\Component\Security\Core\Exception\AuthenticationException $authException The exception that started the authentication process
*
* #return \Symfony\Component\HttpFoundation\Response
*/
public function start(Request $request, \Symfony\Component\Security\Core\Exception\AuthenticationException $authException = null)
{
return new RedirectResponse('/login');
}
/**
* Called when authentication executed, but failed (e.g. wrong username password).
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the login page or a 403 response.
*
* If you return null, the request will continue, but the user will
* not be authenticated. This is probably not what you want to do.
*
* #param Request $request
* #param \Symfony\Component\Security\Core\Exception\AuthenticationException $exception
*
* #return \Symfony\Component\HttpFoundation\Response|null
*/
public function onAuthenticationFailure(Request $request, \Symfony\Component\Security\Core\Exception\AuthenticationException $exception)
{
// TODO: Implement onAuthenticationFailure() method.
}
/**
* Called when authentication executed and was successful!
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the last page they visited.
*
*
If you return null, the current request will continue, and the user
* will be authenticated. This makes sense, for example, with an API.
*
* #param Request $request
* #param \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token
* #param string $providerKey The provider (i.e. firewall) key
*
* #return void
*/
public function onAuthenticationSuccess(Request $request, \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token, $providerKey)
{
// TODO: Implement onAuthenticationSuccess() method.
}
}
Hope some of you could help on this. Thanks a lot.
Since Google plus doesn't exist anymore you need to change the Google API for the login. Check the official Google Oauth2.0 for Google APIs (https://developers.google.com/identity/protocols/googlescopes). For this you need to change the scope inside the config.yml file. Instead of "https://www.googleapis.com/auth/plus.login" you can use "https://www.googleapis.com/auth/cloud-platform" or you can use scope: "openid". That at least works for me.
Related
I would like to know how to pass UserInterface as a parameter to the encodePassword function. Actually, when running the following command line php bin/console doctrine:fixtures:load , I got this message error:
Argument 1 passed to Symfony\Component\Security\Core\Encoder\UserPasswordEncoder::encodePassword() must be an instance of Symfony\Component\Security\Core\User\UserInterface, instance of App\Entity\User given,
These are my relevant files:
User.php :
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
* #UniqueEntity(fields="email", message="Un utilisateur existe déjà avec cet email.")
*/
class User
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $nom;
/**
* #ORM\Column(type="string", length=255)
*/
private $prenom;
/**
* #ORM\Column(type="text")
*/
private $roles;
/**
* #ORM\Column(type="string", length=255)
*/
private $email;
/**
* #ORM\Column(type="string", length=255)
*/
private $password;
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getPrenom(): ?string
{
return $this->prenom;
}
public function setPrenom(string $prenom): self
{
$this->prenom = $prenom;
return $this;
}
public function getRoles(): ?string
{
return $this->roles;
}
public function setRoles(string $roles): self
{
$this->roles = $roles;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
// public function getRoles(){
// return [
// 'ROLE_ADMIN'
// ];
// }
public function getSalt() {}
public function eraseCredentials() {}
// Some added code
public function serialize(){
return $this->serialize([
$this->id,
$this->email,
$this->password
]);
}
public function unserialize($string){
list(
$this->id,
$this->email,
$this->password
) = unserialize($string, ['allowed_classes' => false]);
}
}
UserFixture.php :
<?php
namespace App\DataFixtures;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class UserFixture extends Fixture
{
private $encoder;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder = $encoder;
}
public function load(ObjectManager $manager): void
{
$user = new User();
$user->getNom('test');
$user->setPrenom('test');
$user->setRoles('["ROLE_ADMIN"]');
$user->setEmail('test#gmail.com');
$user->setPassword(
$this->encoder->encodePassword($user,'test')
);
$manager->persist($user);
$manager->flush();
}
}
UserInterface.php :
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien#symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\User;
use Symfony\Component\Security\Core\Role\Role;
/**
* Represents the interface that all user classes must implement.
*
* This interface is useful because the authentication layer can deal with
* the object through its lifecycle, using the object to get the encoded
* password (for checking against a submitted password), assigning roles
* and so on.
*
* Regardless of how your users are loaded or where they come from (a database,
* configuration, web service, etc.), you will have a class that implements
* this interface. Objects that implement this interface are created and
* loaded by different objects that implement UserProviderInterface.
*
* #see UserProviderInterface
*
* #author Fabien Potencier <fabien#symfony.com>
*/
interface UserInterface
{
/**
* Returns the roles granted to the user.
*
* public function getRoles()
* {
* return ['ROLE_USER'];
* }
*
* Alternatively, the roles might be stored in a ``roles`` property,
* and populated in any number of different ways when the user object
* is created.
*
* #return array<Role|string> The user roles
*/
public function getRoles();
/**
* Returns the password used to authenticate the user.
*
* This should be the encoded password. On authentication, a plain-text
* password will be salted, encoded, and then compared to this value.
*
* #return string|null The encoded password if any
*/
public function getPassword();
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* #return string|null The salt
*/
public function getSalt();
/**
* Returns the username used to authenticate the user.
*
* #return string The username
*/
public function getUsername();
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials();
}
UserPasswordEncoderInterface.php :
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien#symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* UserPasswordEncoderInterface is the interface for the password encoder service.
*
* #author Ariel Ferrandini <arielferrandini#gmail.com>
*
* #method bool needsRehash(UserInterface $user)
*/
interface UserPasswordEncoderInterface
{
/**
* Encodes the plain password.
*
* #param string $plainPassword The password to encode
*
* #return string The encoded password
*/
public function encodePassword(UserInterface $user, $plainPassword);
/**
* #param string $raw A raw password
*
* #return bool true if the password is valid, false otherwise
*/
public function isPasswordValid(UserInterface $user, $raw);
}
So, what's wrong in my code and how can I fix it. Any idea?
In your user.php file update it to this:
class User implements UserInterface
{...
i have a big problem implementing JWT Tokens on symfony.
I already make work the JWT token, but i need to add to the token info the User roles too. i am doing this using a Listener (JWTCreatedListener):
public function onJWTCreated(JWTCreatedEvent $event)
{
$request = $this->requestStack->getCurrentRequest();
$payload = $event->getData();
$payload['ip'] = $request->getClientIp();
$payload['roles'] = $event->getUser()->getRoles();
$event->setData($payload);
}
I implemented the Role.php (AppBundle/Entity/Role.php) on this way:
<?php
namespace AppBundle\Entity;
use Symfony\Component\Security\Core\Role\RoleInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="acme_role")
* #ORM\Entity()
*/
class Role implements RoleInterface
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id()
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=30)
*/
private $name;
/**
* #ORM\Column(name="role", type="string", length=20, unique=true)
*/
private $role;
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="roles")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* #see RoleInterface
*/
public function getRole()
{
return $this->role;
}
// ... getters and setters for each property
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Role
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set role
*
* #param string $role
*
* #return Role
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* Add user
*
* #param \AppBundle\Entity\User $user
*
* #return Role
*/
public function addUser(\AppBundle\Entity\User $user)
{
$this->users[] = $user;
return $this;
}
/**
* Remove user
*
* #param \AppBundle\Entity\User $user
*/
public function removeUser(\AppBundle\Entity\User $user)
{
$this->users->removeElement($user);
}
/**
* Get users
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getUsers()
{
return $this->users;
}
}
And my User class:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Table(name="users")
* #ORM\Entity
*/
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=500)
*/
private $password;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
*
*/
private $roles;
public function __construct($username)
{
$this->isActive = true;
$this->username = $username;
$this->roles = new ArrayCollection();
}
public function getUsername()
{
return $this->username;
}
public function getSalt()
{
return null;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getRoles()
{
return $this->roles->toArray();
}
public function eraseCredentials()
{
}
/**
* 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 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;
}
/**
* Add role
*
* #param \AppBundle\Entity\Role $role
*
* #return User
*/
public function addRole(\AppBundle\Entity\Role $role)
{
$this->roles[] = $role;
return $this;
}
/**
* Remove role
*
* #param \AppBundle\Entity\Role $role
*/
public function removeRole(\AppBundle\Entity\Role $role)
{
$this->roles->removeElement($role);
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return $this->isActive;
}
// serialize and unserialize must be updated - see below
public function serialize()
{
return serialize(array(
// ...
$this->isActive
));
}
public function unserialize($serialized)
{
list (
// ...
$this->isActive
) = unserialize($serialized);
}
}
The problem is that this method getRole() always returns empty.
This is my db data:
[users]
id username password is_active
1 abriceno $2y$13$NW6uNOKJGUQTSXirej4HKOwIa6mWzYqFxzz1ppWQjyp... 1
[acme_role]
id name role
1 admin ROLE_ADMIN
[user_role]
user_id user_role
1 1
Also, i try to call the data from a controller test using doctrine:
public function indexAction(Request $request)
{
$repository = $this->getDoctrine()->getRepository('AppBundle:User');
$user = $repository->findOneByusername('abriceno');
$username = $user->getUsername();
$roles = $user->getRoles();
$arr = array(
'username' => $user->getUsername(),
'password' => $user->getPassword(),
'roles' => $user->getRoles()
);
return new JsonResponse($arr);
this returns:
{"username":"abriceno","password":"$2y$13$NW6uNOKJGUQTSXirej4HKOwIa6mWzYqFxzz1ppWQjypQJLIgUGJ.m","roles":[{}]}
I am so desperate... thanks for all the help that you can provide to me.
UPDATE 1
If i do print_r($role) this prints a huuuuuge list of values:
array(1) { [0]=> object(AppBundle\Entity\Role)#624 (4) { ["id":"AppBundle\Entity\Role":private]=> int(1) ["name":"AppBundle\Entity\Role":private]=> string(5) "admin" ["role":"AppBundle\Entity\Role":private]=> string(10) "ROLE_ADMIN" ["users":"AppBundle\Entity\Role":private]=> object(Doctrine\ORM\PersistentCollection)#626 (9) { ["snapshot":"Doctrine\ORM\PersistentCollection":private]=> array(0) { } ["owner":"Doctrine\ORM\PersistentCollection":private]=> *RECURSION*
... and keeps going... very strange!!
Seems that the join table is not handled directly by doctrine, so you need do specify to doctrine howto map the referenced fied with the JoinTable annotation (See the default mapping section.)
In your case, try to modify the roles relation definition in the User entity as follow:
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
* #ORM\JoinTable(name="user_role",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_role", referencedColumnName="id")})
*
*/
private $roles;
Hope this help
Finally i fix this with this code:
// Work of roles
$roles = $event->getUser()->getRoles();
$role_length = count($roles);
$role_list = array();
for ($i=0; $i <$role_length ; $i++) {
array_push($role_list,$roles[$i]->getRole());
}
$payload = $event->getData();
$payload['ip'] = $request->getClientIp();
$payload['roles'] = $role_list;
The problem (i guess) is on the ->getRoles(); code. This returns a array of Entity\Role class, not an array of roles.
Now the dump is:
{
"token": "eyJhbGciOiJSUzI1NiJ9.....",
"data": {
"roles": [
"ROLE_ADMIN"
]
}
}
I have made on my Symfony 3.0 project a User Entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="AppBundle\Entity\UserRepository")
*/
class Users
{
/**
* #ORM\Column(type="string", length=60)
* #ORM\Id
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="AppBundle\Doctrine\AutoIdGenerate")
*/
private $id;
/**
* #ORM\Column(type="string", length=15)
*/
private $username;
/**
* #ORM\Column(type="string", length=30)
*/
private $password;
/**
* #ORM\Column(type="string", length=60)
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="AppBundle\Doctrine\AutoIdGenerate")
*/
private $token;
/**
* #ORM\Column(type="boolean")
*/
private $activated;
/**
* Get id
*
* #return string
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* #param string $username
*
* #return Users
*/
public function setUsername($username)
{
$this->username = strip_tags($username);
return $this;
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set password
*
* #param string $password
*
* #return Users
*/
public function setPassword($password)
{
$options = ['cost' => 42];
$this->password = password_hash ($password,CRYPT_BLOWFISH,$options);
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set token
*
* #param string $token
*
* #return Users
*/
public function setToken($token)
{
$this->token = $token;
return $this;
}
/**
* Get token
*
* #return string
*/
public function getToken()
{
return $this->token;
}
/**
* Set activated
*
* #param boolean $activated
*
* #return Users
*/
public function setActivated($activated)
{
$this->activated = $activated;
return $this;
}
/**
* Get activated
*
* #return boolean
*/
public function getActivated()
{
return $this->activated;
}
}
And a Repository that Iplements My logic:
<?php
namespace AppBundle\Entity;
use AppBundle\Entity\Product;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
public function register($username,$password,$email)
{
//A Generic return Object
$status=array('status'=>'true','data'=>"");
if(empty($username))
{
$status['status']=-1;
$status['data']='Username not given';
return $status;
}
if(empty($password))
{
$status['status']=-1;
$status['data']='Password not given';
return $status;
}
if(empty($email))
{
$status['status']=-1;
$status['data']='Email not given';
return $status;
}
$user=new User();
$user->setUsername($username)->setPassword($password);
$em=$this->getEntityManager();
$em->persist($user);
$em->flush();
return $status;
}
}
What I wanna know is when an Entry Sucessfully Inserted to send the email to verify the registration. How I will know That the User Has Sucessfully Inserted In the DB; Also How I will take any Sort of AutoGenerated Fields such as id and token in order to verify the mail address.
I Found that In order to Use Email I can Use the Swiftmailer so the Question
Is NOT hoiw to send an Email, but how I will know that the user successfully inserted.
If your entry is correctly inserted, you'll not have any error.
Otherwise, you'll have one (unless you have a mistake that is not coming from the insertion).
To catch a possible error, you can do this :
// ...
$em->persist($user);
try {
$em->flush();
// Success
} catch (\Exception $e) {
$result['data'] = $e->getMessage(); // The exact message of the exception thrown during INSERT
// Error
}
Otherwise, you can consider the entry has been stored.
For the auto-generated fields, they are immediately available through getters after calling $em->flush().
You can register Event Listener for Doctrine postFlush event.
Example listener:
<?php
use Doctrine\ORM\Event\PostFlushEventArgs;
class PostFlushExampleListener
{
public function postFlush(PostFlushEventArgs $args)
{
// ...
}
}
Service registration in Symfony:
services:
my.listener:
class: PostFlushExampleListener
tags:
- { name: doctrine.event_listener, event: postFlush }
Symfony documentation: http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html
Doctrine documentation: http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/events.html#listening-and-subscribing-to-lifecycle-events
You should have all autogenerated fields available just after calling flush action there where you call it. Doctrine events are non mandatory in your case. I would just assume that after flush i will have all needed data and data will be set in database.
I get this error 'Must implement UserProviderInterface'
when trying to using the symfony login features. I'm referencing the repository * #ORM\Entity(repositoryClass="Login\LogBundle\Entity\Repository\UserRepository") . Does anyone have any ideas.
Entity
<?php
namespace Login\LogBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Login\LogBundle\Entity\Repository\UserRepository")
* #ORM\HasLifecycleCallbacks()
*/
class User implements UserInterface, \Serializable
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $password;
/**
* #var string
*/
private $name;
/**
* #var string
*/
private $username;
/**
* #ORM\Column(type="string", length=32)
*/
private $salt;
/**
* #ORM\Column(type="string", length=60, unique=true)
*/
private $email;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
/**
/**
* #OneToMany(targetEntity="userapidetails", mappedBy="user")
*/
public $userapidetails;
public function __construct()
{
$this->userapidetails = new ArrayCollection();
$this->isActive = true;
$this->salt = md5(uniqid(null, true));
}
public function getUserapidetails() {
return $this->userapidetails;
}
// public function setUserapidetails($userapidetails) {
// $this->userapi=$userapidetails;
// return $this;
// }
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set password
*
* #param string $password
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set name
*
* #param string $name
* #return User
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set username
*
* #param string $username
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return $this->username;
}
/**
* #var string
*/
private $isactive;
/**
* 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 salt
*
* #param string $salt
* #return User
*/
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
/**
* Get salt
*
* #return string
*/
public function getSalt()
{
return $this->salt;
}
/**
* Set isactive
*
* #param string $isactive
* #return User
*/
public function setIsactive($isactive)
{
$this->isactive = $isactive;
return $this;
}
/**
* Get isactive
*
* #return string
*/
public function getIsactive()
{
return $this->isactive;
}
/**
* #inheritDoc
*/
public function getRoles()
{
return array('ROLE_USER');
}
/**
* #inheritDoc
*/
public function eraseCredentials()
{
}
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->salt,
$this->password,
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->salt,
$this->password,
) = unserialize($serialized);
}
}
Repository
// src/Acme/UserBundle/Entity/UserRepository.php
namespace Login\LogBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Doctrine\ORM\NoResultException;
class UserRepository extends EntityRepository implements UserProviderInterface
{
public function loadUserByUsername($username)
{
$q = $this
->createQueryBuilder('u')
->where('u.username = :username OR u.email = :email')
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery();
try {
// The Query::getSingleResult() method throws an exception
// if there is no record matching the criteria.
$user = $q->getSingleResult();
} catch (NoResultException $e) {
$message = sprintf(
'Unable to find an active admin AcmeUserBundle:User object identified by "%s".',
$username
);
throw new UsernameNotFoundException($message, 0, $e);
}
return $user;
}
public function refreshUser(UserInterface $user)
{
$class = get_class($user);
if (!$this->supportsClass($class)) {
throw new UnsupportedUserException(
sprintf(
'Instances of "%s" are not supported.',
$class
)
);
}
return $this->find($user->getId());
}
public function supportsClass($class)
{
return $this->getEntityName() === $class
|| is_subclass_of($class, $this->getEntityName());
}
}
Check the app/config/security.yml file with something like this:
# app/config/security.yml
security:
providers:
main:
entity:
class: Acme\UserBundle\Entity\User
property: username
And path to repositoryClass UserRepository is wrong, maybe you mean this:
#ORM\Entity(repositoryClass="Login\LogBundle\Entity\UserRepository")
becouse you use namespace \Login\LogBundle\Entity\UserRepository.
To load user from Repository, you should omit the { property: username } from security.yml.
Simple process,
Need to create Users with mapped Roles.
I followed the step from link
http://symfony.com/doc/current/cookbook/security/entity_provider.html
User and Roles table generated but users_roles table is not generated in MySql...
Will i need to create it manually?
Second
I have configured with User table for Authentication
After login it redirects to Error page,
FatalErrorException: Error: Call to a member function toArray() on a non-object in /var/www/vibilling_3/src/ViBillingPortal/AuthenticationBundle/Entity/users.php line 130
I searched, but i cant find any solutions... Below my code
Bill/PortalBundle/Entity/users.php
namespace ViBillingPortal\AuthenticationBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* users
*/
class users implements UserInterface, \Serializable
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $username;
/**
* #var string
*/
private $password;
/**
* #var string
*/
private $created_date;
/**
* #ORM\ManyToMany(targetEntity="roles", inversedBy="users")
* #ORM\JoinTable(name="user_roles")
*/
private $userroles;
public function __construct()
{
$this->userroles = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* #param string $username
* #return users
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set password
*
* #param string $password
* #return users
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set created_date
*
* #param string $created_date
* #return users
*/
public function setCreated_date($password)
{
$this->password = $created_date;
return $this;
}
/**
* Get Created_date
*
* #return string
*/
public function getCreated_date()
{
return $this->created_date;
}
/**
* Get Roles
*/
public function getRoles()
{
return $this->userroles->toArray();
}
/**
* #inheritDoc
*/
public function getSalt()
{
}
/**
* #inheritDoc
*/
public function eraseCredentials()
{
}
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
}
}
Bill/PortalBundle/Entity/roles.php
namespace ViBillingPortal\AuthenticationBundle\Entity;
use Symfony\Component\Security\Core\Role\RoleInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* roles
*/
class roles implements RoleInterface, \Serializable
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $name;
/**
* #ORM\Column(name="role", type="string", length=20, unique=true)
*/
private $role;
/**
* #ORM\ManyToMany(targetEntity="users", mappedBy="userroles")
*/
protected $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return roles
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #see RoleInterface
*/
public function getRole()
{
return $this->role;
}
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
}
}
You should use a ManyToMany relation, not a ManyToOne if you want to use a join table : http://docs.doctrine-project.org/en/2.0.x/reference/association-mapping.html#many-to-many-bidirectional
For the second error, it's strange as you initialize usersroles as an ArrayCollection in your construct method, it should work.
Could you add a var_dump to look what is stored in this property ?
Why don't you get any setter/getter for usersroles ?
I think you should also read Symfony coding standards : http://symfony.com/doc/current/contributing/code/standards.html. You coding style is not consistent.