symfony2 cookie created after page load - symfony

I am trying to execute and then display a cookie in my nav header template.
Currently my controller checks if a user is logged in. If not logged in then checks for a cookie. If no cookie exists a generic cookie is created. And then finally the template is loaded.
Currently the cookie is being created and template is loaded just fine, but the value passed when rendering the template does not show up after the page loads. I can see the cookie is created, and if I reload the page a 2nd time everything works as intended.
So I know this means the headers are not being sent as expected correct? But I cannot figure out the proper way to do this in Symfony2.
Here is my controller code:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Cookie;
class navController extends Controller {
public function displayNavAction() {
$userState = '';
$userZipcode = '';
if ($this->container->get('security.context')->getToken() != null) {
// To check if user is authenticated or anonymous
if ($this->container->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY') === true) {
$user = $this->getUser();
$userCity = $user->getCity();
$userState = $user->getState();
$userZipcode = $user->getZipcode();
} else {
//User is not logged in, so check for cookie.
$request = $this->get('request');
$cookies = $request->cookies;
if ($cookies->has('city'))
{
//User not logged in, but cookie exists. So use cookie.
$userCity = $cookies->get('city');
} else {
//User not logged in, and no existing cookie. So create cookie.
$cookieGuest = array(
'name' => 'city',
'value' => 'seattle',
'path' => '/',
'time' => time() + 3600 * 24 * 7,
false,
false
);
$cookie = new Cookie($cookieGuest['name'], $cookieGuest['value'], $cookieGuest['time'], $cookieGuest['path']);
$response = new Response();
$response->headers->setCookie($cookie);
$response->sendHeaders();
$userCity = $cookies->get('city');
}
}
}
return $this->render(
'trrsywxBundle:Default:nav.html.twig', array('city' => $userCity, 'state' => $userState, 'zipcode' => $userZipcode, 'tempjunk' => $userCity));
}
}

You are not using the response you created that's the problem try this :
$userCity = $cookies->get('city');
$response = new Response($this->render(
'trrsywxBundle:Default:nav.html.twig',array(
'city' => $userCity,
'state' => $userState,
'zipcode' => $userZipcode,
'tempjunk' => $userCity
)));
$cookie = new Cookie($cookieGuest['name'], $cookieGuest['value'],$cookieGuest['time'], $cookieGuest['path']);
$response->headers->setCookie($cookie);
$response->sendHeaders();
return $response;
then you can get cookie value in your twig using the request global {% app.request.cookies %}

Related

WP API Authentication can not set the cookie

i try to make API authentication with my WP app
i have write the add the below code to add new fields and upon login it send data to External API if it user exist in API return login or create new WP user if not just don't do anything or gives an error, but i have the issue with Cookie now and get the "Error: Cookies are blocked due to unexpected output."
here is my code:
add_action('login_form', 'businessId', 10, 1);
function businessId()
{
?>
<div class="businessid_wrap">
<label for="businessId">business ID</label>
<input type="text" id="businessId" name="businessId" value=""/>
</div>
<?php
}
function au_auth($user, $username, $password)
{
$endpoint = 'will be my API endpoint url';
// Makes sure there is an endpoint set as well as username and password
if (!$endpoint || $user !== null || (empty($username) && empty($password))) {
return false;
}
$auth_args = [
'method' => 'POST',
'headers' => [
'Content-type: application/json',
],
'sslverify' => false,
'body' => [
'businessId' => $_POST['businessId'],
'userLogin' => $username,
'userPassword' => $password,
],
];
$response = wp_remote_post($endpoint, $auth_args);
$body = json_decode($response['body'], true);
var_dump ($response);
if (!$response) {
// User does not exist, send back an error message
$user = new WP_Error('denied', __('<strong>Error</strong>: Your username or password are incorrect.'));
} elseif ($response) {
/for now i just dumping the return data to check
var_dump ($response);
}
remove_action('authenticate', 'wp_authenticate_username_password', 20);
return $user;
}
add_filter('authenticate', 'au_auth', 10, 3);

How to use Lock Component in Symfony3.4 with username

I am trying to use the lock component in symfony 3.4, like it is described on
https://symfony.com/doc/3.4/components/lock.html
I want to prevent multiple data changes from different users.
For example user1 is calling the same company form with data, then user2
How can I tell user2, that editing data is blocked by user1 (incl username) ?
UPDATE:
It is used in backend, where a lot of employees editing data of customers, order etc.
this form is just for editing. that means, if they want to update some data, they click "edit". They should be informed when another employee changes this record before the data is loaded into the form. It sometimes takes some time for the employee to change everything. If the employee receives a message when saving it, they have to go back,reload the data and start all over again.
an example out of my controller:
public function CompanyEdit(Request $request)
{
$error = null;
$company_id = $request->get('id');
// if (!preg_match('/^\d+$/', $company_id)){
// return $this->showError();
// }
$store = new SemaphoreStore();
$factory = new Factory($store);
$lock = $factory->createLock('company-edit-'.$company_id, 30);
if(!$lock->acquire()) {
//send output with username
// this data is locked by user xy
return 0;
}
$company = $this->getDoctrine()->getRepository(Company::class)->find($company_id);
$payment = $this->getDoctrine()->getRepository(Companypay::class)->findOneBy(array('company_id' => $company_id));
$form = $this->createFormBuilder()
->add('company', CompanyFormType::class, array(
'data_class' => 'AppBundle\Entity\Company',
'data' => $company
))
->add('payment', CompanyPayFormType::class, array(
'data_class' => 'AppBundle\Entity\CompanyPay',
'data' => $payment
))
->getForm();
$form->handleRequest($request);
$company = $form->get('company')->getData();
$payment = $form->get('payment')->getData();
if ($form->isSubmitted() && $form->isValid()) {
$event = new FormEvent($form, $request);
if ($payment->getCompanyId() == null) {
$payment->setCompanyId($company->getId());
}
try {
$this->getDoctrine()->getManager()->persist($company);
$this->getDoctrine()->getManager()->persist($payment);
$this->getDoctrine()->getManager()->flush();
$this->container->get('app.logging')->write('Kundendaten geändert', $company->getId());
} catch (PDOException $e) {
$error = $e->getMessage();
}
if (null === $response = $event->getResponse()) {
return $this->render('customer/edit.html.twig', [
'form' => $form->createView(),
'company' => $company,
'error' => $error,
'success' => true
]);
}
$lock->release();
return $response;
}
You can't (Locks can't have any metadata), but you probably don't want this in the first place.
In this case, you create a Lock when a user opens the edit page and release it when a user submits the form. But what if the users opens the page and doesn't submit the form? And why can't a user even view the form?
This looks like a XY-problem. I think you're trying to prevent users to overwrite data without knowing. Instead, you can add a timestamp or hash to the form that changes after changing the entity. For example:
<form type="hidden" name="updatedAt" value="{{ company.updatedAt()|date('U') }}" />
And in your form:
<?php
if ($company->getUpdatedAt()->format('U') !== $form->get('updatedAt')->getData()) {
throw new \LogicException('The entity has been changed after you opened the page');
}
Disclaimer: code is not tested and just as an example how this solution can look like.

Sonata Admin - Custom AJAX call

I have created a custom list view in sonata admin to display a calendar.
I'm trying to add events to the calendar dynamically, but I'm getting an error with the CSRF token being invalid.
I have the following code:
public function listAction()
{
if (false === $this->admin->isGranted('LIST')) {
throw new AccessDeniedException();
}
$datagrid = $this->admin->getDatagrid();
$formView = $datagrid->getForm()->createView();
// set the theme for the current Admin Form
$this->get('twig')->getExtension('form')->renderer->setTheme($formView, $this->admin->getFilterTheme());
$em = $this->getDoctrine()->getManager();
$events = $em->getRepository('BMCrmBundle:Event')->findAll();
$event = new Event();
$formEvent = $this->createForm(new EventType(), $event );
return $this->render($this->admin->getTemplate('list'), array(
'action' => 'list',
'form' => $formView,
'datagrid' => $datagrid,
'csrf_token' => $this->getCsrfToken('sonata.batch'),
'events' => $events,
'formEvent' => $formEvent->createView()
));
}
view
var url = "{{ path('create_event', { _sonata_admin: 'bm.crm.admin.event'} ) }}";
$.post(url, form.serialize(), function(data) {
alert(data);
});
This always returns that the CSRF token is invalid
Any ideas?
Check if in your view, you have the following line:
{{ form_rest(form) }}
because I believe that you are rendering form fields one by one and not the whole form at once and forgot to render the rest of the form, which contains the CSRF token.

get('security.context')->isGranted in functional test

I want do one functional test over on service Symfony2. The idea is call before to the controller and after that, load the service with the function. The function is this one:
function save($title,$description,$setId,$html,$validate,$articles){
$articles = explode(',', $articles);
if (false === $this->container->get('security.context')->isGranted('ROLE_USER')) {
throw new \Exception("Not allowed");
}else{
$profileId = $this->container->get('security.context')->getToken()->getUser()->getId();
$userName = $this->container->get('security.context')->getToken()->getUser()->getUserName();
}
}
and now my test code is :
$client = static::createClient();
$crawler = $client->request('GET','/sets/save',
array(
"title"=>"rtyui",
"description"=>"aksdjhashdkjahskjdh",
"set_id"=>"",
"html"=>"",
"validate"=>1,
"articels"=>"3,4"
)
);
but doesn't work already that I have this lines:
if (false === $this->container->get('security.context')->isGranted('ROLE_USER')) {
throw new \Exception("Not allowed");
Now, the question is, how i can do the validation process? I've tried to do this validation process as show the documentation:
$client = static::createClient(array(), array(
'PHP_AUTH_USER' => 'username',
'PHP_AUTH_PW' => 'pa$$word',
));
but I got the same error.
Also you can login user by Security Token:
$client = static::createClient();
$container = $client->getContainer();
$container->get('security.context')->setToken(
new UsernamePasswordToken(
$user, null, 'main', $user->getRoles()
)
);
Where:
$user - instance of User entity with role ROLE_USER,
main - your security provider name

Create a symfony2 remember me cookie manually (FOSUserBundle)

Could somebody explain how you can manually create a remember me cookie in a controller?
I want the users to stay logged in after they pressed the "register"
button, without having to login with their credentials afterwards.
I've tried to create a cookie manually but i'm guessing the cookie
value is incorrect, and therefor the "remember me" functionality
doesn't work.
A cookie with the correct name gets set. I've checked that.
The remember me functionality works as expected when using the normal
login procedure with the user's credentials.
security.yml
security.yml remember me
security:
firewalls:
main:
remember_me:
lifetime: 86400
domain: ~
path: /
key: myKey
This is what I have now, even though the cookie is set, it doesn't work.
$um = $this->get('fos_user.user_manager');
$member = $um->createUser();
… Form stuff with bindRequest etc.
$um->updatePassword($member);
$um->updateUser($member);
$providerKey = $this->container->getParameter('fos_user.firewall_name');
$securityKey = 'myKey';
$token = new RememberMeToken($member, $providerKey, $securityKey,
$member->getRoles());
$this->container->get('security.context')->setToken($token);
$redirectResponse = new RedirectResponse($url);
$redirectResponse->headers->setCookie(
new \Symfony\Component\HttpFoundation\Cookie(
'REMEMBERME',
base64_encode(implode(':', array($member->getUsername(),
$member->getPassword()))),
time() + 60*60*24
)
);
return $redirectResponse;
Update:
I've also tried working with the
PersistentTokenBasedRememberMeServices class with reflection but it does not work. a cookie gets set but it's not working
$token = $this->container->get('security.context')->getToken();
$providerKey = $this->container->getParameter('fos_user.firewall_name');
$securityKey = 'myKey';
$persistenService = new
PersistentTokenBasedRememberMeServices(array($um), $providerKey,
$securityKey, array('path' => '/', 'name' => 'REMEMBERME', 'domain' =>
null, 'secure' => false, 'httponly' => true,
'lifetime' => 86400));
$persistenService->setTokenProvider(new InMemoryTokenProvider());
$method = new \ReflectionMethod('Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices',
'onLoginSuccess');
$method->setAccessible(true);
$method->invoke($persistenService, $request, $redirectResponse, $token);
I'm using Symfony v2.0.5 and FOSUserBundle 1.0
UPDATE 2:
I've tried a 3rd way. The same as above but without reflection:
$token = $this->container->get('security.context')->getToken();
$providerKey = $this->container->getParameter('fos_user.firewall_name');
$securityKey = 'myKey';
$persistenService = new PersistentTokenBasedRememberMeServices(array($um), $providerKey, $securityKey, array('path' => '/', 'name' => 'REMEMBERME', 'domain' => null, 'secure' => false, 'httponly' => true, 'lifetime' => 31536000, 'always_remember_me' => true, 'remember_me_parameter' => '_remember_me'));
$persistenService->setTokenProvider(new InMemoryTokenProvider());
$persistenService->loginSuccess($request, $redirectResponse, $token);
Here is how I did it. I'm not using the FOSUserBundle and I'm using Doctrine Entity User Provider, but it should be trivial to adjust to your needs. Here is a general solution:
// after registration and persisting the user object to DB, I'm logging the user in automatically
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
// but you can also get the token directly, if you're user is already logged in
$token = $this->container->get('security.context')->getToken();
// write cookie for persistent session storing
$providerKey = 'main'; // defined in security.yml
$securityKey = 'MySecret'; // defined in security.yml
$userProvider = new EntityUserProvider($this->getDoctrine()->getEntityManager(), 'MyCompany\MyBundle\Entity\User', 'username');
$rememberMeService = new TokenBasedRememberMeServices(array($userProvider), $securityKey, $providerKey, array(
'path' => '/',
'name' => 'MyRememberMeCookie',
'domain' => null,
'secure' => false,
'httponly' => true,
'lifetime' => 1209600, // 14 days
'always_remember_me' => true,
'remember_me_parameter' => '_remember_me')
);
$response = new Response();
$rememberMeService->loginSuccess($request, $response, $token);
// further modify the response
// ........
return $response;
Just remember you have to set always_remember_me option to true (like I did in the code above) or have it in your $_POST parameters somehow, otherwise method isRememberMeRequested of AbstractRememberMeServices will return false and the cookie won't be stored.
You were pretty close to the correct solution though :) What you did wrong (in the 3rd attempt) is that you've changed the order of parameters here:
$persistenService = new PersistentTokenBasedRememberMeServices(array($um), $providerKey, $securityKey, array('path' => '/', 'name' => 'REMEMBERME', 'domain' => null, 'secure' => false, 'httponly' => true, 'lifetime' => 31536000, 'always_remember_me' => true, 'remember_me_parameter' => '_remember_me'));
Take a look at __construct() in AbstractRememberMeServices.php. You should pass a $securityKey as 2nd argument and $providerKey as 3rd argument, not the other way around like you did by mistake ;)
What I don't know yet, is how to get parameters from security.yml directly in the controller not to duplicate it. By using $this->container->getParameter() I can get parameters stored under parameters key in config.yml, but not the ones places higher in the configuration tree. Any thoughts on this?
If you are setting the rememberme cookie directly, you have to use the following format:
base64_encode(<classname>:base64_encode(<username>):<expiry-timestamp>:<hash>)
where the hash will be:
sha256(<classname> . <username> . <expiry-timestamp> . <password> . <key>)
the key is the key you have entered in your security(.xml/.yml) in the remember_me section.
This is taken from processAutoLoginCookie() method in the Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeService.php file.
This is all done by the generateCookieValue() method in the same class.
However, I would not recommend on using doing it this way directly, but try to see if you can call the TokenBasedRememberMeService::onLoginSuccess() method, which sets this cookie for you to make the code more robust and portable.
For me the easiest solution was extend a BaseTokenBasedRememberMeServices and let it handle
namespace AppBundke\Security\Http;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices as BaseTokenBasedRememberMeServices;
class TokenBasedRememberMeServices extends BaseTokenBasedRememberMeServices
{
protected $options_new = array('name' => 'REMEMBERME', 'domain' => null, 'path' => '/');
public function __construct($userProvider, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null)
{
return parent::__construct(array($userProvider), $secret, $providerKey, array_merge($this->options_new, $options));
}
public function generateCookie($user, $username, $expires, $password)
{
$cookie = new Cookie(
$this->options['name'],
parent::generateCookieValue(get_class($user), $username, $expires, $password),
$expires,
$this->options['path'],
$this->options['domain'],
$this->options['secure'],
$this->options['httponly']
);
return $cookie;
}
}
and in controller;
$user = $this->getUser();
$providerKey = $this->getParameter('fos_user.firewall_name');
$secret = $this->getParameter('secret');
$cookie_life_time = $this->getParameter('cookie_life_time');
$remember_me_service = new TokenBasedRememberMeServices($user, $secret, $providerKey );
$remember_me_cookie = $remember_me_service->generateCookie($user, $user->getUsername(),(time() + $cookie_life_time), $user->getPassword());
then response set cookie to $remember_me_cookie
I hope its works with you 2.
I had the same issue when I tried to set REMEMBERME cookie an User after a connection by token, using Guard Authentication.
In this situation I had no Response object to be able to use $response->headers->setCookie() and needs to use setcookie().
And in this situation, create a RedirectResponse is not appropriate.
This needs to be refactored but I post the raw procedural on which I based my service
$expires = time() + 2628000;
$hash = hash_hmac(
'sha256',
get_class($user).$user->getUsername().$expires.$user->getPassword(), 'secret in parameters.yml'
);
$value = base64_encode(implode(':', [get_class($user), base64_encode($user->getUsername()), $expires, $hash]));
setcookie(
'REMEMBERME',
$value,
$expires,
'/',
'host',
'ssl boolean',
true
);

Resources