FOSRestBundle & invalid form CRF token - symfony

I'm trying to implement FOSRestBundle and Symfony forms.
I have found this tutorial but I have problem with this part
private function processForm(PageInterface $page, array $parameters, $method = "PUT")
{
$form = $this->formFactory->create(new PageType(), $page, array('method' => $method));
$form->submit($parameters, 'PATCH' !== $method);
if ($form->isValid()) { //form is not valid.
$page = $form->getData();
$this->om->persist($page);
$this->om->flush($page);
return $page;
}
throw new InvalidFormException('Invalid submitted data', $form);
}
ERROR: The CSRF token is invalid. Please try to resubmit the form.
Here is the controller from tutorial. And here is my class controller:
public function newAction(Request $request)
{
$form = new EntryType();
$newEntry = $this->container->get('entries.entry.handler')->post(
$request->request->get($form->getName())
);
return View::create()
->setStatusCode(200)
->setFormat('json')
->setSerializationContext(SerializationContext::create()->setGroups(array('list')))
->setData($newEntry);
}
Should I skip checking isValid() or fix this somehow? How?
OK, It is clear now. CRF verification (csrf_protection) should be disabled
CSRF token is invalid when calling rest post api from php Client
https://github.com/liuggio/symfony2-rest-api-the-best-2013-way/issues/1#issuecomment-31435232
CSRF validation needed or not when using RESTful API?

From part 3 of the tutorial :
It's possible to disable the CSRF based on the user’s role.
# app/config/config.yml
fos_rest:
disable_csrf_role: ROLE_API
# you can also try
# disable_csrf_role: IS_AUTHENTICATED_FULLY
See also this issue.

Related

How programmatically login with fr3d_ldapbundle

I am using symfony 3.3 with fos_userbundle and fr3d_ldapbundle to authentichate my users trough LDAP.
The login works correctly if a try to use the standard login form generated.
But what I need to do is a manual(programmatically) login.
What is the best way to do it with fr3d_ldapbundle?
Sorry guys, I give you more details:
I tried to follow this guide: https://ourcodeworld.com/articles/read/459/how-to-authenticate-login-manually-an-user-in-a-controller-with-or-without-fosuserbundle-on-symfony-3
If I try to use the fos_user.user_manager the login works correctly, but using the fr3d_ldap.ldap_manager it doesn't work. (the isPasswordValid function return me "Username or Password not valid")
The user is retrieved correctly from LDAP server, but the "password" field is empty if I print the $user object. Using the standard login form the authentication works correctly and the username is stored in my fos user bundle table with the password field empty. Could be this my problem?
Also the $salt is empty.
This is my code of LoginAction:
public function loginAction(Request $request)
{
// This data is most likely to be retrieven from the Request object (from Form)
// But to make it easy to understand ...
$_username = "user";
$_password = "password";
// Retrieve the security encoder of symfony
$factory = $this->get('security.encoder_factory');
/// Start retrieve user
// Let's retrieve the user by its username:
/*
// If you are using FOSUserBundle:
$user_manager = $this->get('fos_user.user_manager');
$user = $user_manager->findUserByUsername($_username);
//Or by yourself
$user = $this->getDoctrine()->getManager()->getRepository("ApiBundle:User")
->findOneBy(array('username' => $_username));
*/
//Using fr3d/ldap-bundle
$user_manager = $this->get('fr3d_ldap.ldap_manager');
$user = $user_manager->findUserByUsername($_username);
//print_r($user);die();
/// End Retrieve user
// Check if the user exists !
if(!$user){
return new Response(
'Username doesnt exists',
Response::HTTP_UNAUTHORIZED,
array('Content-type' => 'application/json')
);
}
/// Start verification
$encoder = $factory->getEncoder($user);
$salt = $user->getSalt();
if(!$encoder->isPasswordValid($user->getPassword(), $_password, $salt)) {
return new Response(
'Username or Password not valid.',
Response::HTTP_UNAUTHORIZED,
array('Content-type' => 'application/json')
);
}
/// End Verification
// The password matches ! then proceed to set the user in session
//Handle getting or creating the user entity likely with a posted form
// The third parameter "main" can change according to the name of your firewall in security.yml
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.token_storage')->setToken($token);
// If the firewall name is not main, then the set value would be instead:
// $this->get('session')->set('_security_XXXFIREWALLNAMEXXX', serialize($token));
$this->get('session')->set('_security_main', serialize($token));
// Fire the login event manually
$event = new InteractiveLoginEvent($request, $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
/*
* Now the user is authenticated !!!!
* Do what you need to do now, like render a view, redirect to route etc.
*/
return new Response(
'Welcome '. $user->getUsername(),
Response::HTTP_OK,
array('Content-type' => 'application/json')
);
}
Someone is able to help me?
Thank you.
#Jon Doe, you certainly cannot get the password information on user object while doing ldap authentication.
LDAP uses bind function which takes username and password information, tries to authenticate and return success or failure.
While using FR3DLdapBundle, this should be done inside Authentication Provider. Check LdapAuthenticationProvider.php file for following code.
if (!$this->ldapManager->bind($currentUser, $presentedPassword)) {
throw new BadCredentialsException('The credentials were changed from another session.');
}
In your controller - LoginAction you shouldn't be doing any authentication.
Just check for any authentication error and check for any access specific role if you need to have role based access as following example.
// get the login error if there is one
$error = $this->get('security.authentication_utils')->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $this->get('security.authentication_utils')->getLastUsername();
//if you need to check for the user role.
$roleGranted = $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN');

How to get jwt token from controller (user already logged in)

So I'm using Lexik JWT bundle (Symfony 2.8) to authenticate over Google and when user is logging in it works well. My Success handler looks like this:
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$jwt = $this->jwtManager->create($user);
$response = new JsonResponse();
$event = new AuthenticationSuccessEvent(['token' => $jwt], $user, $response);
$this->dispatcher->dispatch(Events::AUTHENTICATION_SUCCESS, $event);
$redirectResponse = new RedirectResponse('http://localhost:3000?token='.$event->getData()['token']."&username=".$user->getUsername());
return $redirectResponse;
}
So I'm redirecting user to some localhost and passing token as "token" get variable and that works well. Later I can pass that token value trough header and I get authenticated.
Problem is - I want to get the same token from my controller. I'm using the similar code:
$jwtManager = $this->get('lexik_jwt_authentication.jwt_manager');
$tokenStorage = $this->get('security.token_storage');
$token = $tokenStorage->getToken();
$user = $token->getUser();
$jwt = $jwtManager->create($user);
$response = new JsonResponse();
$event = new AuthenticationSuccessEvent(['token' => $jwt], $user, $response);
$token = $event->getData()['token'];
echo $token;
And I really get some token, but that's not the same one I get from success handler. Tried passing it as header "Autorization" parameter, but it doesn't work. I'm getting 401 error and message:
Unable to verify the given JWT through the given configuration. If the \"lexik_jwt_authentication.encoder\" encryption options have been changed since your last authentication, please renew the token. If the problem persists, verify that the configured keys/passphrase are valid.
What I'm doing wrong here? Why I'm getting different token and how can I get token I'm getting form success handler?
Found the solution. It goes like:
$user = $this->get('security.token_storage')->getToken()->getUser();
$jwtManager = $this->get('lexik_jwt_authentication.jwt_manager');
$token = $jwtManager->create($user);
I know this is an old question, but I found a solution that let you use the token anywhere, not just in the controller.
Instead of using TokenInterface, use TokenStorageInterface
public function __construct(TokenStorageInterface $tokenStorage) {
$this->token = $tokenStorage->getToken();
$this->user = $this->token->getUser();
}

samlspbundle integration with fosuserbundle

I try to integrate the bundle samlspbundle on a project running with fosuserbundle.
I actually received information from my idp which send me the saml with the email address of the user.
What i'm trying to do is load the user from my table fosuser and then authenticate it.
this is the method i am in my model SamlToUser :
private function loadUserByTargetedID($targetedID)
{
$repository = $this->container->get('doctrine')->getManager()->getRepository('MCCAppBDDBundle:User');
$user = $repository->findOneBy(
array('email' => $targetedID)
);
if ($user) {
$userManager = $this->container->get('fos_user.user_manager');
$url = $this->container->get('router')->generate('homepage');
$response = new RedirectResponse($url);
$this->container->get('fos_user.security.login_manager')->loginUser(
$this->container->getParameter('fos_user.firewall_name'),
$user,
null
);
$userManager->updateUser($user);
return $user;
}
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException();
}
After that i have this error : PHP Warning: session_regenerate_id(): Cannot regenerate session id - headers already sent
I'm not sure is the right thing to do.
If you need other detail, i can give you.
Thanks to help.

Symfony2 Doctrine - Flushing in kernel.response listener flushs bad data

In order to do some logging for my Symfony2 app, I created a service that logs any connection, here is the method called on kernel.response :
public function log(FilterResponseEvent $event)
{
$log = new Log();
$request = $event->getRequest();
$response = $event->getResponse();
//fill the Log entity with stuff from request & response data
$manager = $this->container->get('doctrine.orm.entity_manager');
$manager->persist($log);
$manager->flush();
}
All of this seems fine, however when I execute a test like this one (patch with empty data to trigger a failure):
$this->client->request(
'PATCH',
'/users/testificate',
array(
'firstName' => '',
)
);
Which calls this action :
protected function processForm($item, $method = 'PATCH')
{
$form = $this->createForm(new $this->form(), $item, array('method' => $method));
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$response = new Response();
// Set the `Location` header only when creating new resources
if ($method == 'POST') {
$response->setStatusCode(201);
$response->headers->set('Location',
$this->generateUrl(
'get_' . strtolower($class), array('slug' => $item->getId()),
true // absolute
)
);
}
else {
$response->setStatusCode(204);
}
$this->em->flush();
return $response;
}
$this->em->detach($item);
return RestView::create($form, 400);
}
Although the test fails, the entity is patched, and of course it must not.
After some search what I've learnt is:
The parameters enter the form validator
The validation fails, thus returning a 400 http code without flushing the entity
However during the validation process, the entity gets hydrated with the invalid data
When the service is called on kernel.response, the $manager->flush(); flush all the data... including the bad data provided by the PATCH test.
What I've tried thus far:
1) Do a $manager->clear(); before $manager->persist(); ... doesn't change anything
2) Do a $manager->detach($item); if the form validation failed... doesn't change anything
Thanks !
I recently stumbled across problems with flushing in kernel.response when upgrading from Doctrine 2.3.4 to the latest 2.4 branch. Try flusing the log entities from kernel.terminate. Leave any modifications to the Response in kernel.response.

how to get the session variable in the view in symfony2

Thanks for your valuable suggestions
i have created a login system where i want to store the id's of users in session variables
this is my controller for login system
use Symfony\Component\HttpFoundation\Session\Session;
class successController extends Controller
{
public function successAction(Request $request)
{
--some code for form--
$repository = $em->getRepository('RepairStoreBundle:users');
$query = $repository->auth($name,$password);
$error="sorry invalid username or password";
if($query== false)
{
return $this->render('RepairLoginBundle:login:login.html.php', array(
'form' => $form->createView(),'error'=>$error,));
}
else
{
$role=$query[0]['role'];
$id=$query[0]['id'];
if($role == 1)
{
$session = new Session();
$session->start();
$session->set('id',$id);
$result=$repository->display();
return $this->render('RepairLoginBundle:login:success.html.php',array('result'=>$result,));
}
else
{
$session = new Session();
$session->start();
$session->set('id',$id);
$res= $repository->edit($id);
return $this->render('RepairLoginBundle:login:user.html.php',array('res'=>$res));
}
}
}
}
when admin logins with role=1 it will render to success.html.php
in this view how can i get the session variable which i have set in the controller.
i have used $session->get('id');
it is giving me server error please help with this
Upfront Authentication should better be done with the Security Component in Symfony2.
Read more about it in The Book - Security. You should probably also take a look at FOSUserBundle
Accessing the session from a PHP template in symfony2:
echo $app->getSession()->get('whatever');
Session Handling
There is an article in the official documentation:
Components/HttpFoundation - Session Data Management
The API documentation for the Session Component can be found here:
http://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Session.html
In the symfony2 standard-edition you can get the session from within a controller with:
$session = $this->getRequest()->getSession();
As you already have the request as an argument in successAction you could access the session with:
$session = $request->getSession();
Set a value with ( $value needs to be serializable ):
$session->set('key',$value);
Get a value with:
$session->get('key');
Saving (and closing) the session can be done with:
$session->save();
You should also loook at the SessionBag class.
you create a SessionBag and register it with the session. see:
Symfony API
In the registered SessionBag - which implements AttributeBagInterface - you can get and set your key/value's as desired.
TIP: If you want to get the current User and you have a container aware controller ( container injected )
you can use:
$user = $this->container->get('security.context')->getToken()->getUser();
if you are extending Symfony's Controller class in the standard-edition - the shorter way is:
$user = $this->get('security.context')->getToken()->getUser();
or even shorter (Symfony > 2.1.x):
$user = $this->getUser();
Alternative ( If your controller is not container aware ):
Define the controller as a service and inject #security.context:
YAML:
# src/Vendor/YourBundle/Resources/config/services.yml
services:
my.controller.service:
class: Vendor\YourBundle\Controller\successController
arguments: ["#security.context"]
Vendor\YourBundle\Controller\successController:
protected $securityContext;
public function __construct(SecurityContextInterface $securityContext)
{
$this->securityContext = $securityContext;
}
then in your action:
$user = $this->securityContext->getToken()->getUser();
Note:: you have to use the service in your routing aswell if you choose the controller-as-service variant. example routing.yml :
[...]
route_name:
pattern: /success
defaults: { _controller: my.controller.service:successAction }
[...]
[...]
Note... you can also inject the session with "#session"
# src/Vendor/YourBundle/Resources/config/services.yml
[...]
arguments: ["#security.context","#session"]
Note injecting the whole container is resource-heavy. advanced developers inject their needed services one-by-one and not the whole container.
Tip: Normally Controller classes are written with a capital first letter - example: *S*uccessController
General TIP: You have unnecessary dublicate code in your example:
// 'if' and 'else' execute the same stuff here
// result: dublicate code = more code = harder to read
if($role == 1)
{
$session = new Session();
$session->start();
[...]
}
else
{
$session = new Session();
$session->start();
[...]
}
should better be ...
// better: put this stuff before the if/else statement
$session = new Session();
$session->start();
if($role == 1)
{
[...]
}
else
{
[...]
}

Resources