How you can access to the context user (i.e.: $this->getUser()) from inside a repository?
Something like:
public function findInviteRequestByUser(\Fundacity\UserBundle\Entity\User $user)
{
$contextUser = $this->getUser();
...
// HERE GOES THE QUERY
...
return $query->getResult();
}
you should pass security context as an argument.
Your controller
$securityContext = $this->get('security.context');
$em->getRepository('YourBundle')->findInviteRequestByUser($securityContext);
Your entity repository
public function findInviteRequestByUser(\Symfony\Component\Security\Core\SecurityContext $context)
{
$user = $context->getToken()->getUser();
...
}
Related
My website is running Symfony 3.4 and I made my own user member system.
My User entity contains a Datetime field 'lastLogin' and I can't find a solution to update it every time a user logged in.
I created a custom UserChecker then I tried to update the field in it :
<?php
namespace CoreBundle\Security;
use CoreBundle\Entity\User as AppUser;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class UserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user)
{
if (!$user instanceof AppUser) {
return;
}
if ( $user->getDeleted() || !$user->getEnabled() )
{
throw new AuthenticationException();
}
else
{
// BELOW IS WHAT I TRY, BUT FAIL.
$entityManager = $this->get('doctrine')->getManager();
$user->setLastLogin(new \DateTime());
$entityManager->persist($user);
$entityManager->flush();
}
}
public function checkPostAuth(UserInterface $user)
{
if (!$user instanceof AppUser) {
return;
}
}
}
But it doesn't work. Maybe I can't use the doctrine entity manager in this file ?
If I use $this->get('doctrine')->getManager(); I get :
Fatal Error: Call to undefined method
CoreBundle\Security\UserChecker::get()
Dunno why #doncallisto removed his post. It was (IMHO) the right thing.
Take a look at http://symfony.com/doc/current/components/security/authentication.html#authentication-success-and-failure-events
So you have several options.
SecurityEvents::INTERACTIVE_LOGIN - triggers every time the user
full out the login form and submit credentials. Will work, but you
won't get last_login updates if you have remember_me cookie or similar
AuthenticationEvents::AUTHENTICATION_SUCCESS - triggers each time
(every request) when authentication was successful. It means your last_login will be updated each time on every request unless user logged out
so you'll need a EventSubscriber. Take a look at this article. https://thisdata.com/blog/subscribing-to-symfonys-security-events/
MAybe you'll need a simplified version.
public static function getSubscribedEvents()
{
return array(
// AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthenticationFailure', // no need for this at that moment
SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin', // this ist what you want
);
}
and then the onSecurityInteractiveLogin method itself.
public function onSecurityInteractiveLogin( InteractiveLoginEvent $event )
{
$user = $this->tokenStorage->getToken()->getUser();
if( $user instanceof User )
{
$user->setLastLogin( new \DateTime() );
$this->entityManager->flush();
}
}
P.S.
FosUserBundle uses interactive_login and a custom event to set last_login on entity
look at: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/EventListener/LastLoginListener.php#L63
Friend you can use this to inject the entityManager by a constructor
use Doctrine\ORM\EntityManagerInterface;
public function __construct(EntityManagerInterface $userManager){
$this->userManager = $userManager;
}
And in the checkPreAuth you call it
public function checkPreAuth(UserInterface $user){
if (!$user instanceof AppUser) {
return;
}
if ( $user->getDeleted() || !$user->getEnabled() ){
throw new AuthenticationException();
}else{
// BELOW IS WHAT I TRY, BUT FAIL.
$user->setLastLogin(new \DateTime());
$this->userManager->persist($user);
$this->userManager->flush();
}
}
I've put a registration form in a method so, that I can use it in different places.
My service registration controller looks like this:
public function loadRegisterForm()
{
$user = new User();
$form = $this->createForm(RegistrationType::class, $user);
$form->handleRequest($this->request);
$errors = "";
if ($form->isSubmitted())
{
if ($form->isValid())
{
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
$user->setIsActive(1);
$user->setLastname('none');
$user->setCountry('none');
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
}
else
{
$errors = $this->get('validator')->validate($form);
}
}
$parametersArray['form'] = $form;
$parametersArray['errors'] = $errors;
return $parametersArray;
}
services.yml looks like this:
register_form_service:
class: ImmoBundle\Controller\Security\RegistrationController
calls:
- [setContainer, ["#service_container"]]
And the main controller where I load the service controller:
private function indexAction()
{
/**
* Load register form
*/
$registerForm = $this->get('register_form_service');
$registerParameters = $registerForm->loadRegisterForm();
$registerParameters['form']->handleRequest($request);
return $this->render(
'ImmoBundle::Pages/mainPage.html.twig',
array(
'register_form' => $registerParameters['form']->createView(),
'errors' => $registerParameters['errors'],
)
);
}
The form itself is well rendered, so there is no problem. However nothing happens if I try to submit the form. I know that I should add the following line to the main controller
if ($registerParameters['form']->isSubmitted())
{
// add to db
}
But is there any way to do it only in a service controller?
You do not need a service definition to inject the container into your controller. If the controller extends Symfony\Bundle\FrameworkBundle\Controller\Controller all services are accesible via ->get(). Next to that, $form->isValid() already checks whether the form is submitted.
Why is your action private? It should be public, and it need to get the Request object as it's first parameter:
public function indexAction(Request $request)
{
$user = new User();
$form = $this->createForm(RegistrationType::class, $user);
$form->handleRequest($request);
if ($form->isValid()) {
// Do something here
}
}
See http://symfony.com/doc/current/book/forms.html#handling-form-submissions
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();
I use the sonata-admin bundle.
I have the relationship with the user (FOSUserBundle) in the PageEntity.
I want to save the current user which create or change a page.
My guess is get the user object in postUpdate and postPersist methods of the admin class and this object transmit in setUser method.
But how to realize this?
On the google's group I saw
public function setSecurityContext($securityContext) {
$this->securityContext = $securityContext;
}
public function getSecurityContext() {
return $this->securityContext;
}
public function prePersist($article) {
$user = $this->getSecurityContext()->getToken()->getUser();
$appunto->setOperatore($user->getUsername());
}
but this doesn't work
In the admin class you can get the current logged in user like this:
$this->getConfigurationPool()->getContainer()->get('security.token_storage')->getToken()->getUser()
EDIT based on feedback
And you are doing it this? Because this should work.
/**
* {#inheritdoc}
*/
public function prePersist($object)
{
$user = $this->getConfigurationPool()->getContainer()->get('security.token_storage')->getToken()->getUser();
$object->setUser($user);
}
/**
* {#inheritdoc}
*/
public function preUpdate($object)
{
$user = $this->getConfigurationPool()->getContainer()->get('security.token_storage')->getToken()->getUser();
$object->setUser($user);
}
Starting with symfony 2.8, you should use security.token_storage instead of security.context to retrieve the user. Use constructor injection to get it in your admin:
public function __construct(
$code,
$class,
$baseControllerName,
TokenStorageInterface $tokenStorage
) {
parent::__construct($code, $class, $baseControllerName);
$this->tokenStorage = $tokenStorage;
}
admin.yml :
arguments:
- ~
- Your\Entity
- ~
- '#security.token_storage'
then use $this->tokenStorage->getToken()->getUser() to get the current user.
I was dealing with this issue on the version 5.3.10 of symfony and 4.2 of sonata. The answer from greg0ire was really helpful, also this info from symfony docs, here is my approach:
In my case I was trying to set a custom query based on a property from User.
// ...
use Symfony\Component\Security\Core\Security;
final class YourClassAdmin extends from AbstractAdmin {
// ...
private $security;
public function __construct($code, $class, $baseControllerName, Security $security)
{
parent::__construct($code, $class, $baseControllerName);
// Avoid calling getUser() in the constructor: auth may not
// be complete yet. Instead, store the entire Security object.
$this->security = $security;
}
// customize the query used to generate the list
protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface
{
$query = parent::configureQuery($query);
$rootAlias = current($query->getRootAliases());
// ..
$user = $this->security->getUser();
// ...
return $query;
}
}
I'm trying to find a good way for handling my access controls in Symfony2.
My requirements:
90% of my application can only be accessed by authenticated users
in many controllers I need to check if the user is the owner
there are also some differences for different user roles
What I've done already:
installed JMSSecurityExtraBundle to check permissions via annotation
defined global ace's for my entity classes
I create an ace for the owner for every object during the create process
The check for owner and roles is no Problem. I only want to define in a global way that a user has to be authenticated and for exceptions (sites that can be accessed anonymous) I want to define it separated (best via annotations).
I don't want to do this via routing pattern.
I'm not sure it be what you're looking for, but did you try with Event Listener ?
You can make your verification in the onKernelController method. Then, you will can create different Interfaces and check the type of your controller in the listener.
class AceBuilderListener implements EventSubscriber{
private $container;
public function setContainer($container){
$his->container = $container;
}
public function getSubscribedEvents()
{
return array(
Events::prePersist,
Events::preUpdate,
Events::preRemove,
Events::postPersist,
Events::postUpdate,
Events::postRemove,
Events::loadClassMetadata,
);
}
public function prePersist(){ echo( get_class($entity) ); }
public function preUpdate(){ echo( get_class($entity) ); }
public function preRemove(){ echo( get_class($entity) ); }
public function postPersist(){ echo( get_class($entity) ); }
public function postUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entityManager = $args->getEntityManager();
echo get_class($entity);
// perhaps you only want to act on some "Product" entity
if ($entity instanceof Product | x) {
// ... do something with the Product
}
}
public function postRemove(){ die( get_class($entity) ); }
public function loadClassMetadata( LoadClassMetadataEventArgs $args ){
$classMetadata = $args->getClassMetadata();
$entityManager = $args->getEntityManager();
$user = $this->container->get('security.context')->getToken()->getUser();
// you can check here if isGranted();
// and get the entity from the object $classMetadata
$this->container->get('security.context')->isGranted('EDIT', $entity);
}
}