Creating an admin user using datafixtures and fosuserbundle - symfony

I'm trying to create a new User Admin from a fixture. I'm using FOSUserBundle and Symfony2.
$userManager = $this->container->get('fos_user.user_manager');
//$userAdmin = $userManager->createUser();
$userAdmin = new UserAdmin();
$userAdmin->setUsername('francis');
$userAdmin->setEmail('francis#francis.com');
$userAdmin->setEnabled(true);
$userAdmin->setRoles(array('ROLE_ADMIN'));
$userManager->updateUser($userAdmin, true);
I'm always getting this error:
[ErrorException]
Notice: Undefined property:
INCES\ComedorBundle\DataFixtures\ORM\LoadUserAdminData::$container in
/public_html/Symfony/src/INCES/ComedorBundle/DataFixtures/ORM/LoadUserAdminData.php line 16

This worked for me (i'm also using FOSUserBundle):
// Change the namespace!
namespace Acme\DemoBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class LoadUserData implements FixtureInterface, ContainerAwareInterface
{
//.. $container declaration & setter
public function load(ObjectManager $manager)
{
// Get our userManager, you must implement `ContainerAwareInterface`
$userManager = $this->container->get('fos_user.user_manager');
// Create our user and set details
$user = $userManager->createUser();
$user->setUsername('username');
$user->setEmail('email#domain.com');
$user->setPlainPassword('password');
//$user->setPassword('3NCRYPT3D-V3R51ON');
$user->setEnabled(true);
$user->setRoles(array('ROLE_ADMIN'));
// Update the user
$userManager->updateUser($user, true);
}
}
Hope this helps someone! :)

Follow this section of the documentation.

The Error is because the $container is currently undefined. To fix this, add the ContainerAwareInterface to your class definition.
class LoadUserData implements FixtureInterface, ContainerAwareInterface
{
...
}
This will not completely get you what you want though, since you are creating the user without the UserManager. Instead you should be using the line you have commented out.
It seems to me that you don't need the UserAdmin class. The admin users should be a subset of the User distinguished only by the roles that they have.
You should use the UserManager to create a User(not UserAdmin) and set the roles.
If you need to keep an index of all admin users, a MySQL VIEW could accomplish this, or you could create your own custom "cache" table and use Doctrine Listeners to update it when needed.
This question is fairly old, so I am guessing you found the answer or at least a workaround.
Would you please provide that? It is ok to answer your own questions.

This is the updated version for SF3+
This answer is based on Anil and Mun Mun Das answers.
namespace App\DataFixtures;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use FOS\UserBundle\Model\UserManagerInterface;
class AdminFixtures extends Fixture
{
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function load(ObjectManager $manager)
{
$user = $this->userManager->createUser();
$user->setUsername('username');
$user->setEmail('email#domain.com');
$user->setPlainPassword('password');
$user->setEnabled(true);
$user->setRoles(array('ROLE_ADMIN'));
$this->userManager->updateUser($user);
}
}

This is what I did using Symfony 4, SonataAdmin and FosUserBundle
namespace App\DataFixtures;
use App\Application\Sonata\UserBundle\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
class UserFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$user = new User();
$user->setUsername('yourusername');
$user->setEmail('youremail#email.com');
$user->setEnabled(true);
$user->setPlainPassword('yourpassword');
$user->setRoles(array('ROLE_SUPER_ADMIN'));
$manager->persist($user);
$manager->flush();
}
}

Related

Symfony get current User entity in controller

I'm beginner in Symfony (6.1) and sometimes I need to get the current User in my controllers.
The way I use for the moment is :
$user = $userRepository->find($this->getUser()->getId());
But they are a better way ?
Because $this->getUser() give me the UserInterface and I need the User entity. screenshot example
Thanks to read me
Using $this->getUser() will get you the current user (that implements UserInterface). The getUser() method lives in AbstractController.php that is part of the Symfony FrameworkBundle. You could extend this controller if you really want to change the getUser() method to not use the UserInterface, but I think it would be better to simply change the typehint of the function (setUser) you call (in your screenshot -please write this out next time-) to use the UserInterface. Something like this:
public function setUser(UserInterface $user)
{
//....
}
Edit:
After consideration, thanks to Cerad's valid point below, it is probably best to use setUser(User $user) given that you might use another class that implements UserInterface. Then either use var annations to tell your IDE which class you use, like so:
/** #var User $user */
$user = $this->getUser();
Or extend AbstractController so that it has a getUser method with a User return type like so:
namespace App\Controller;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class BaseController extends AbstractController
{
protected function getUser(): ?User
{
if (!$this->container->has('security.token_storage')) {
throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".');
}
if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return null;
}
return $token->getUser();
}
}
or maybe even better, like this:
/**
* #return User|null
*/
protected function getUser(): ?UserInterface
{
return parent::getUser();
}
I'm a bit puzzled by some of the other answers so it might be that I am the one who is confused. The basic problem is that getUser is typehinted to return an UserInterface but the actual User object is needed. My solution is to explicitly tell the IDE what getUser is actually returning:
use App\Entity\User; # or whetever the application defined user is
class MyController extends AbstractController
{
public function someMethod()
{
/** #var User */
$user = $this->getUser();
And everyone, especially the IDE, is happy.
Once again you need to do what is basically a typecast because the security system really only cares about the UserInterface and does not worry about anything else the user might have. Hence we have:
class AbstractController {
protected function getUser(): ?UserInterface {
A bit off-topic but I am looking forward to the day when this bit of annotation is no longer needed and replace with something like:
User $user = $this->getUser();
You have many ways to get your current user, please try this one:
$creditCard= new creditCard();
$creditCard->setUser($this->getUser());
Or this one:
$creditCard= $this->get('security.token_storage')->getToken()->getUser();

Symfony 6 - Attempted to call an undefined method named "getDoctrine" [duplicate]

As my IDE points out, the AbstractController::getDoctrine() method is now deprecated.
I haven't found any reference for this deprecation neither in the official documentation nor in the Github changelog.
What is the new alternative or workaround for this shortcut?
As mentioned here:
Instead of using those shortcuts, inject the related services in the constructor or the controller methods.
You need to use dependency injection.
For a given controller, simply inject ManagerRegistry on the controller's constructor.
use Doctrine\Persistence\ManagerRegistry;
class SomeController {
public function __construct(private ManagerRegistry $doctrine) {}
public function someAction(Request $request) {
// access Doctrine
$this->doctrine;
}
}
You can use EntityManagerInterface $entityManager:
public function delete(Request $request, Test $test, EntityManagerInterface $entityManager): Response
{
if ($this->isCsrfTokenValid('delete'.$test->getId(), $request->request->get('_token'))) {
$entityManager->remove($test);
$entityManager->flush();
}
return $this->redirectToRoute('test_index', [], Response::HTTP_SEE_OTHER);
}
As per the answer of #yivi and as mentionned in the documentation, you can also follow the example below by injecting Doctrine\Persistence\ManagerRegistry directly in the method you want:
// src/Controller/ProductController.php
namespace App\Controller;
// ...
use App\Entity\Product;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\HttpFoundation\Response;
class ProductController extends AbstractController
{
/**
* #Route("/product", name="create_product")
*/
public function createProduct(ManagerRegistry $doctrine): Response
{
$entityManager = $doctrine->getManager();
$product = new Product();
$product->setName('Keyboard');
$product->setPrice(1999);
$product->setDescription('Ergonomic and stylish!');
// tell Doctrine you want to (eventually) save the Product (no queries yet)
$entityManager->persist($product);
// actually executes the queries (i.e. the INSERT query)
$entityManager->flush();
return new Response('Saved new product with id '.$product->getId());
}
}
Add code in controller, and not change logic the controller
<?php
//...
use Doctrine\Persistence\ManagerRegistry;
//...
class AlsoController extends AbstractController
{
public static function getSubscribedServices(): array
{
return array_merge(parent::getSubscribedServices(), [
'doctrine' => '?'.ManagerRegistry::class,
]);
}
protected function getDoctrine(): ManagerRegistry
{
if (!$this->container->has('doctrine')) {
throw new \LogicException('The DoctrineBundle is not registered in your application. Try running "composer require symfony/orm-pack".');
}
return $this->container->get('doctrine');
}
...
}
read more https://symfony.com/doc/current/service_container/service_subscribers_locators.html#including-services
In my case, relying on constructor- or method-based autowiring is not flexible enough.
I have a trait used by a number of Controllers that define their own autowiring. The trait provides a method that fetches some numbers from the database. I didn't want to tightly couple the trait's functionality with the controller's autowiring setup.
I created yet another trait that I can include anywhere I need to get access to Doctrine. The bonus part? It's still a legit autowiring approach:
<?php
namespace App\Controller;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Symfony\Contracts\Service\Attribute\Required;
trait EntityManagerTrait
{
protected readonly ManagerRegistry $managerRegistry;
#[Required]
public function setManagerRegistry(ManagerRegistry $managerRegistry): void
{
// #phpstan-ignore-next-line PHPStan complains that the readonly property is assigned outside of the constructor.
$this->managerRegistry = $managerRegistry;
}
protected function getDoctrine(?string $name = null, ?string $forClass = null): ObjectManager
{
if ($forClass) {
return $this->managerRegistry->getManagerForClass($forClass);
}
return $this->managerRegistry->getManager($name);
}
}
and then
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Entity\Foobar;
class SomeController extends AbstractController
{
use EntityManagerTrait
public function someAction()
{
$result = $this->getDoctrine()->getRepository(Foobar::class)->doSomething();
// ...
}
}
If you have multiple managers like I do, you can use the getDoctrine() arguments to fetch the right one too.

How to get the current logged User in a service

In Symfony 2.8/3.0, with our fancy new security components, how do I get the currently logged User (i.e. FOSUser) object in a service without injecting the whole container?
Is it even possible in a non-hacky way?
PS: Let's not consider the "pass it to the service function as a parameter" for being trivially obvious. Also, dirty.
Inject security.token_storage service into your service, and then use:
$this->token_storage->getToken()->getUser();
as described here: http://symfony.com/doc/current/book/security.html#retrieving-the-user-object and here: http://symfony.com/doc/current/book/service_container.html#referencing-injecting-services
Works with Symfony 3.4, 4.x, 5.x & above. The Security utility class was introduced in Symfony 3.4.
use Symfony\Component\Security\Core\Security;
public function indexAction(Security $security)
{
$user = $security->getUser();
}
https://symfony.com/doc/3.4/security.html#always-check-if-the-user-is-logged-in
Using constructor dependency injection, you can do it this way:
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class A
{
private $user;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->user = $tokenStorage->getToken()->getUser();
}
public function foo()
{
dump($this->user);
}
}
In symfo 4 :
use Symfony\Component\Security\Core\Security;
class ExampleService
{
private $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function someMethod()
{
$user = $this->security->getUser();
}
}
See doc : https://symfony.com/doc/current/security.html#retrieving-the-user-object
From Symfony 3.3, from a Controller only, according this blog post: https://symfony.com/blog/new-in-symfony-3-2-user-value-resolver-for-controllers
It's easy as:
use Symfony\Component\Security\Core\User\UserInterface
public function indexAction(UserInterface $user)
{...}
With Symfony 5.2+ and PHP 8.0+ you can also get the logged user using the #[CurrentUser] attribute
namespace App\Controller;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
class FooController extends AbstractController
{
public function index(#[CurrentUser] ?User $user)
{
// ...
}
}
Blog post: https://symfony.com/blog/new-in-symfony-5-2-controller-argument-attributes
Documentation: https://symfony.com/doc/current/security.html
Symfony does this in Symfony\Bundle\FrameworkBundle\ControllerControllerTrait
protected function getUser()
{
if (!$this->container->has('security.token_storage')) {
throw new \LogicException('The SecurityBundle is not registered in your application.');
}
if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
return $user;
}
So if you simply inject and replace security.token_storage, you're good to go.
if you class extend of Controller
$this->get('security.context')->getToken()->getUser();
Or, if you has access to container element..
$container = $this->configurationPool->getContainer();
$user = $container->get('security.context')->getToken()->getUser();
http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements

How to Get Current User inside Repository Symfony 2.7

All of my query in Entity Repository needs to be filtered by user.
Now I want to know how can I access the currently logged in user in Entity Repository directly.
What I did today is to get the currently logged in user in my controller, through the use of $this->getUser() and then pass it to Entity Repository and this is not efficient.
You need to inject security.token_storage service into another one to get the current user, but as of Repository classes belong to Doctrine project, not Symfony, it is not recommended to do this.. May be there is a way to achieve it by creating custom entityManager class as described here, but I don't think it would a good solution..
Instead of customizing an entityManager better create a service which calls repository classes' methods, inject desired services into it.. Let Repository classes do their job.
Implementation would be something like this:
RepositoryClass:
class MyRepository extends EntityRepository
{
public function fetchSomeDataByUser(UserInterface $user)
{
// query
}
}
Service:
class MyService
{
private $tokenStorage;
public function _construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
// other services
}
public function getSomeDataByUser()
{
$user = $this->tokenStorage->getToken()->getUser();
return $this->entityManager->getRepository(MyREPOSITORY)->fetchSomeDataByUser($user);
}
}
Usage:
public function someAction()
{
$dataByUser = $this->get(MYSERVICE)->getSomeDataByUser();
}
If you use JMSDiExtraBundle it can be done by adding setter injection:
use Doctrine\ORM\EntityRepository;
use JMS\DiExtraBundle\Annotation as DI;
class YourRepository extends EntityRepository
{
/** #var User current user entity */
protected $user;
/**
* #DI\InjectParams({
* "token_storage" = #DI\Inject("security.token_storage")
* })
*/
public function setSimplaManager(TokenStorageInterface $tokenStorage)
{
$token = $tokenStorage->getToken();
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
$this->user = $user;
}
}

allow only one connection on the same login with FOSUserBundle

I'm creating a website thanks to Symfony2 with FOSUserBundle.
I'm triyng to deny multiple connections on the same login (but from different computers for example).
I've 2 solutions :
Create an event listner on authentification but I didn't manage to make it. (even with the cookbook).
override the login_check method but my FOSUserBundle doesn't work if I do it.
Do you have any better options?
Or any solutions?
Got it finaly. There is just one last update to make to solve it all.
You need to add an other field to the User entity. sessionId (string).
Then update your LoginListener class like that :
// YourSite\UserBundle\Listener\YourSiteLoginListener.php
//...
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$request = $event->getRequest();
$session = $request->getSession();
$user = $event->getAuthenticationToken()->getUser();
$has_session = is_file ( '/path_to_your_php_session_file/'.'sess_'.$user->getSessionId() );
if($user->getLogged() && $has_session){
throw new AuthenticationException('this user is already logged');
}else{
$user->setLogged(true);
$user->setSessionId($session->getId());
$this->userManager->updateUser($user);
}
}
Maybe this will help people to solve this problem.
It's kind of a solution but there is still a problem :
If the user session is killed by php (after too mush time without action for example), you will have to go into your database to reset the "logged" value to 0.
So my solution is :
-add the field "logged" (boolean) to you User entity.
-in YourSite\UserBundle\Listener create a : YourSiteLoginListener.php with this code
namespace YourSite\UserBundle\Listener;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContext;
class YourSiteLoginListener
{
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if($user->getLogged()){
throw new AuthenticationException('this user is already logged');
}else{
$user->setLogged(true);
$this->userManager->updateUser($user);
}
}
}
-then in the same directory, create a logout handler : YourSiteLogoutHandler.php
namespace YourSite\UserBundle\Listener;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Logout\LogoutHandlerInterface;
class YourSiteLogoutHandler implements LogoutHandlerInterface
{
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function logout (Request $request, Response $response, TokenInterface $token){
$user = $token->getUser();
if($user->getLogged()){
$user->setLogged(false);
$this->userManager->updateUser($user);
}
}
}
-finaly declare those services in your app/config.yml for example:
services:
yoursite_login_listener:
class: YourSite\UserBundle\Listener\YourSiteLoginListener
arguments: [#fos_user.user_manager]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method :onSecurityInteractiveLogin }
yoursite_logout_handler:
class: YourSite\UserBundle\Listener\YourSiteLogoutHandler
arguments: [#fos_user.user_manager]
In Symfony3, the logout handler was not trigged by the code above.
I rebuild the code so the system is updated when the user is logging out.
namespace YourSite\UserBundle\Listener;
use FOS\UserBundle\Model\UserManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
class LogoutSuccessHandler implements LogoutSuccessHandlerInterface
{
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function onLogoutSuccess(Request $request){
global $kernel;
$user = $kernel->getContainer()->get('security.token_storage')->getToken()->getUser();
if($user->getLogged()){
$user->setLogged(false);
$this->userManager->updateUser($user);
}
$referer = $request->headers->get('referer');
return new RedirectResponse($referer);
}
}

Resources