Test roles access to controllers in symfony - symfony

I'm asking myself how to tests with phpunit in symfony roles access.
For example, if i have an indexAction and 5 different roles in my security config, i want to be sure that user A will have a 401, user B a 403, user C a 500...
But it cause an issue: tests are really long to execute, because we have 5 functional tests by action.
Now, i'm doing that kind of thing:
/**
* #covers \App\Bundle\FrontBundle\Controller\DefaultController::indexAction()
*
* #dataProvider rolesAllAccess
*
* #param string $user
* #param integer $expectedCode
*
* #return void
*/
public function testRolesIndexAction($user, $expectedCode)
{
$client = $this->createClientWith($user);
$client->request('GET', '/');
$this->assertEquals($expectedCode, $client->getResponse()->getStatusCode());
}
The function createClientWith authenticate a client that i have defined in my dataProvider before. It makes exactly what i described before.
Do you have any idea on how doing that better or - at least - with better performances ?
Thanks!

Depends on your authentication method. I use JWT. Also, all my web tests extends ApiTestCase that extends WebTestCase. And In all WebTestCases I use a logged user. Logged use log in inside the setup method.
abstract class ApiTestCase extends WebTestCase
{
protected function setUp()
{
$client = static::makeClient();
$client->request(
'POST',
'/tokens', [
'username' => 'username',
'password' => 'password'
], [
// no files here
],
$headers = [
'HTTP_CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'HTTP_ACCEPT' => 'application/json',
]
);
$response = $client->getResponse();
$data = json_decode($response->getContent(), true);
$this->client = static::createClient(
array(),
array(
'HTTP_Authorization' => sprintf('%s %s', 'Bearer', $data['token']),
'HTTP_CONTENT_TYPE' => 'application/json',
'HTTP_ACCEPT' => 'application/json',
)
);
}
}
And here an example of test:
class DivisionControllerTest extends ApiTestCase
{
public function testList()
{
$this->client->request('GET', '/resource');
$response = $this->client->getResponse();
$expectedContent = ' !!! put expected content here !!! ';
$this->assertEquals(
$expectedContent,
$response->getContent()
);
}
}
Your test could be
public function testRolesIndexAction($expectedCode)
{
$this->client->request('GET', '/');
$this->assertEquals($expectedCode, $this->client->getResponse()->getStatusCode());
}

Try to use this to call one time $this->createClientWith. I suggest to look here for more recommandation.

Related

PDF document creation EasyAdmin symfony 5

I have this configuration which allows me to create a pdf document in the CRUD, is there a way to add this code in the CRUD easyAdmin or link the CRUD of my EasyAdmin documentos to the CRUD of symfony.
I have problems creating the document in the EasyAdmin table
DocumentController.php
<?php
namespace App\Controller;
use App\Entity\Document;
use App\Form\DocumentType;
use App\Repository\DocumentRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use App\Service\FileUploader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
/**
* #IsGranted("ROLE_USER")
* #Route("/documents")
*/
class DocumentController extends AbstractController
{
/**
* #Route("/", name="document_index", methods={"GET"})
*/
public function index(DocumentRepository $documentRepository): Response
{
return $this->render('document/index.html.twig', [
'documents' => $documentRepository->findAll([], ['created_at' => 'desc']),
]);
}
/**
* #Route("/new", name="document_new", methods={"GET","POST"})
*/
public function new(Request $request, FileUploader $fileUploader): Response
{
$document = new Document();
$document->setCreatedAt(new \DateTime('now'));
$form = $this->createForm(DocumentType::class, $document);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$file = $form['fileDocument']->getData();
$originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
// this is needed to safely include the file name as part of the URL
$fileName = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename);
$fileName = md5(uniqid()) . '.' . $file->guessExtension();
$file->move(
$this->getParameter('brochures_directory'),
$fileName
);
$document->setFileDocument($fileName);
$entityManager->persist($document);
$entityManager->flush();
return $this->redirectToRoute('document_index', array('id' => $document->getId()));
}
return $this->render('document/new.html.twig', [
// 'document' => $document,
'form' => $form->createView(),
]);
}
/**
* #Route("/{id}", name="document_show", methods={"GET"})
*/
public function show(Document $document): Response
{
return $this->render('document/show.html.twig', [
'document' => $document,
]);
}
/**
* #Route("/{id}/edit", name="document_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Document $document): Response
{
$form = $this->createForm(DocumentType::class, $document);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$file = $form['fileDocument']->getData();
$originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
// this is needed to safely include the file name as part of the URL
$fileName = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename);
$fileName = md5(uniqid()) . '.' . $file->guessExtension();
$file->move(
$this->getParameter('brochures_directory'),
$fileName
);
$document->setFileDocument($fileName);
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('document_index');
}
return $this->render('document/edit.html.twig', [
'document' => $document,
'form' => $form->createView(),
]);
}
/**
* #Route("/{id}", name="document_delete", methods={"DELETE"})
*/
public function delete(Request $request, Document $document): Response
{
if ($this->isCsrfTokenValid('delete' . $document->getId(), $request->request->get('_token'))) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($document);
$entityManager->flush();
}
return $this->redirectToRoute('document_index');
}
}
DocumentCrudController Easy Admin
<?php
namespace App\Controller\Admin;
use App\Entity\Document;
use App\Entity\Publication;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Validator\Constraints\File;
class DocumentCrudController extends AbstractCrudController
{
public static function getEntityFqcn(): string
{
return Document::class;
}
public function configureCrud(Crud $crud): Crud
{
return $crud
->setPageTitle(Crud::PAGE_INDEX, 'Liste de Documents')
;
}
public function configureFields(string $pageName): iterable
{
ImageField::new('fileDocument', 'Document PDF')->setFormType(FileType::class)
->setBasePath('docs');
return [
IdField::new('id')->onlyOnIndex(),
TextField::new('nomDocument', 'Titre'),
DateTimeField::new('created_at', 'Date de création'),
TextField::new('fileDocument', 'Document PDF')
->hideOnIndex()
->setFormType(FileType::class, [
'constraints' => [
new File([
'maxSize' => '1024k',
'mimeTypes' => [
'application/pdf',
'application/x-pdf',
],
'mimeTypesMessage' => 'Veuillez télécharger un document PDF valide',
])
],
]),
];
}
public function configureActions(Actions $actions): Actions
{
return $actions
->add(Crud::PAGE_INDEX, Action::DETAIL);
}
}
I don't know how I can implement the same configuration in easy admin.
Look Here this is what happens when i create a document from table EasyAdmin.
Thank you.
That's a little weird but you need to use the ImageField (https://symfony.com/bundles/EasyAdminBundle/current/fields/ImageField.html)
And in you CrudController:
public function configureFields(string $pageName): iterable{
return [
ImageField::new('pdf', 'Your PDF')
->setFormType(FileUploadType::class)
->setBasePath('documents/') //see documentation about ImageField to understand the difference beetwen setBasePath and setUploadDir
->setUploadDir('public/documents/')
->setColumns(6)
->hideOnIndex()
->setFormTypeOptions(['attr' => [
'accept' => 'application/pdf'
]
]),
];
}
See documentation about ImageField to understand the difference beetwen setBasePath and setUploadDir
----- EDIT ----
In your index page of CRUD, you can create a link for your file like this:
public function configureFields(string $pageName): iterable{
return [
ImageField::new('pdf', 'Your PDF')
->setFormType(FileUploadType::class)
->setBasePath('documents/') //see documentation about ImageField to understand the difference beetwen setBasePath and setUploadDir
->setUploadDir('public/documents/')
->setColumns(6)
->hideOnIndex()
->setFormTypeOptions(['attr' => [
'accept' => 'application/pdf'
]
]),
TextField::new('pdf')->setTemplatePath('admin/fields/document_link.html.twig')->onlyOnIndex(),
];
}
Your templates/admin/fields/document_link.html.twig :
{% if field.value %}
Download file
{% else %}
--
{% endif %}

API Platform Symfony, cannot validate values of type boolean when send sms with sarbacane

I created a custom operation to send sms using symfony and sarbacane :
in my AppUser entity I added annotations :
* "GET",
* "PUT",
* "PATCH",
* "DELETE",
* "send_sms"={
* "method"="POST",
* "path"="/app_users/{id}/sms",
* "controller"=SmsController::class,
* "normalization_context"={"groups"={"user:read"}},
* "put"={"validation_groups"={"Default", "sedValidation"}}
* }
* }
In my controller I implements the invoke method :
public function __invoke(AppUser $user, Request $request, SerializerInterface $serializer) : bool
{
$data = $request->getContent();
// json decode transforms to object by default
// add true
$json_encode = json_decode($data, true);
$content = $json_encode['content'];
$currentUser = $this->getUser();
$currentUserPhone = $currentUser->getPhone();
$res = $this->sarbacaneApiHelper->call('campaigns/sms', [
'name' => sprintf("eXpanded n°%s", uniqid()),
'kind' => 'SMS_NOTIFICATION',
'smsFrom' => "eXpanded", // entre 3 et 11 caractères alpha-numériques
'content' => $content, // max 450 caractères
]);
$phone = $currentUserPhone;
$sarbacaneCampaignId = $res->id;
// Ajoute des destinataires à la campagne Sarbacane
$res = $this->sarbacaneApiHelper->call(sprintf('campaigns/%s/recipients', $sarbacaneCampaignId), [
[
'phone' => $phone,
],
]);
$params = [
"phone" => $currentUserPhone,
];
$this->sarbacaneApiHelper->call(sprintf('campaigns/%s/send', $sarbacaneCampaignId), $params);
$sent = true;
return $sent;
}
I tested the api using postman, and I got 500 internal Server Error :
"hydra:description": "Cannot validate values of type "boolean" automatically. Please provide a constraint."
Why does this error message appear?
An invoke() method must return either:
a Symfony\Component\HttpFoundation\ResponseResponse instance,
an instance of the target entity (seems to be AppUser in this case).
In your case, the method returns true; since the validation comes right after the controller, Api-Platform try to validate this boolean, and this is not possible. It expects an entity.
About the showed code within the question
It remains pretty unclear to me what you're trying to achieve:
Why the $user arg is never used?
Do you want to save any entity once your e-mail is sent?
Why do you fetch the Request content ?

JMS Serializer event is not working

I am sure it is a small error but I cannot find it.
I am trying to follow the official doc and implement an event listener on the pre_serialize event.
My service declaration:
<service id="app.question_serializer_subscriber" class="AppBundle\Serializer\QuestionSerializerSubscriber">
<tag name="jms_serializer.event_subscriber"/>
</service>
My subscriber:
<?php
namespace AppBundle\Serializer;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\ObjectEvent;
class QuestionSerializerSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'method' => 'onPreSerialize',
)
);
}
public function onPreSerialize(ObjectEvent $event)
{
die('in event');
}
}
And my controller:
$question = $repo->findLastVersionByQuestionId((int) $questionId);
$serializer = SerializerBuilder::create()->build();
$context = new SerializationContext();
return new JsonResponse(json_decode(
$serializer->serialize(
$question,
'json',
$context
),
true
));
When I access the route my entity Question is serialized and displayed, but why does the die('in event'); is not displayed ?
Maybe it has a relation with the fact that my object is a Doctrine entity (issue 666 or PR 677 )
I finally find the issue. The problem is
$serializer = SerializerBuilder::create()->build();
This does not work but this does:
$serializer = $this->get('jms_serializer');
Try adding the class attribute, as example:
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'class' => 'FQCN_class_name',
'method' => 'onPreSerialize',
)
);
}
Another difference regarding the doc is in the argument of the class method: you should use PreSerializeEvent instead of ObjectEvent:
So like this:
public function onPreSerialize(PreSerializeEvent $event)
{
// ...
}
Check your service is correctly load from the container as example with the console command:
php app/console debug:container --tag=jms_serializer.event_subscriber
Hope this help

Using Subscribing Handler Interface when Serialize te modify parameters in symfony

I have a REST API and have an Entity Userwith field called Avatar, in DB I save name XXXX.jpg but when I return I want to add a url in this field Avatar, for example www.mylink.com/XXXX.jpg.
I'm trying with a service implements SubscribingHandlerInterfacebut I don't know how I can use it.
I have this method in this service:
class UrlManager implements SubscribingHandlerInterface
{
public static function getSubscribingMethods()
{
return array(
array(
'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
'format' => 'json',
'type' => 'AppBundle/Entity/User',
'method' => 'serializeUrlAvatar',
),
);
}
public function serializeUrlAvatar(User $user)
{
$url = 'www.mylink.com';
return array(
"avatar" => $url . $user->getAvatar()
);
}
}
but how can I call this service to modify url when I serialize.
Now I do this:
$_format = 'json';
$json = $this->get('jms_serializer')->serialize($user, $_format);
return new Response($json, 200, ['Content-Type' => 'application/' . $_format]);
In service.yml:
app.url_converter_service:
class: AppBundle\Service\UrlManager
tags:
- { name: jms_serializer.subscribing_handler }
Update
In my controller I call this function like this:
$result = $this->get('app.url_converter_service')->serializeUrlAvatar($user);
$json = $this->get('jms_serializer')->serialize($result, $_format);
return new Response($json, 200, ['Content-Type' => 'application/' . $_format]);
So my question is, exists a way to remove the first line and serialize correctly (add the url) when I serialize?
Have you registered your service like this?
# app/config/services.yml
avatar_url_handler:
class: YourBundle\Serializer\Handler\AvatarUrlHandler
tags:
- { name: jms_serializer.subscribing_handler }
I found a solution. I create a service which implements EventSubscriberInterface like this:
class UserSerializeHandler implements EventSubscriberInterface
{
private $user_uploads;
public function __construct($user_uploads){
$this->user_uploads = $user_uploads;
}
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.pre_serialize',
'class' => User::class,
'method' => 'onPreSerializeUser'
));
}
public function onPreSerializeUser(PreSerializeEvent $event)
{
/** #var User $user */
$user = $event->getObject();
$avatar = $user->getAvatar();
$user->setAvatar($this->user_uploads . "/" . $avatar);
}
}
In service.yml:
app.serializer_user_service:
class: AppBundle\Service\UserSerializeHandler
arguments: ['%user_uploads%']
tags:
- { name: jms_serializer.event_subscriber }
I have user_uploads in parameters.yml like this:
user_uploads: 'https://myUrl.com'
And in any Controller that I serialize a User, I add the url in the Avatar paramter.
$json = $this->get('jms_serializer')->serialize($user, $_format);
return new Response($json, 200, ['Content-Type' => 'application/' . $_format]);

How to authenticate login in laravel 5.3

I am having a code for login which is in my AuthController like this.
public function login(Request $request){
$email = $request->input('email');
$password = $request->input('password');
$validation = array(
'email' =>'required',
'password' => 'required');
$validator = Validator::make($request->all(), $validation);
if ($validator->fails()) {
$messages = $validator->messages();
return redirect('login_with_assismo')
->withErrors($validator)
->withInput(Input::except('password'));
} else {
if (auth()->authenticate()) {
return redirect()->intended('welcome');
}
}
}
When i use this is i think login performs but it redirect me to the page somthng like this
localhost:8000/login
Anyone help me how to authenticate login am i doing something wrong or what. Please get the solution for this.
You can use attempt() function to login the user as:
public function login(Request $request)
{
$inputs = $request->only('email', 'password');
$rules = array(
'email' =>'required',
'password' => 'required'
);
$validator = Validator::make($inputs, $rules);
if ($validator->fails()) {
$messages = $validator->messages();
return redirect('login_with_assismo')
->withErrors($validator)
->withInput($request->except('password'));
}
if (auth()->attempt($inputs)) {
return redirect()->intended('welcome');
}
return redirect()->back()->withInput();
}
You may want to use the Auth facade, as described here:
https://laravel.com/docs/5.3/authentication#authenticating-users
Dont panic bro its because of Auth middleware go with #amit's code just go to
App\Http\Middleware\RedirectIfAuthenticated
and replace this route with your's
if (Auth::guard($guard)->check()) {
return redirect('/home');
}
after that you are good to go

Resources