Pretty way to send cookie? - symfony

$cookie = new Cookie('my-cookie','cookie');
$response->headers->setCookie($cookie);
$response->send();
This code causes problems in case to use redirect
return $this->redirect($this->generateUrl('my_route');
or use SensioFrameworkExtraBundle
/**
* #Template()
*/
public function showAction()
{
return array('entities' => $entities);
}

By following the API doc, $this->redirect Create a new RedirectResponse object so your prepared response with cookie is not used anymore.
You need to create a new RedirectResponse object, set the cookie and return it.
You can do something like that.
$response = new RedirectResponse();
$cookie = new Cookie('my-cookie','cookie');
$response->headers->setCookie($cookie);
return $response
Same thing for return array([...]) that will create a new Response object too.

Related

Twilio Symfony - The controller must return a \"Symfony\\Component\\HttpFoundation\\Response\" but returned Twilio\\TwiML\\VoiceResponse."

I want to implement Twilio browser to browser call with Symfony5 and ApiPlatform
I'm following this tuto:
https://www.twilio.com/docs/voice/client/tutorials/calls-between-devices?code-sample=code-generate-twiml-from-client-parameters-3&code-language=PHP&code-sdk-version=5.x
I have this function, that's the one I want my TwiML app to be configured on
/**
* #Route("/twilio/handle/twiml/{clientId}", name="twilio_handl_twiml")
* #param $clientId
* #return VoiceResponse
*/
public function handleTwiml($clientId): VoiceResponse
{
/** #var Client $client */
$client = $this->clientRepository->findOneBy(['id' => 11]);
$to = $client->getUser()->getLastName().$client->getUser()->getId();
$voiceResponse = new VoiceResponse();
$number = htmlspecialchars($to);
$dial = $voiceResponse->dial(null, array('callerId' => '+15017122661'));
if (isset($to)) {
if (preg_match("/^[\d\+\-\(\) ]+$/", $number)) {
$dial->number($number);
} else {
$dial->client($number);
}
} else {
$voiceResponse->say('There has been an issue. Thanks for calling!');
}
return $voiceResponse;
}
And I've declared it as a custom route on one of my entities in the "get" section:
* "twilio_handl_twiml"={
* "path"="/twilio/handle/twiml/{clientId}",
* "controller"="TwilioController:class"
* },
Now the function creates a proper VoiceResponse object
But when I call this route I get the following error message:
The controller must return a "Symfony\Component\HttpFoundation\Response" object but it returned an object of type Twilio\TwiML\VoiceResponse.
Now does anyone know why I couldn't return whatever kind of Response I want from a custom route ?
I don't really see why the framework would declare this as an error
If anyone can help me understand better this error I'd appreciate it
Thanks!
Twilio developer evangelist here.
As #Cerad has said in the comments, you need to respond with an object derived from the Symfony Response object.
I haven't used Symfony, so please excuse me if this is wrong, but I think you can update your handler to the following, it might work:
use Symfony\Component\HttpFoundation\Response;
/**
* #Route("/twilio/handle/twiml/{clientId}", name="twilio_handl_twiml")
* #param $clientId
* #return Response
*/
public function handleTwiml($clientId): VoiceResponse
{
/** #var Client $client */
$client = $this->clientRepository->findOneBy(['id' => 11]);
$to = $client->getUser()->getLastName().$client->getUser()->getId();
$voiceResponse = new VoiceResponse();
$number = htmlspecialchars($to);
$dial = $voiceResponse->dial(null, array('callerId' => '+15017122661'));
if (isset($to)) {
if (preg_match("/^[\d\+\-\(\) ]+$/", $number)) {
$dial->number($number);
} else {
$dial->client($number);
}
} else {
$voiceResponse->say('There has been an issue. Thanks for calling!');
}
$response = new Response(
$voiceResponse->asXML(),
Response::HTTP_OK,
['content-type' => 'application/xml']
);
return $response;
}
The key here is to build up the Symfony response with the content of the voice response ($voiceResponse->asXML()) and also set the content type to application/xml.

Login with custom authentication

I want a login with a custom field to authenticate users into the platform.
The point is to check a field 'pw_expires_at' to \DateTime('now'), to log the user.
Here's what I did so far:
In the controller:
$user->setPassword(
$passwordEncoder->encodePassword(
$user,
$mdp)
);
$user->setPwExpiresAt(new \DateTime("now + 1 minute"));
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
In the Authenticator:
public function checkCredentials($credentials, UserInterface $user)
{
$valid = false;
$validDate = $this->checkDate($credentials, $user);
$validPassword = $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
if($validDate && $validPassword) {
$valid = true;
}
return $valid;
}
/**
* #return bool
*/
public function checkDate($credentials, UserInterface $user){
$now = new \DateTime('now');
$pwdate = new \DateTime();
$pwdate = $this->entityManager->getRepository(Users::class)->findOneBy([
'email' => $credentials['email']
]);
if ($pwdate > $now) {
return false;
}
else {
return true;
}
}
I also added the new function checkDate() in the AuthenticatorInterface.php.
The problem is : I can log in at anytime.
You are comparing (>) a user object repository->findBy(...) which returns a Users::class with a DateTime object $now = new \DateTime();.
Also the $user object entityManager reponse is most likely the same object returned by your getUsername function (the one you pass as an argument in this function) and thus can be skipped? If it is a DTO that does not contain this expired value then add it back in.
Also you are not using the credentials for anything anymore then so removed it as well.
I would change this to something like:
public function checkDate(UserInterface $user) {
$now = new \DateTime();
$pwdate = $user->getPwExpiresAt();
// we dont need the if/else as this ($pwdate > $now)
// is an expression and will already return true/false;
return $pwdate > $now;
}
Some more suggestions:
You might want to reconsider renaming the function to something more expressive like $this->hasAuthenticationExpired($user) this should give a clear indication of what the function is doing other than "checking a date (for what?!)" without reading through the function.
You can move this function to the user object like
public function hasExpired() { return $this->getPwExpiresAt() && new \DateTime() > $this->getPwExpiresAt(); }
and just call if (!$user->hasExpired()) { which is actually a preferred way for many people as this can be easily reused and accessed whenever handling the user object anywhere.

symfony test - authenticated doesnt work on new request

Good morning.
Tldr: I think that, I need for Symfony Test Client something similiar to js xhr settings: withcredentals:true.
Symfony 3.1. I have action in rest api controller:
/**
* #Rest\GET("/get-stuff")
* #Rest\View
* #Security("is_granted('IS_AUTHENTICATED_FULLY')")
*/
public function GetStuffAction($userId)
{
// get userId from session
$userId = $this->getUser()->getId();
$em = $this->getDoctrine()->getManager();
$Stuff = $em->getRepository('MyApiBundle:Stuff')->findBy(['user' => $userId]);
return $this->viewGroup($Stuff, ['stuff']);
}
And it does work correct.
Now I want Test, so I have :
class TestCase extends WebTestCase
{
public function testIndex()
{
$this->client = self::createClient();
$this->storage = new MockFileSessionStorage(__dir__.'/../../../../app/cache/test/sessions');
$this->session = new Session($this->storage);
$this->logIn($this->getUser(), new Response());
$this->client->request('GET', '/get-stuff');
$content = $this->client->getResponse()->getContent();
$this->assertEquals({"message":"Full authentication is required to access this resource.","code":0},$content);
}
public function logIn(User $user, Response $response)
{
$this->session->start();
$this->cookie = new Cookie('MOCKSESSID', $this->storage->getId());
$this->cookieJar = new CookieJar();
$this->cookieJar->set($this->cookie);
$this->token = new UsernamePasswordToken($user, 'user', 'main', $user->getRoles());
$this->session->set('_security_main', serialize($this->token));
$this->getSecurityManager()->loginUser(
$this->container->getParameter('fos_user.firewall_name'),
$user,
$response
);
$this->session->save();
}
}
And test for it gives me message: "Full authentication is required to access this resource". After method logIn() I can read session data which are connected with logged user.
How can I make to be logged during this part to not receive message about authentication?:
$this->client->request('GET', '/get-stuff');
$content = $this->client->getResponse()->getContent();
More details:
1. My test class extends WebTestCase.
2. My user is overwritten for FosUserBundle.
I suppose that is something like in javascript:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
but I don't know what.
Thank you in advance for helping solve my problem!
Ok, I was good way to do this:
public function logIn(User $user) {
$session = $this->session;
$firewallContext = 'secured_area';
$token = new UsernamePasswordToken($user, 'user', 'main', $user->getRoles());
$session->set('_security_'.$firewallContext, serialize($token));
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$this->client->getCookieJar()->set($cookie);
}

How can i get the user with id session in param request with symfony2

I'm trying to do a little API with Symfony2.
I send a session id to my controller with a URL like this:
localhost/symfony2/web/app_dev.php/users/getuser/c5auv7mrp45rnd046cfv0vgl96
Then, in Symfony,
/**
* #Route("/getuser/{sessionId}")
*/
public function getSessionAction(Request $request, $sessionId)
{
// Here is what i'm trying to do
$packJson = array(
'user_id' => $userid
);
$response = new JsonResponse();
$response->setData($packJson);
return $response;
}
So, i would like to retrieve my user Id only with the sessionId argument.
Of course, it will be load from Db
I don't understand the logic between Session object and User Objet
Thanks
I think you want to use a token to identify a user. That means you have one token for each user in your database. If that is correct then it has nothing to do with sessions or a session-object.
you could simple retrieve your user with:
/**
* #Route("/getuser/{token}")
*/
public function getSessionAction($token)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AdminBundle:User')->findOneBy(array('token' => $token);
$response = new JsonResponse();
if (!$entity) {
$response->setData('error' => 'bad token');
return $response;
}
$packJson = array(
'user_id' => $entity->getId()
);
$response->setData($packJson);
return $response;
}

How to use mock entitymanager that returns fake repository in controller?

In my test I'm trying to mock the entity manager so it'll return a repository that will not connect to the database but instead return a fake value:
In the test according to this documentation:
$session = new Session(new MockArraySessionStorage());
$mockManager = $this
->getMockBuilder('\Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
->getMock();
$mockManager->expects($this->any())
->method('getRepository')
->will($this->returnValue(new userRepo()));
$client = static::createClient();
$container = $client->getContainer();
$container->set('session', $session);
$container->set('doctrine.orm.entity_manager',$mockManager);
$client->request('POST', '/secured/login'
,array('userName'=>'username','password'=>'password'
,'rememberMe'=>'on'));
$response = $client->getResponse();
//....
In test, the userRepo:
class userRepo {
public function isValidUser($userName, $password) {
echo "this is isvaliduser";
return $this->getFullUserById(22);
}
public function getFullUserById($id){
echo "this is getfulluserbyid";
return ["name"=>"someName"];
}
}
In the controller:
public function loginAction(Request $request) {
$userRepo = $this->getDoctrine()->getManager()
->getRepository('mytestBundle:User');
$user=$userRepo->isValidUser($userName,$password);
$response = new Response();
//... other code using session and whatnot
$response->headers->set("Content-Type", 'application/json');
$response->setContent(json_encode($user));
return $response;
}
The fake repository is never used as the echo doesn't show up when I run the test.
Up until creating the mock I think it's working as it should but setting the mock may be the problem $container->set('doctrine.orm.entity_manager',$mockManager); as the controller when calling $this->getDoctrine()->getManager() gets the actual entity manager and not the mock one.
Hmm, every time I spend a lot of time trying to figure stuff out; right after I descide to post a question the answer shows itself in yet another google search and trying that:
Solution was:
$container->set('doctrine.orm.default_entity_manager', $mockManager);
$container->set('doctrine.orm.entity_manager',$mockManager);

Resources