Skip to content

Commit

Permalink
Merge pull request #37 from Matteo-Peronnet/fix-captcha-validation
Browse files Browse the repository at this point in the history
Adding several captchas
  • Loading branch information
lenybernard authored Jun 1, 2018
2 parents 543bd7c + 627a199 commit f93e829
Show file tree
Hide file tree
Showing 35 changed files with 1,091 additions and 495 deletions.
70 changes: 70 additions & 0 deletions Controller/CaptchaController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Victoire\Widget\FormBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Victoire\Widget\FormBundle\Domain\Captcha\Adapter\CaptchaInterface;
use Victoire\Widget\FormBundle\Domain\Captcha\Adapter\SecurimageAdapter;
use Victoire\Widget\FormBundle\Entity\WidgetForm;

/**
* Class CaptchaController.
*
* @Route("/_victoire_form_captcha")
*/
class CaptchaController extends Controller
{

/**
* @Route("/validate/captcha/{widget_form_id}", name="victoire_form_captcha_validate")
* @ParamConverter("widget", class="VictoireWidgetFormBundle:WidgetForm", options={"id" = "widget_form_id"})
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function validateCaptchaAjaxAction(Request $request, WidgetForm $widget) {

$captchaHandler = $this->get('victoire.form_widget.domain.captcha.handler');
try {
/** @var $captchaAdapter CaptchaInterface */
$captchaAdapter = $captchaHandler->getCaptcha($widget->getCaptcha(), true);
} catch (\Exception $e) {
throw $this->createNotFoundException();
}

$data = [];
if(!$captchaAdapter->validateCaptcha($request, false)) {
$captchaAdapter->generateNewCaptcha();
$data = $captchaAdapter->getTwigParameters();
$data['valid'] = false;
} else {
$data['valid'] = true;
}

return new JsonResponse($data);
}

/**
* @Route("/render/captcha/{widget_form_id}", name="victoire_form_captcha_render")
* @ParamConverter("widget", class="VictoireWidgetFormBundle:WidgetForm", options={"id" = "widget_form_id"})
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function rendererCaptchaAction(Request $request, WidgetForm $widget) {

$captchaHandler = $this->get('victoire.form_widget.domain.captcha.handler');
try {
/** @var $captchaAdapter CaptchaInterface */
$captchaAdapter = $captchaHandler->getCaptcha($widget->getCaptcha(), true, true);
} catch (\Exception $e) {
throw $this->createNotFoundException();
}

return $this->render($captchaAdapter->getViewPath(), array_merge(['widget' => $widget], $captchaAdapter->getTwigParameters()));
}
}
21 changes: 12 additions & 9 deletions Controller/FormController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
namespace Victoire\Widget\FormBundle\Controller;

use Gedmo\Sluggable\Util\Urlizer;
use ReCaptcha\ReCaptcha;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\Constraints\Email as EmailConstraint;
use Troopers\AlertifyBundle\Controller\AlertifyControllerTrait;
use Victoire\Bundle\MediaBundle\Entity\Media;
use Victoire\Widget\FormBundle\Domain\Captcha\Adapter\CaptchaInterface;
use Victoire\Widget\FormBundle\Entity\WidgetForm;
use Victoire\Widget\FormBundle\Event\WidgetFormeMailEvent;

Expand All @@ -23,14 +23,15 @@ class FormController extends Controller
{
use AlertifyControllerTrait;


/**
* Handle the form submission.
*
* @param Request $request
*
* @return array
* @throws \Exception
* @Route("/addFormAnswerAction", name="victoire_contact_form_result")
*
* @return array
*/
public function addFormAnswerAction(Request $request)
{
Expand All @@ -45,16 +46,18 @@ public function addFormAnswerAction(Request $request)
$widget = $this->get('doctrine.orm.entity_manager')->getRepository('VictoireWidgetFormBundle:WidgetForm')->find($_taintedValues['id']);
$data = [];

///////////////////////// validation reCAPTCHA (if reCAPTCHA field checked) //////////////////////////////////////////
$recaptcha_helper = $this->container->get('victoire.form_widget.helper.recaptcha');
if ($widget->isRecaptcha() && $recaptcha_helper->canUseReCaptcha()) {
$recaptcha = new ReCaptcha($this->container->getParameter('victoire_widget_form.recaptcha_private_key'));
$resp = $recaptcha->verify($request->request->get('g-recaptcha-response'), $request->getClientIp());
///////////////////////// validation Captcha (if captcha field selected) //////////////////////////////////////////

if (!$resp->isSuccess()) {
try {
$captchaHandler = $this->get('victoire.form_widget.domain.captcha.handler');
/* @var $captchaAdapter CaptchaInterface */
$captchaAdapter = $captchaHandler->getCaptcha($widget->getCaptcha());
if (!$captchaAdapter->validateCaptcha($request)) {
$this->scold($this->get('translator')->trans('widget_form.form.captcha.error', [],'victoire'));
return $this->redirect($request->headers->get('referer'));
}
} catch (\Exception $e) {
// Do nothing. It's the case where selected captcha is None.
}

foreach ($_taintedValues['questions'] as $question) {
Expand Down
56 changes: 56 additions & 0 deletions Domain/Captcha/Adapter/AbstractCaptcha.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Victoire\Widget\FormBundle\Domain\Captcha\Adapter;

use Symfony\Component\HttpFoundation\Request;

abstract class AbstractCaptcha implements CaptchaInterface {

/**
* Check if the captcha is valid or not
* @param Request $request
* @param bool $clear
* @return bool
*/
abstract public function validateCaptcha($request, $clear = true);

/**
* Get the captcha name
* @return string
*/
abstract public function getName();

/**
* Check if current configuration allow to use this captcha
* @return boolean
*/
public function canBeUsed()
{
return true;
}

/**
* Return all parameters necessary in the view
* @return array
*/
public function getTwigParameters()
{
return [];
}

/**
* Return the view path to render the widget
*/
public function getViewPath()
{
return;
}

/**
* Regenerate a new Captcha
* @return mixed
*/
public function generateNewCaptcha() {
return;
}
}
13 changes: 13 additions & 0 deletions Domain/Captcha/Adapter/CaptchaCodeInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Victoire\Widget\FormBundle\Domain\Captcha\Adapter;

interface CaptchaCodeInterface
{
/**
* Get captcha Code by namespace
* @param $namespace
* @return mixed
*/
public function getCaptchaCode($namespace);
}
45 changes: 45 additions & 0 deletions Domain/Captcha/Adapter/CaptchaInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace Victoire\Widget\FormBundle\Domain\Captcha\Adapter;

use Symfony\Component\HttpFoundation\Request;

interface CaptchaInterface
{
/**
* Check if the captcha is valid or not
* @param Request $request
* @param bool $clear
* @return bool
*/
public function validateCaptcha($request, $clear = true);

/**
* Get the captcha name
* @return string
*/
public function getName();

/**
* Check if current configuration allow to use this captcha
* @return boolean
*/
public function canBeUsed();

/**
* Return all parameters necessary in the view
* @return array
*/
public function getTwigParameters();

/**
* Return the view path to render the widget
*/
public function getViewPath();

/**
* Regenerate a new Captcha
* @return mixed
*/
public function generateNewCaptcha();
}
131 changes: 131 additions & 0 deletions Domain/Captcha/Adapter/GregwarCaptchaAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

namespace Victoire\Widget\FormBundle\Domain\Captcha\Adapter;

use Gregwar\Captcha\CaptchaBuilder;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class GregwarCaptchaAdapter extends AbstractCaptcha implements CaptchaCodeInterface {

/**
* @var CaptchaBuilder
*/
private $captchaBuilder;

/**
* @var string
*/
private $namespace;

/**
* @var SessionInterface
*/
private $session;

public function __construct(SessionInterface $session, $env)
{
$this->session = $session;

if ($this->canBeUsed()) {
$phrase = ($env === 'ci' || $env === 'test') ? "correctly" : null;
$this->captchaBuilder = new CaptchaBuilder($phrase);
$this->generateNewCaptcha();
}
}

/**
* Check if the captcha is valid or not
* @param Request $request
* @param bool $clear
* @return bool
*/
public function validateCaptcha($request, $clear = true)
{
$code = $request->get('captcha_code');
$namespace = $request->get('captcha_namespace');
$codeDisplay = $this->getCaptchaCode($namespace);
if ($clear) {
$this->session->remove($namespace);
}

return strtolower($code) === strtolower($codeDisplay);
}

/**
* Get the captcha name
* @return string
*/
public function getName()
{
return 'GregwarCaptcha';
}

public function getTwigParameters() {
return [
'captcha_image' => $this->captchaBuilder->inline(),
'captcha_namespace' => $this->namespace
];
}

/**
* Check if current configuration allow to use this captcha
* @return boolean
*/
public function canBeUsed()
{
return extension_loaded('gd');
}

/**
* Regenerate a new Captcha
* @return mixed
*/
public function generateNewCaptcha() {
$this->setNamespace($this->generateCaptchaNamespace());
$this->captchaBuilder->build();
$this->session->set($this->getNamespace(), $this->captchaBuilder->getPhrase());
}

/**
* Return the view path to render the widget
*/
public function getViewPath()
{
return '@VictoireWidgetForm/form/captcha/captcha.html.twig';
}

/**
* Generate an unique id for the captcha namespace
* @return string
*/
private function generateCaptchaNamespace() {
return md5(uniqid(rand(), true));
}

/**
* @param string $namespace
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
}

/**
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}

/**
* Get captcha Code by namespace
* @param $namespace
* @return mixed
*/
public function getCaptchaCode($namespace)
{
return $this->session->get($namespace);
}
}
Loading

0 comments on commit f93e829

Please sign in to comment.