Consultez la FAQ sur le ZF avant de poster une question
Vous n'êtes pas identifié.
Bonjour,
J'ai des soucis pour gérer le champ CSRF dans un formulaire.
Je créé mon champ dans ma classe formulaire :
[lang=php] $saiCrsf = new Csrf('CSRF'); $saiCrsf->setLabel('csrf'); $this->add($saiCrsf);
Et lorsque je fais le isValid() j'ai ce message :
The form submitted did not originate from the expected site
Si je met un point d'arrêt dans le isValid() lorsqu'il fait la comparaison de la valeur du champ avec celle sauvegardée, la valeur sauvegardée est vide ($hash).
[lang=php] public function isValid($value, $context = null) { $this->setValue((string) $value); $hash = $this->getValidationToken(); if ($value !== $hash) { $this->error(self::NOT_SAME); return false; } return true; }
Ai-je oublié quelque chose ?
Dernière modification par J0r (29-11-2012 16:47:55)
Hors ligne
Salut il faudrait voir comment tu le fais au niveau de ton contrôleur car ça fonctionne très bien.
De plus tu n'as pas besoin du setLabel c'est un élément invisible on s'en fou du label .
Hors ligne
Pour le label c'était juste pour tester si ça avait une incidence sur le fonctionnement, comme ça ne marchait pas j'ai essayé.
Ci dessous le code dans mon action.
Dans prepareElements, je créé mes champs et les ajoute au formulaire.
Dans prepareFiltes je créé les filtres et les validateurs.
[lang=php] $form = new AuthentificationForm(); $form->prepareElements(); //On rentre les données passées en POST dans le formulaire $form->setData($this->getRequest()->getPost()); //On applique les filtres et les validateurs $formFilter = $form->prepareFilters(); $form->setInputFilter($formFilter); //On vérifie les validateurs du formulaires if (!$form->isValid($_POST)) { $view->setVariable('form', $form); return $view; }
Ceci marche pour tous mes formulaires, seul celui avec le champ CSRF ne fonctionne pas.
Hors ligne
Franchement c'est vraiment crade comme façon de faire. Je me souviens pas que le documentation officielle recommande ce genre de chose.
Comme je l'ai dit dans un autre sujet traitant des formulaires; tu as le choix de soit avoir ta classe form et ta classe input filter séparée soit dans la même classe en implémentant l'interface InputProviderInterface (si je me trompe pas sur le nom). Cette interface implémente la méthode getInputFilter() qui fonctionne de la même manière que l'ajout de composant dans le formulaire (via array, la doc officielle t'en dira plus). Donc la méthode que tu utilises n'est clairement pas recommandée.
Ensuite l'utilité d'un CSRF sur un formulaire d'authentification reste à discuter. C'est censé protéger des usurpations d'identité via des failles CSRF, principalement sur des actions GET mais tu as raison de protéger aussi les post c'est plus compliqué à mettre en place pour le "pirate" mais ça se fait. Le fait étant que sur une authentification le pirate doit connaitre le login et le mot de passe du membre hors les attaques CSRF se font généralement sur des données génériques (URL etc ...).
Pour en revenir à ton code si tu utilises la méthode avec le form et le input filter séparés tu dois les assembler dans une factory (ou directement dans le Module.php) en instanciant ton form puis ton input filter et en faisant un $form->setInputFilter($inputFilter).
Tel que je vois ton code tu sembles vouloir utiliser la même classe pour regrouper les deux, je t'encourage donc à implémenter l'interface de façon à ne pas avoir à faire toute la partie "prepare" car normalement on défini les éléments dans le constructeur du form et le input filter dans getInputFilter(). Avec cette méthode tu peux définir dans ton module.config.php tu peux l'ajouter dans "invokable" pour récupérer ton formulaire depuis le service manager.
Enfin ton code n'est pas cohérent tu fais $form->setData($this->getRequest()->getPost()); et après !$form->isValid($_POST) pourquoi ?
[lang=php] $form = $this->getServiceLocator()->get('Application\Form\AuthentificationForm'); // soit en créant un getter dans ton contrôleur, si c'est le cas penses à ajouter un attribut $authentificationForm et dans le getter tester s'il est déjà défini ou pas pour ne pas le récupérer plusieurs fois via le service manager dans la même requête. C'est pas très grave mais c'est un petit gain. $datas = $this->request->getPost(); $form->setData($form) if (!$form->isValid()) { // Pas la peine de remettre les données elles y sont déjà et ça ne doit plus être possible de toute façon return new ViewModel(array('form' => $form')); }
Hors ligne
Je ne te suis pas trop sur la version crade, comme j'ai déjà dis je suis ce qui a été dis et fais dans la formation ZF2 à part le regroupement.
Après je peux séparer les deux classes cela ne va pas changer grand chose.
Quelles sont les avantages à récupèrer le formulaire via le serviceManager plutôt que via une appel dans l'action ?
Ci-dessous le code de l'application présenter en formation ZF2 :
[lang=php] public function indexAction() { $message = 'Post a "hoot" to another user, or a "holler" to all'; $data = ''; $users = array_merge(array('' => 'Choose'), $this->_usersTable->getSelectUsers()); $request = $this->getRequest(); $form = new HootAndHollerForm(); $form->prepareElements($users); if ($request->isPost()) { $filter = new HootAndHollerFilter(); $inputFilter = $filter->prepareFilters(); $form->setInputFilter($inputFilter); $form->setData($request->getPost()); if ($form->isValid()) { $authService = new AuthenticationService(); if ($authService->hasIdentity()) { $sender = $authService->getIdentity()->email; } else { $sender = 'guest@zend.com'; } $text = $inputFilter->getValue('text'); $recipient = ($inputFilter->getValue('type') == 'T') ? $inputFilter->getValue('recipient') : NULL; $this->_messagesTable->add($sender, $text, $recipient); $message = 'Message Sent'; } else { $message = 'Back to the Drawing Board!'; } $data = $inputFilter->getValues(); } return new ViewModel(array('form' => $form, 'message' => $message, 'data' => $data)); }
EDIT, mes excuses concernant la question sur le setData, j'avais pas vu le $_POST, un reliquat qui trainait.
Dernière modification par J0r (29-11-2012 12:03:56)
Hors ligne
Sur le site de Zend, cela ressemble quand même au truc crade que j'ai fais :
http://framework.zend.com/manual/2.0/en … controller
Hors ligne
J0r a écrit:
Je ne te suis pas trop sur la version crade, comme j'ai déjà dis je suis ce qui a été dis et fais dans la formation ZF2 à part le regroupement.
Je me souviens pas avoir lu que tu avais fait une formation mais pour moi une partie du code est crade. Notamment le fait que ça ne soit pas cohérent pourquoi à un moment utiliser $this->getRequest()->getPost() (qui va récupérer $_POST au final) et après utiliser directement $_POST ? De plus l'utilisation du $_POST sur le isValid() est inutile puisque isValid() ne prend pas de paramètres le form ayant déjà reçu les données via le setData().
J0r a écrit:
Après je peux séparer les deux classes cela ne va pas changer grand chose.
Je suis tout à fait d'accord avec toi, je voulais simplement signaler que la façon de faire n'était pas conforme à ce qui est préconisé. Elle fonctionne surement.
Je t'invite à aller regarder le code du webinar de Bakura, la video n'est pas encore dispo ça traine un peu. C'est lui qui a participé à l'élaboration de Zend\Form et il ne préconise pas de faire comme tu le fais :p.
J0r a écrit:
Concernant la question sur le setData, cf la doc de Zend (http://framework.zend.com/manual/2.0/en … start.html
Code:
[lang=php] // Get the data. In an MVC application, you might try: $data = $request->getPost(); // for POST data $data = $request->getQuery(); // for GET (or query string) data $form->setData($data); // Validate the form if ($form->isValid()) {
Et ça ne ressemble pas vraiment à ce que tu as fait notamment le isValid()
J0r a écrit:
Quelles sont les avantages à récupèrer le formulaire via le serviceManager plutôt que via une appel dans l'action ?
Ca permet de factoriser le code d'avoir à éviter de remettre toujours ton input filter manuellement dans chaque contrôleur. Là tu le refais à chaque fois alors que via le service manager tu le fais une seule fois dans ton application et quand tu vas récupérer l'instance tout sera prêt à l'emploi .
J0r a écrit:
Ci-dessous le code de l'application présenter en formation ZF2 :
Code:
[lang=php] public function indexAction() { $message = 'Post a "hoot" to another user, or a "holler" to all'; $data = ''; $users = array_merge(array('' => 'Choose'), $this->_usersTable->getSelectUsers()); $request = $this->getRequest(); $form = new HootAndHollerForm(); $form->prepareElements($users); if ($request->isPost()) { $filter = new HootAndHollerFilter(); $inputFilter = $filter->prepareFilters(); $form->setInputFilter($inputFilter); $form->setData($request->getPost()); if ($form->isValid()) { $authService = new AuthenticationService(); if ($authService->hasIdentity()) { $sender = $authService->getIdentity()->email; } else { $sender = 'guest@zend.com'; } $text = $inputFilter->getValue('text'); $recipient = ($inputFilter->getValue('type') == 'T') ? $inputFilter->getValue('recipient') : NULL; $this->_messagesTable->add($sender, $text, $recipient); $message = 'Message Sent'; } else { $message = 'Back to the Drawing Board!'; } $data = $inputFilter->getValues(); } return new ViewModel(array('form' => $form, 'message' => $message, 'data' => $data)); }
Je sais pas trop où tu as eu ta formation, loin de moi l'idée qu'elle n'était pas intéressante mais concernant les formulaires c'est vraiment dégueulasse et ne correspond pas vraiment aux préconisations proposée par Bakura entre autre lors de son webinar :s.
Hors ligne
J'ai fais la formation dans les bureaux de Zend à Paris par un formateur certifié Zend Frameword
Hors ligne
Désolé d'intervenir sur ce sujet, mais je me posais la même question :
Orkin a écrit:
Jor a écrit:
Quelles sont les avantages à récupèrer le formulaire via le serviceManager plutôt que via une appel dans l'action ?
Ca permet de factoriser le code d'avoir à éviter de remettre toujours ton input filter manuellement dans chaque contrôleur. Là tu le refais à chaque fois alors que via le service manager tu le fais une seule fois dans ton application et quand tu vas récupérer l'instance tout sera prêt à l'emploi
.
Ça évite d'avoir à remettre à chaque fois l'input filter, mais en terme de performance, c'est correct ?
Si on met le formulaire dans le service manager, il sera chargé peut importe l'action (même si on en a pas besoin) non ?
Hors ligne
Les validateurs sont dans la même classe que les filtres non ? comme ils sont différents selon les formulaires si il n'y a qu'un seul inputFilter comment on fait ?
Hors ligne
J0r a écrit:
J'ai fais la formation dans les bureaux de Zend à Paris par un formateur certifié Zend Frameword
Erf parce qu'on retrouve les habitues du ZF1 et non celles du ZF2 notamment sur les noms de variables; il n'y a pas du tout l'utilisation du service manager sur le service d'authentification alors qu'il a tout à fait sa place dedans et enfin le fait de renvoyer $data à la vue alors que le formulaire connait déjà les données sans oublier le fait que $data est rempli depuis le input filter au lieu du formulaire (ça fonctionne aussi mais c'est pas du tout cohérent, tu donnes les valeurs au form : $form->setData($this->request()->getPost()) et tu les récupères en faisant $inputFilter->getValues(), pour suivre le cheminement c'est pas clair du tout).
Il n'y a pas l'utilisation du module.config.php pour tout ce qui est adresse mail pour la variable $sender ça se fait pas en dur dans le contrôleur mais en passant par un fichier de configuration pour éviter justement d'avoir à le modifier partout. C'est une formation donc ça représente pas une application globale et il en a peut être parlé après mais je trouve ça bien de montrer quelque chose de propre.
Pour la partie $message c'est pareil il y a le plugin FlashMessenger qui sert justement à ça.
Enfin je ne veux pas critiquer le formateur je n'y étais pas mais on voit très clairement les automatismes proposés par le ZF1 donc on peux supposer que c'est un formateur certifié Zend Framework 1 et non Zend Framework 2.
Je t'encourage quand même à aller jeter un oeil sur le code du webinar de Bakura tu trouveras pas mal de réponses je pense.
Seryus a écrit:
Désolé d'intervenir sur ce sujet, mais je me posais la même question :
Ça évite d'avoir à remettre à chaque fois l'input filter, mais en terme de performance, c'est correct ?
Si on met le formulaire dans le service manager, il sera chargé peut importe l'action (même si on en a pas besoin) non ?
Tu as tout à fait raison de te poser la question. En terme de performance ça n'a que très peu d'impact car ce que fais le service manager c'est juste connaitre une référence vers un alias sous forme d'un array. Lorsque l'on va demander au service manager de nous renvoyer l'instance d'un objet en fonction de l'alias si on a défini que c'était une factory il va appeler la factory et retourner l'instance de l'objet si on a défini un "invokable" il va juste faire un new de la classe.
Une référence à un objet dans le service manager ne va pas l'instancier à chaque requête là est tout l'intérêt il ne l'instanciera que si on en a besoin donc si on fait $serviceManager->get('alias'). Lorsque l'on se trouve dans la même requête il est capable si on a autorisé l'objet à être partagé de renvoyer le même objet dans un contrôleur, un service, un plugin etc ... Là où je préconisais d'avoir un attribut dans la classe qui fait l'appel c'est pour faire ce genre de chose :
[lang=php] // Dans un controller public function getAuthentificationService() { if (!$this->auth) { $this->setAuthentificationService($this->getServiceLocator()->get('Zend\Service\AuthentificationService')); } return $this->auth; } public function setAuthentificationService(AuthentificationService $auth) { $this->auth = $auth; }
De cette façon si l'on utilise plusieurs fois l'authentificationService dans le contrôleur on ne le demandera qu'une seule fois au serviceManager. Le gain est minime mais tout ça mi bout à bout
[lang=php] public function indexAction() { // Je veux vérifier si le membre est authentifié, il existe un plugin mais on peut imaginer un truc comme ça if ($this->getAuthentificationService()->hasIdentity()) { // Là le SM va instancier comme il faut $this->getAuthentificationService()->getIdentity() // Là on va renvoyer l'attribut du contrôleur qui est déjà rempli comme il faut. Après on peut stocker le service dans la fonction. On retrouve fréquemment ce genre d'utilisation dans les services. } }
J0r a écrit:
Les validateurs sont dans la même classe que les filtres non ? comme ils sont différents selon les formulaires si il n'y a qu'un seul inputFilter comment on fait ?
Je ne suis pas sûr d'avoir bien compris ta question mais si tu suis l'exemple de Bakura tu vas avoir plusieurs fieldset qui vont représenter tes modèles et c'est très rarement le cas lorsque par moment tu as besoin d'un mail de 10 caractères maximum et dans d'autre 20. Donc tes filtres et validateurs pour l'email d'un même modèle seront toujours les mêmes. Ce que tu feras c'est simplement ajouter le fieldSet à ton formulaire et celui-ci ira récupérer les filtres et validateur que tu as définis.
Si je prend l'exemple de mon application j'ai séparé les formulaires et les input filter (je dois refactoriser pour tout mettre dans une seul classe). J'ai un input filter par formulaire je sais donc quel input filter je dois ajouter à mon formulaire.
Les input filter regroupent à la fois les validateurs et les filtres puisque leur rôle c'est de valider les données à l'aide des validateurs et de les filtrer (genre un trim, mise en majuscule etc ...).
Hors ligne
Plus haut tu écris ceci
Orkin a écrit:
Ca permet de factoriser le code d'avoir à éviter de remettre toujours ton input filter manuellement dans chaque contrôleur. Là tu le refais à chaque fois alors que via le service manager tu le fais une seule fois dans ton application et quand tu vas récupérer l'instance tout sera prêt à l'emploi wink.
Et maintenant tu dis que tu as un inputfilter par formulaire, comme dans mon cas j'ai un formulaire par controller, je vois pas trop le gain.
Orkin a écrit:
J0r a écrit:
Les validateurs sont dans la même classe que les filtres non ? comme ils sont différents selon les formulaires si il n'y a qu'un seul inputFilter comment on fait ?
Je ne suis pas sûr d'avoir bien compris ta question mais si tu suis l'exemple de Bakura tu vas avoir plusieurs fieldset qui vont représenter tes modèles et c'est très rarement le cas lorsque par moment tu as besoin d'un mail de 10 caractères maximum et dans d'autre 20. Donc tes filtres et validateurs pour l'email d'un même modèle seront toujours les mêmes. Ce que tu feras c'est simplement ajouter le fieldSet à ton formulaire et celui-ci ira récupérer les filtres et validateur que tu as définis.
Si je prend l'exemple de mon application j'ai séparé les formulaires et les input filter (je dois refactoriser pour tout mettre dans une seul classe). J'ai un input filter par formulaire je sais donc quel input filter je dois ajouter à mon formulaire.
Les input filter regroupent à la fois les validateurs et les filtres puisque leur rôle c'est de valider les données à l'aide des validateurs et de les filtrer (genre un trim, mise en majuscule etc ...).
Hors ligne
J0r a écrit:
Et maintenant tu dis que tu as un inputfilter par formulaire, comme dans mon cas j'ai un formulaire par controller, je vois pas trop le gain.
Parce que je dois refactoriser c'est pas propre ce que j'ai fait. Il n'y avait pas encore eu les webinar qui m'ont bien éclairé sur ce que je n'avais pas compris.
Là où tu gagnes du temps tu as à utiliser le même formulaire à différents endroit de ton site. Par exemple un formulaire qui a un input qui est obligatoire si tu as à l'utiliser à plusieurs endroit tu n'aurais pas à ré-écrire toute la partie $form->setInputFilter() etc ... Tu le feras une fois dans la factory et quand tu vas demander ton formulaire au service manager il va automatiquement y mettre le inputfilter que tu lui as spécifier de ce fait tu ne ré-écris pas 15 fois la même chose et si tu as à faire des modifications tu les fais à un seul endroit.
C'est là tout l'intérêt du service manager j'aurais presque envie de dire qu'il faut penser composant (un formulaire instancié avec son input filter, ses hydrateurs etc ...) a un intérêt d'être retourné par le service manager car tous ces éléments forment un composant et dissociés ils n'ont pas d'intérêt.
Hors ligne
Ok, le webinar de bakura c'est bien celui-ci ? https://github.com/bakura10/ZendFormWebinar
Hors ligne
Bon j'ai changé plein de truc, en relisant la partie formulaire du livre de Vincent Blanchon + le webinar de bakura, surement pas assez parce que ça fonctionne pas
Formulaire :
[lang=php] namespace Authentification\Form; use Zend\Form\Form; class AuthentificationForm extends Form { public function __construct() { parent::__construct('authentification-from'); //Création du champ courriel $this->add(array( 'type' => 'Zend\Form\Element\Text', 'name' => 'EMAIL', 'options' => array( 'label' => 'Courriel * : ' ) )); //Création du champ mot de passe $this->add(array( 'type' => 'Zend\Form\Element\Password', 'name' => 'MOTPASSE', 'options' => array( 'label' => 'Mot de passe * : ' ) )); //Création du champ CSRF $this->add(array( 'type' => 'Zend\Form\Element\Csrf', 'name' => 'CSRF', )); //Création du bouton Valider $this->add(array( 'type' => 'Zend\Form\Element\Submit', 'name' => 'VALIDER', 'attributes' => array( 'value' => 'Valider' ) )); } }
Filters :
[lang=php] namespace Authentification\Form; use Zend\InputFilter\InputFilter; class AuthentificationFilter extends InputFilter { public function __construct() { $this->add(Array( 'name' => 'EMAIL', 'required' => true, 'filters' => Array( Array( 'name' => 'Zend\Filter\StripTags', ), ), 'validator' => Array( Array( 'name' => 'Zend\Validator\StringLength', Array( 'max' => 150, ), ), Array( 'name' => 'Zend\Validator\EmailAddress', Array( 'useDomainCheck' => false ), ), ), )); $this->add(Array( 'name' => 'MOTPASSE', 'required' => true, 'filters' => Array( Array( 'name' => 'Zend\Filter\StripTags', ), ), 'validator' => Array( Array( 'name' => 'Zend\Validator\StringLength', Array( 'min' => 8, 'messageTemplates' => Array( 'stringLengthTooShort' => 'Le mot de passe doit faire au minimum 8 caractères.' ), ), ), Array( 'name' => 'Zend\Validator\Regex', Array( 'pattern' => '/^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z]).{8,}$/', 'messageTemplates' => Array( 'regexNotMatch' => 'Le mot de passe doit avoir au minimum une lettre minuscule, une lettre majuscule et un chiffre.' ), ), ), ), )); } }
Controller, j'ai pas encore implémenté la partie avec le service manager, j'essaye déjà de le faire fonctionner comme ça :
[lang=php] //Fonction d'index, affichage du formulaire function indexAction() { //Création de la vue $view = new ViewModel(); $form = new AuthentificationForm(); //Si ce n'est pas une requéte POST alors on arrete. if (!$this->getRequest()->isPost()) { $view->setVariable('form', $form); return $view; } $formFilter = new AuthentificationFilter(); $form->setInputFilter($formFilter); //On rentre les données passées en POST dans le formulaire $form->setData($this->getRequest()->getPost()); //On vérifie les validateurs du formulaires if (!$form->isValid()) { $view->setVariable('form', $form); return $view; }
Message identique concernant le CSRF : The form submitted did not originate from the expected site
Dernière modification par J0r (29-11-2012 16:07:36)
Hors ligne
Ah c'est déjà beaucoup plus sympa comme ça . Juste une question pourquoi des Array avec un A majuscule ?
Sinon là c'est plus embêtant, normalement le composant CSRF te créer un champ en session et c'est celui-ci qu'il utilise pour faire la comparaison.
J'ai déjà eu ce problème lorsque j'avais un autre formulaire dans ma vue qui était affiché et qui avait le même nom pour le CSRF du coup j'en avais 2 différents avec le même nom ça posait problème.
A tout hasard as-tu ajouté cette instruction quelque part à ton form ?
[lang=php]$this->setAttribute('method', 'post');
Tu as une erreur dans ton input filter pour les validateur c'est validatorS et non validator
A part ça et l'utilisation du service manager il n'y a pas de différence avec mes formulaires qui utilisent le CSRF. J'avoue que je sèche un peu ...
Hors ligne
Aucune raison pour le A de array
Le setAttribute je l'avais mis dans la vue et pas dans la classe form, j'ai changé.
Je viens de trouver l'erreur je suis un grosse buse, j'utilise un controller par défaut qui étend AbstractActionController et tous mes controllers l'étendent, et dans celui-ci je fais un raz de session
Au moins ça m'aura permis de corriger mes fomulaires.
Merci.
Hors ligne
Ahah ça arrive à tout le monde le principal c'est de s'en rendre compte . Et oui ça t'auras permis d'avoir des formulaires plus propres !
Hors ligne