Passed
Branch master (a0cc06)
by Dāvis
17:06
created

IsTrueValidator::validate()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 2
dl 0
loc 13
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace Sludio\HelperBundle\Captcha\Validator\Constraint;
4
5
use Symfony\Component\HttpFoundation\Request;
6
use Symfony\Component\Validator\Constraint;
7
use Symfony\Component\Validator\ConstraintValidator;
8
use Symfony\Component\Validator\Exception\ValidatorException;
9
10
class IsTrueValidator extends ConstraintValidator
11
{
12
    /**
13
     * The reCAPTCHA server URL's
14
     */
15
    const RECAPTCHA_VERIFY_SERVER = "https://www.google.com";
16
17
    /**
18
     * Recaptcha Private Key
19
     *
20
     * @var String
21
     */
22
    protected $secretKey;
23
24
    /**
25
     * Request Stack
26
     *
27
     * @var Request
28
     */
29
    protected $request;
30
31
    /**
32
     * HTTP Proxy informations
33
     * @var Array
34
     */
35
    protected $httpProxy;
36
37
    /**
38
     * Enable serverside host check.
39
     *
40
     * @var Boolean
41
     */
42
    protected $verifyHost;
43
44
    /**
45
     * Construct.
46
     *
47
     * @param String $secretKey
48
     * @param Array $httpProxy
49
     * @param Boolean $verifyHost
50
     */
51
    public function __construct($secretKey, array $httpProxy, $verifyHost)
52
    {
53
        $this->secretKey = $secretKey;
54
        $this->request = Request::createFromGlobals();
55
        $this->httpProxy = $httpProxy;
56
        $this->verifyHost = $verifyHost;
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function validate($value, Constraint $constraint)
63
    {
64
        // define variable for recaptcha check answer.
65
        $remoteip = $this->request->getClientIp();
66
        $response = $this->request->get("g-recaptcha-response");
67
68
        $isValid = $this->checkAnswer($this->secretKey, $remoteip, $response);
69
70
        if ($isValid["success"] !== true) {
71
            $this->context->addViolation($constraint->message);
72
            // Perform server side hostname check
73
        } elseif ($this->verifyHost && $isValid["hostname"] !== $this->request->getHost()) {
74
            $this->context->addViolation($constraint->invalidHostMessage);
75
        }
76
    }
77
78
    /**
79
     * Calls an HTTP POST function to verify if the user's guess was correct.
80
     *
81
     * @param String $secretKey
82
     * @param String $remoteip
83
     * @param String $response
84
     *
85
     * @throws ValidatorException When missing remote ip
86
     *
87
     * @return Boolean
88
     */
89
    private function checkAnswer($secretKey, $remoteip, $response)
90
    {
91
        if ($remoteip == null || $remoteip == "") {
92
            throw new ValidatorException("sludio_helper.captcha.recaptcha.validator.remote_ip");
93
        }
94
95
        // discard spam submissions
96
        if ($response == null || strlen($response) == 0) {
97
            return false;
98
        }
99
100
        $response = $this->httpGet(self::RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/siteverify", [
101
                "secret" => $secretKey,
102
                "remoteip" => $remoteip,
103
                "response" => $response,
104
            ]);
105
106
        return json_decode($response, true);
0 ignored issues
show
Bug introduced by
$response of type array is incompatible with the type string expected by parameter $json of json_decode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

106
        return json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
107
    }
108
109
    /**
110
     * Submits an HTTP POST to a reCAPTCHA server.
111
     *
112
     * @param String $host
113
     * @param String $path
114
     * @param Array  $data
115
     *
116
     * @return Array response
117
     */
118
    private function httpGet($host, $path, $data)
119
    {
120
        $host = sprintf("%s%s?%s", $host, $path, http_build_query($data, null, "&"));
121
122
        $context = $this->getResourceContext();
123
124
        return file_get_contents($host, false, $context);
125
    }
126
127
    /**
128
     * Resource context.
129
     *
130
     * @return resource context for HTTP Proxy.
131
     */
132
    private function getResourceContext()
133
    {
134
        if (null === $this->httpProxy["host"] || null === $this->httpProxy["port"]) {
135
            return null;
136
        }
137
138
        $options = [];
139
        $protocols = [
140
            "http",
141
            "https",
142
        ];
143
        foreach ($protocols as $protocol) {
144
            $options[$protocol] = [
145
                "method" => "GET",
146
                "proxy" => sprintf("tcp://%s:%s", $this->httpProxy["host"], $this->httpProxy["port"]),
147
                "request_fulluri" => true,
148
            ];
149
150
            if (null !== $this->httpProxy["auth"]) {
151
                $options[$protocol]["header"] = sprintf("Proxy-Authorization: Basic %s", base64_encode($this->httpProxy["auth"]));
152
            }
153
        }
154
155
        return stream_context_create($options);
156
    }
157
}