Passed
Push — master ( cc80b9...1640e1 )
by Divine Niiquaye
12:24
created

CaptchaValidationHandler   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 69
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 31
c 1
b 0
f 0
dl 0
loc 69
ccs 0
cts 27
cp 0
rs 10
wmc 9

2 Methods

Rating   Name   Duplication   Size   Complexity  
B authenticate() 0 38 7
A add() 0 8 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Biurad opensource projects.
7
 *
8
 * PHP version 7.4 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 *
17
 */
18
19
namespace Biurad\Security\Handler;
20
21
use Biurad\Security\Helper;
22
use Psr\Http\Message\ServerRequestInterface;
23
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
24
25
/**
26
 * Request Captcha validation.
27
 *
28
 * @author Divine Niiquaye Ibok <[email protected]>
29
 */
30
class CaptchaValidationHandler
31
{
32
    public const RECAPTCHA = 'recaptcha';
33
    public const HCAPTCHA = 'hcaptcha';
34
35
    private array $validators = [
36
        self::RECAPTCHA => [null, ['g-recaptcha-response'], null],
37
        self::HCAPTCHA => [null, ['h-captcha-response'], null],
38
    ];
39
40
    /**
41
     * Add/Replace captcha validation.
42
     *
43
     * @param string            $type          The name of the captcha to use
44
     * @param string            $secret        The secret code to access
45
     * @param array<int,string> $parameterKeys The required parameters list from request body
46
     * @param callable|null     $handler       The handler is in form of: $handler($request, $credentials, $secret)
47
     */
48
    public function add(string $type, string $secret, array $parameterKeys = [], callable $handler = null): void
49
    {
50
        if ($validator = &$this->validators[$type] ?? null) {
51
            $secret = $validator[$secret] ?? $secret;
52
            $parameterKeys = \array_merge($validator[1] ?? [], $parameterKeys);
53
        }
54
55
        $validator = [$secret, $parameterKeys, $handler];
56
    }
57
58
    /**
59
     * Authenticate a request using captcha.
60
     */
61
    public function authenticate(ServerRequestInterface $request, string $type = self::RECAPTCHA): bool
62
    {
63
        [$secret, $parameters, $handler] = $this->validators[$type] ?? [null, [], null];
64
        $credentials = Helper::getParameterValues($request, $parameters);
65
66
        if (!isset($secret)) {
67
            throw new BadCredentialsException('Cannot authenticate request without secret key');
68
        }
69
70
        if (null !== $handler) {
71
            return $handler($request, $credentials, $secret);
72
        }
73
74
        if (self::RECAPTCHA === $type) {
75
            if (empty($captcha = $credentials['g-recaptcha-response'])) {
76
                return false;
77
            };
78
79
            $url = 'https://www.google.com/recaptcha/api/siteverify?secret=' . \urlencode($secret) . '&response=' . \urlencode($captcha);
80
            $response = \file_get_contents($url);
81
            $responseKeys = \json_decode($response, true);
82
83
            return $responseKeys['success'] ?? false;
84
        }
85
86
        if (self::HCAPTCHA === $type) {
87
            if (empty($captcha = $credentials['h-captcha-response'])) {
88
                return false;
89
            };
90
91
            $url = 'https://hcaptcha.com/siteverify?secret=' . \urlencode($secret) . '&response=' . \urlencode($captcha) . '&remoteip=';
92
            $response = \file_get_contents($url . ($request->getServerParams()['REMOTE_ADDR'] ?? ''));
93
            $responseKeys = \json_decode($response, true);
94
95
            return $responseKeys['success'] ?? false;
96
        }
97
98
        return false;
99
    }
100
}
101