Symfony getting logged in user's id - symfony

I am developing an application using Symfony2 and doctrine 2. I would like to know how can I get the currently logged in user's Id.

Current Symfony versions (Symfony 4, Symfony >=3.2)
Since Symfony >=3.2 you can simply expect a UserInterface implementation to be injected to your controller action directly. You can then call getId() to retrieve user's identifier:
class DefaultController extends Controller
{
// when the user is mandatory (e.g. behind a firewall)
public function fooAction(UserInterface $user)
{
$userId = $user->getId();
}
// when the user is optional (e.g. can be anonymous)
public function barAction(UserInterface $user = null)
{
$userId = null !== $user ? $user->getId() : null;
}
}
You can still use the security token storage as in all Symfony versions since 2.6. For example, in your controller:
$user = $this->get('security.token_storage')->getToken()->getUser();
Note that the Controller::getUser() shortcut mentioned in the next part of this answer is no longer encouraged.
Legacy Symfony versions
The easiest way to access the user used to be to extend the base controller, and use the shortcut getUser() method:
$user = $this->getUser();
Since Symfony 2.6 you can retrieve a user from the security token storage:
$user = $this->get('security.token_storage')->getToken()->getUser();
Before Symfony 2.6, the token was accessible from the security context service instead:
$user = $this->get('security.context')->getToken()->getUser();
Note that the security context service is deprecated in Symfony 2 and was removed in Symfony 3.0.

In symfony2, we can get this simpler by this code:
$id = $this->getUser()->getId();

You can get the variable with the code below:
$userId = $this->get('security.context')->getToken()->getUser()->getId();

This can get dressed in the method:
/**
* Get user id
* #return integer $userId
*/
protected function getUserId()
{
$user = $this->get('security.context')->getToken()->getUser();
$userId = $user->getId();
return $userId;
}
And induction $this->getUserId()
public function example()
{
print_r($this->getUserId());
}

Related

How to get the currently logged in User in EasyAdmin

How can I fetch the currently logged in User from anywhere within the Backend code? For example I have an EventSubscriber class and want to fetch it from there.
How can I do that w/o the help of i.e. AbstractController?
Symfony AbstractController is the core of most Controllers. Including EasyAdmin crud controller (XXXCrudController) extends AbstractController so you can access the same methods.
One of those is getUser() which return the current logged in user.
* Get a user from the Security Token Storage.
*
* #return UserInterface|null
*
* #throws \LogicException If SecurityBundle is not available
*
* #see TokenInterface::getUser()
*/
protected function getUser()
{
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;
}
// #deprecated since 5.4, $user will always be a UserInterface instance
if (!\is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return null;
}
return $user;
}
So when trying to get the logged used in a controller, just use this method.
If you want to get the same thing, but for example in a service, you can basically do the same as what the method actually does by using the service injection with TokenStorageInterface to access the TokenStorage service which can get the current user.
So in your event subscriber, add TokenStorageInterface in your constructor to use it to first get the token and then your user. You may have to add another check to see if there is an user logged in (by checking if there is a token for example)
//YourService.php
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
private $tokenStorage
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function yourMethod()
{
//get token then user
$user = $tokenStorage->getToken()->getUser();
}

Simulating auth in tests for Symfony 4.4 with ZenstruckFoundry

In Symfony 4.4 I am attempting simulate authenticating a user so that I can write PHPUnit tests for a secured area of my application. I am using the ZenstruckFoundry to create a factory for my User that will be authenticated.
I have also followed the Symfony docs for Creating an Authentication Token and originally opened an issue in the zenstruck/foundry repo. Since this doesn't actually seem to be an issue with Foundry I'm asking here on SO.
My test to an endpoint behind my main firewall simply looks like this:
/**
* #test
*/
public function it_displays_the_dashboard()
{
$user = UserFactory::new()->createOne();
$this->auth($user);
$this->appClient->request('GET', '/'); // <- this requires an auth'd user
$this->assertResponseIsSuccessful();
$this->assertSelectorTextContains('[data-test="user-profile"]', $user->getUsername());
}
/**
* Simulate authentication
*
* #param Proxy $user
*/
protected function auth(Proxy $user)
{
$session = self::$container->get('session');
$firewallName = 'main';
// **********************************************
// **********************************************
// $user = $user->object(); <-- If I uncomment to use the actual User model then auth doesn't work
// **********************************************
// **********************************************
$token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles());
$session->set('_security_'.$firewallName, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->appClient->getCookieJar()->set($cookie);
}
The issue I'm encountering is this: If I pass the Proxy user to UsernamePasswordToken then the token is created successfully and the user is authenticated. However, this causes Twig to not be able to use its magic methods to resolve getter attributes. For example, my Twig template has {{ user.username }}, Behind the scenes Twig resolves this to the getUsername() method on my User instance. But since the Proxy class doesn't have such a method, I get this error:
Error: Call to undefined method App\Model\User::username()
If I modify my template to be {{ user.getUsername }} then my test passes.
But, if I pass the User class directly to UsernamePasswordToken using $user->object() then the token is initially created, but then seemingly doesn't exist by the time authentication takes place and my test fails because I'm not authenticated.
If I look at the stacktrace I can see that the User is no longer the User that was used when creating the token and I see the following:
/Users/myuser/code/adminapp/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php:84:
class Symfony\Component\Security\Core\Authentication\Token\AnonymousToken#847 (6) {
private $secret =>
string(7) "OxIzLMV"
private $user =>
string(5) "anon."
private $roles =>
array(0) {
}
private $roleNames =>
array(0) {
}
private $authenticated =>
bool(true)
private $attributes =>
array(0) {
}
}
I would like to be able to pass the actual User class using $user->object() so that I don't have to manipulate my Twig templates by using the getters directly.

Symfony 4 - Creation form - Object is null

Symfony 4.3
Custom users provider (no FOS)
PHP 7.1 / MariaDB 10.2
Wamp local server
I already made users edit and users delete functions. It work PERFECT !
Now I want to create a registration form in my website but I don't understand the error !
Return value of App\Entity\User::getFirstname() must be of the type string, null returned
The exception :
in \src/Entity/User.php (line 100)
/**
* #ORM\Column(type="string")
* #Assert\NotBlank()
*/
private $firstname;
public function getFirstname(): string
{
return $this->firstname; // THIS IS LINE 100
}
Below is an extract from my UserController :
/**
* #Route("/users/add", name="app_users_add")
*/
public function addUser(Request $request, EntityManagerInterface $em): Response
{
$user = new User();
$form = $this->createForm(UserType::class, $user); // This line generates the error
[...]
return $this->render('user/add.html.twig', [
'form' => $form->createView()
]);
}
I really don't understand.
My UserEntity is working with my edit method in controller.
MY template user/add.html.twig is good (even if I let it empty)
My UserType form builder work well (I use the same for editing users)
you should add
public function getFirstname(): ?string
return $this->firstname;
}
The reason $form = $this->createForm(UserType::class, $user); generates an error is because you are passing $user as data to the form UserType. (this is refering to a file App\Form\UserType, right?). Since $user is a new object, $firstname of this user is not yet defined, thus the error occurs when you are passing the data of $user to the form. Simply removing the $user argument will work.
If you do not have a file App\Form\UserType, you can either create it or use createFormBuilder() in your controller. See the documentation for more information.

how to get custom user class

Maybe the question is very simple but here it is :
I authenticated using my own User class and UserProvider class. I extended DefaultAuthenticationSuccessHandler to modify the connected user.It should happen in this method :
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
In my custom class User, I have a addRole method.
How can I reach this method from the token given as a parameter in the onAuthenticationSuccess function?
First of all, you need to retrieve the authenticated User :
$user = $token->getUser();
Then, you should be able to call $user->addRole().
But, you need to store the changes in db.
For that, you need to inject the doctrine EntityManager in your service.
Change your service declaration :
# services.yml
your_authentication_success_handler:
# ...
arguments:
entityManager: "#doctrine.orm.entity_manager"
Set the entityManager in the constructor of your service:
// Authentication success handler
public function __construct(\Doctrine\ORM\EntityManager $entityManager = null)
{
$this->_em = $entityManager;
}
Now you can update your User in the onAuthenticationSuccess method like follows:
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$user->addRole('YOUR_ROLE');
$this->_em->flush();
}
Update
Because the User returned is not an instance of your custom User entity, you have to retrieve it using the Repository of your entity.
use YourBundle\Entity\User;
// ...
$user = $token->getUser();
if (!($tokenUser instanceof User) {
$repository = $this->_em->getRepository('YourBundle:User');
$user = $repository->findBy(array(
'username' => $tokenUser->getUsername(), // Assuming the 'username' property is unique
));
}
$user->addRole('YOUR_ROLE'); // Now you can access the method
$this->_em->flush();

FOSUserBundle - PHPUnit - Mock a user

I am using Symfony with the FOSUserBundle and now I like to test some things like:
Doctrine lifecycle
Controller behind firewall
For those tests I need to be a specific user or at least in a user group.
How do I mock a user session so that ...
The lifecycle field like "createdAt" will use the logged in user
The Controller act like some mocked user is logged in
Example:
class FooTest extends ... {
function setUp() {
$user = $this->getMock('User', ['getId', 'getName']);
$someWhereGlobal->user = $user;
// after this you should be logged in as a mocked user
// all operations should run using this user.
}
}
You can do this with LiipFunctionalTestBundle. Once you have installed and configured the Bundle, creating and user and log in in tests is easy.
Create a fixture for your user
This creates a user which will be loaded during tests:
<?php
// Filename: DataFixtures/ORM/LoadUserData.php
namespace Acme\MyBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Acme\MyBundle\Entity\User;
class LoadUserData extends AbstractFixture implements FixtureInterface
{
public function load(ObjectManager $manager)
{
$user = new User();
$user
->setId(1)
->setName('foo bar')
->setEmail('foo#bar.com')
->setPassword('12341234')
->setAlgorithm('plaintext')
->setEnabled(true)
->setConfirmationToken(null)
;
$manager->persist($user);
$manager->flush();
// Create a reference for this user.
$this->addReference('user', $user);
}
}
If you want to use groups of users, you can see the official documentation.
Log in as this user in your test
As explained in LiipFunctionalTestBundle's documentation, here is how to load the user in the database and log in as this user:
/**
* Log in as the user defined in the Data Fixture.
*/
public function testWithUserLoggedIn()
{
$fixtures = $this->loadFixtures(array(
'Acme\MyBundle\DataFixtures\ORM\LoadUserData',
));
$repository = $fixtures->getReferenceRepository();
// Get the user from its reference.
$user = $repository->getReference('user')
// You can perform operations on this user.
// ...
// And perform functional tests:
// Create a new Client which will be logged in.
$this->loginAs($user, 'YOUR_FIREWALL_NAME');
$this->client = static::makeClient();
// The user is logged in: do whatever you want.
$path = '/';
$crawler = $this->client->request('GET', $path);
}
What I would do in this case is to create a CustomWebTestCase which extends the Symfony WebTestCase. In the class I would create a method which does the authentication for me.
Here is an example code:
namespace Company\MyBundle\Classes;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\User\User;
abstract class CustomWebTestCase extends WebTestCase
{
/**
* #param array|null $roles
* #return \Symfony\Bundle\FrameworkBundle\Client
*/
protected static function createAuthenticatedClient(array $roles = null) {
// Assign default user roles if no roles have been passed.
if($roles == null) {
$role = new Role('ROLE_SUPER_ADMIN');
$roles = array($role);
} else {
$tmpRoles = array();
foreach($roles as $role)
{
$role = new Role($role, $role);
$tmpRoles[] = $role;
}
$roles = $tmpRoles;
}
$user = new User('test_super_admin', 'passwd', $roles);
return self::createAuthentication(static::createClient(), $user);
}
private static function createAuthentication(Client $client, User $user) {
// Read below regarding config_test.yml!
$session = $client->getContainer()->get('session');
// Authenticate
$firewall = 'user_area'; // This MUST MATCH the name in your security.firewalls.->user_area<-
$token = new UsernamePasswordToken($user, null, $firewall, $user->getRoles());
$session->set('_security_'.$firewall, serialize($token));
$session->save();
// Save authentication
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie);
return $client;
}
}
The code above will directly create a valid user session and will skip the firewall entirely. Therefore you can create whatever $user you want and it will still be valid. The important part of the code is located in the method createAuthentication. This is what does the authentication magic.
One more thing worth mentioning - make sure you have set framework.session.storage_id to session.storage.mock_file in your config_test.yml so that Symfony will automatically mock sessions instead of you having to deal with that in each test case:
framework:
session:
storage_id: session.storage.mock_file
Now in your test case you would simply extend MyWebTestCase and call the createAuthenticatedClient() method:
class MyTest extends CustomWebTestCase {
public function testSomething() {
//Create authoried and unauthorized clients.
$authenticatedClient = self::createAuthenticatedClient(array("ROLE_SUPER_ADMIN"));
$unauthorizedClient = self::createAuthenticatedClient(array("ROLE_INSUFFICIENT_PERMISSIONS"));
// Check if the page behaves properly when the user doesn't have necessary role(s).
$unauthorizedClient->request('GET', '/secured-page');
$response = $unauthorizedClient->getResponse();
$this->assertFalse($response->isSuccessful());
$this->assertEquals(403, $response->getStatusCode(), "This request should have failed!");
// Check if the page behaves properly when the user HAS the necessary role(s)
$authenticatedClient->request('GET', '/secured-page');
$response = $authenticatedClient->getResponse();
$this->assertTrue($response->isSuccessful());
$this->assertEquals(200, $response->getStatusCode(), "This request should be working!");
}
}
You can see an example in the Symfony official documentation as well.
You can easily do that with LiipFunctionalTestBundle which authorize you lot of shortcut for create Unit Test.
If already you have a form user for create or edit you can use this for your test unit workflow user in your application :
use the makeClient method for logging test
$credentials = array(
'username' => 'a valid username',
'password' => 'a valid password'
);
$client = static::makeClient($credentials);
use your form for test your creation
$crawler = $client->request('GET', '/profile');
$form = $crawler->selectButton('adding')->form();
$form['fos_user_profile_form[firstName]'] = 'Toto';
$form['fos_user_profile_form[lastName]'] = 'Tata';
$form['fos_user_profile_form[username]'] = 'dfgdgdgdgf';
$form['fos_user_profile_form[email]'] = 'testfgdf#grgreger.fr';
$form['fos_user_profile_form[current_password]'] = 'gfgfgdgpk5dfgddf';
testing "createdAt" with just call findOneBy in repository user like this
$user = $this->getObjectManager()
->getRepository('AcmeSecurityBundle:User')
->findOneBy(array('username' => 'testCreateUserUsername'));
$this->assertTrue($user->getCreatedAt() == now());

Resources