Test Failed
Pull Request — master (#38)
by Dmitrii
06:58
created

Recaptcha3Validator::getLastResponse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
1
<?php declare(strict_types=1);
2
3
namespace Karser\Recaptcha3Bundle\Validator\Constraints;
4
5
use Karser\Recaptcha3Bundle\Services\IpResolverInterface;
6
use ReCaptcha\ReCaptcha;
7
use ReCaptcha\Response;
8
use Symfony\Component\Validator\Constraint;
9
use Symfony\Component\Validator\ConstraintValidator;
10
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
11
12
final class Recaptcha3Validator extends ConstraintValidator
13
{
14
    /** @var ReCaptcha */
15
    private $recaptcha;
16
    private $enabled;
17
    private $ipResolver;
18
19
    /** @var Response|null */
20
    private $lastResponse;
21
22 9
    public function __construct($recaptcha, bool $enabled, IpResolverInterface $ipResolver)
23
    {
24 9
        $this->recaptcha = $recaptcha;
25 9
        $this->enabled = $enabled;
26 9
        $this->ipResolver = $ipResolver;
27 9
    }
28
29 9
    public function validate($value, Constraint $constraint): void
30
    {
31 9
        if ($value !== null && !is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
32
            throw new UnexpectedTypeException($value, 'string');
33
        }
34 9
        if (!$constraint instanceof Recaptcha3) {
35 2
            throw new UnexpectedTypeException($constraint, Recaptcha3::class);
36
        }
37 7
        if (!$this->enabled) {
38 1
            return;
39
        }
40
        $value = null !== $value ? (string) $value : '';
41 6
        $this->validateCaptcha($value, $constraint);
42 2
    }
43
44
    public function getLastResponse(): ?Response
45 4
    {
46
        return $this->lastResponse;
47 4
    }
48 4
49 2
    private function validateCaptcha(string $value, Recaptcha3 $constraint): void
50 2
    {
51 2
        if ($value === '') {
52 2
            $this->buildViolation($constraint->messageMissingValue, $value);
53
            return;
54 4
        }
55
        $ip = $this->ipResolver->resolveIp();
56
        $this->lastResponse = $response = $this->recaptcha->verify($value, $ip);
57
        if (!$response->isSuccess()) {
58
            $errorCodes = implode('; ', array_map([$this, 'getErrorMessage'], $response->getErrorCodes()));
59
            $this->buildViolation($constraint->message, $value, $errorCodes);
60
        }
61
    }
62
63
    private function getErrorMessage(string $errorCode): string
64
    {
65
        $messages = [
66
            'missing-input-secret' => 'The secret parameter is missing',
67
            'invalid-input-secret' => 'The secret parameter is invalid or malformed',
68
            'missing-input-response' => 'The response parameter is missing',
69
            'invalid-input-response' => 'The response parameter is invalid or malformed',
70
            'bad-request' => 'The request is invalid or malformed',
71
            'timeout-or-duplicate' => 'The response is no longer valid: either is too old or has been used previously',
72
            'challenge-timeout' => 'Challenge timeout',
73
            'score-threshold-not-met' => 'Score threshold not met',
74
            'bad-response' => 'Did not receive a 200 from the service',
75
            'connection-failed' => 'Could not connect to service',
76
            'invalid-json' => 'Invalid JSON received',
77
            'unknown-error' => 'Not a success, but no error codes received',
78
            'hostname-mismatch' => 'Expected hostname did not match',
79
            'apk_package_name-mismatch' => 'Expected APK package name did not match',
80
            'action-mismatch' => 'Expected action did not match',
81
        ];
82
        return $messages[$errorCode] ?? $errorCode;
83
    }
84
85
    private function buildViolation(string $message, string $value, string $errorCodes = ''): void
86
    {
87
        $this->context->buildViolation($message)
88
            ->setParameter('{{ value }}', $this->formatValue($value))
89
            ->setParameter('{{ errorCodes }}', $this->formatValue($errorCodes))
90
            ->setCode(Recaptcha3::INVALID_FORMAT_ERROR)
91
            ->addViolation();
92
    }
93
}
94