Passed
Pull Request — master (#26)
by Dmitrii
03:28
created

Recaptcha3Validator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 4
dl 0
loc 6
ccs 2
cts 2
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 Symfony\Component\Validator\Constraint;
8
use Symfony\Component\Validator\ConstraintValidator;
9
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
10
11
final class Recaptcha3Validator extends ConstraintValidator
12
{
13
    /** @var ReCaptcha */
14
    private $recaptcha;
15
    private $enabled;
16
    private $ipResolver;
17
    private $revealErrorsFromResponse;
18
19
    public function __construct($recaptcha, bool $enabled, IpResolverInterface $ipResolver, bool $revealErrorsFromResponse = false)
20
    {
21
        $this->recaptcha = $recaptcha;
22 9
        $this->enabled = $enabled;
23
        $this->ipResolver = $ipResolver;
24 9
        $this->revealErrorsFromResponse = $revealErrorsFromResponse;
25 9
    }
26 9
27 9
    public function validate($value, Constraint $constraint): void
28
    {
29 9
        if ($value !== null && !is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) {
30
            throw new UnexpectedTypeException($value, 'string');
31 9
        }
32
        if (!$constraint instanceof Recaptcha3) {
33
            throw new UnexpectedTypeException($constraint, Recaptcha3::class);
34 9
        }
35 2
        if (!$this->enabled) {
36
            return;
37 7
        }
38 1
        $value = null !== $value ? (string) $value : '';
39
        $this->validateCaptcha($value, $constraint);
40
    }
41 6
42 2
    private function validateCaptcha(string $value, Recaptcha3 $constraint): void
43
    {
44
        if ($value === '') {
45 4
            $this->buildViolation($constraint->messageMissingValue, $value);
46
            return;
47 4
        }
48 4
        $ip = $this->ipResolver->resolveIp();
49 2
        $response = $this->recaptcha->verify($value, $ip);
50 2
        if (!$response->isSuccess()) {
51 2
            if ($this->revealErrorsFromResponse && !empty($response->getErrorCodes())) {
52 2
                foreach ($response->getErrorCodes() as $errorCode) {
53
                    $message = $this->getErrorMessage($errorCode, $constraint);
54 4
                    $this->buildViolation($message, $value);
55
                }
56
            } else {
57
                $this->buildViolation($constraint->message, $value);
58
            }
59
        }
60
    }
61
62
    private function getErrorMessage(string $errorCode, Recaptcha3 $constraint): string
63
    {
64
        switch ($errorCode) {
65
            case 'missing-input-secret':
66
                return $constraint->messageMissingInputSecret;
67
            case 'invalid-input-secret':
68
                return $constraint->messageInvalidInputSecret;
69
            case ReCaptcha::E_MISSING_INPUT_RESPONSE:
70
                return $constraint->messageMissingInputResponse;
71
            case 'invalid-input-response':
72
                return $constraint->messageInvalidInputResponse;
73
            case 'bad-request':
74
                return $constraint->messageBadRequest;
75
            case 'timeout-or-duplicate':
76
                return $constraint->messageTimeoutOrDuplicate;
77
            case ReCaptcha::E_CHALLENGE_TIMEOUT:
78
                return $constraint->messageChallengeTimeout;
79
            case ReCaptcha::E_SCORE_THRESHOLD_NOT_MET:
80
                return $constraint->messageScoreThresholdNotMet;
81
            case ReCaptcha::E_BAD_RESPONSE:
82
                return $constraint->messageBadResponse;
83
            case ReCaptcha::E_CONNECTION_FAILED:
84
                return $constraint->messageConnectionFailed;
85
            case ReCaptcha::E_INVALID_JSON:
86
                return $constraint->messageInvalidJson;
87
            case ReCaptcha::E_UNKNOWN_ERROR:
88
                return $constraint->messageUnknownError;
89
            case ReCaptcha::E_HOSTNAME_MISMATCH:
90
                return $constraint->messageHostnameMismatch;
91
            case ReCaptcha::E_APK_PACKAGE_NAME_MISMATCH:
92
                return $constraint->messageApkPackageMismatch;
93
            case ReCaptcha::E_ACTION_MISMATCH:
94
                return $constraint->messageActionMismatch;
95
            default:
96
                return sprintf($constraint->messageNotImplementedErrorCode, $errorCode);
97
        }
98
    }
99
100
    private function buildViolation(string $message, string $value): void
101
    {
102
        $this->context->buildViolation($message)
103
            ->setParameter('{{ value }}', $this->formatValue($value))
104
            ->setCode(Recaptcha3::INVALID_FORMAT_ERROR)
105
            ->addViolation();
106
    }
107
}
108