xref: /webtrees/app/Services/CaptchaService.php (revision d11be7027e34e3121be11cc025421873364403f9)
170ca9c90SGreg Roach<?php
270ca9c90SGreg Roach
370ca9c90SGreg Roach/**
470ca9c90SGreg Roach * webtrees: online genealogy
5*d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
670ca9c90SGreg Roach * This program is free software: you can redistribute it and/or modify
770ca9c90SGreg Roach * it under the terms of the GNU General Public License as published by
870ca9c90SGreg Roach * the Free Software Foundation, either version 3 of the License, or
970ca9c90SGreg Roach * (at your option) any later version.
1070ca9c90SGreg Roach * This program is distributed in the hope that it will be useful,
1170ca9c90SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
1270ca9c90SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1370ca9c90SGreg Roach * GNU General Public License for more details.
1470ca9c90SGreg Roach * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
1670ca9c90SGreg Roach */
1770ca9c90SGreg Roach
1870ca9c90SGreg Roachdeclare(strict_types=1);
1970ca9c90SGreg Roach
2070ca9c90SGreg Roachnamespace Fisharebest\Webtrees\Services;
2170ca9c90SGreg Roach
222e464181SGreg Roachuse Fisharebest\Webtrees\Registry;
2370ca9c90SGreg Roachuse Fisharebest\Webtrees\Session;
24b55cbc6bSGreg Roachuse Fisharebest\Webtrees\Validator;
2570ca9c90SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
2670ca9c90SGreg Roach
2710e06497SGreg Roachuse function assert;
2810e06497SGreg Roachuse function is_float;
2910e06497SGreg Roachuse function is_string;
3070ca9c90SGreg Roachuse function view;
3170ca9c90SGreg Roach
3270ca9c90SGreg Roach/**
3370ca9c90SGreg Roach * Completely Automated Public Turing test to tell Computers and Humans Apart.
3470ca9c90SGreg Roach */
3570ca9c90SGreg Roachclass CaptchaService
3670ca9c90SGreg Roach{
3770ca9c90SGreg Roach    // If the form is completed faster than this, then suspect a robot.
3870ca9c90SGreg Roach    private const MINIMUM_FORM_TIME = 3.0;
3970ca9c90SGreg Roach
4070ca9c90SGreg Roach    /**
4170ca9c90SGreg Roach     * Create the captcha
4270ca9c90SGreg Roach     *
4370ca9c90SGreg Roach     * @return string
4470ca9c90SGreg Roach     */
4570ca9c90SGreg Roach    public function createCaptcha(): string
4670ca9c90SGreg Roach    {
472e464181SGreg Roach        $x = Registry::idFactory()->uuid();
482e464181SGreg Roach        $y = Registry::idFactory()->uuid();
492e464181SGreg Roach        $z = Registry::idFactory()->uuid();
5070ca9c90SGreg Roach
51eeec557aSGreg Roach        Session::put('captcha-t', Registry::timeFactory()->now());
5270ca9c90SGreg Roach        Session::put('captcha-x', $x);
5370ca9c90SGreg Roach        Session::put('captcha-y', $y);
5470ca9c90SGreg Roach        Session::put('captcha-z', $z);
5570ca9c90SGreg Roach
5670ca9c90SGreg Roach        return view('captcha', [
5770ca9c90SGreg Roach            'x' => $x,
5870ca9c90SGreg Roach            'y' => $y,
5970ca9c90SGreg Roach            'z' => $z,
6070ca9c90SGreg Roach        ]);
6170ca9c90SGreg Roach    }
6270ca9c90SGreg Roach
6370ca9c90SGreg Roach    /**
6470ca9c90SGreg Roach     * Check the user's response.
6570ca9c90SGreg Roach     *
6670ca9c90SGreg Roach     * @param ServerRequestInterface $request
6770ca9c90SGreg Roach     *
6870ca9c90SGreg Roach     * @return bool
6970ca9c90SGreg Roach     */
7070ca9c90SGreg Roach    public function isRobot(ServerRequestInterface $request): bool
7170ca9c90SGreg Roach    {
72d8809d62SGreg Roach        $t = Session::pull('captcha-t');
73d8809d62SGreg Roach        $x = Session::pull('captcha-x');
74d8809d62SGreg Roach        $y = Session::pull('captcha-y');
75d8809d62SGreg Roach        $z = Session::pull('captcha-z');
7670ca9c90SGreg Roach
77e6b1f16dSGreg Roach        assert(is_float($t));
78b55cbc6bSGreg Roach        assert(is_string($x));
79b55cbc6bSGreg Roach        assert(is_string($y));
80b55cbc6bSGreg Roach        assert(is_string($z));
81b55cbc6bSGreg Roach
82b55cbc6bSGreg Roach        $value_x = Validator::parsedBody($request)->string($x, '');
83b55cbc6bSGreg Roach        $value_y = Validator::parsedBody($request)->string($y, '');
8470ca9c90SGreg Roach
8570ca9c90SGreg Roach        // The captcha uses javascript to copy value z from field y to field x.
8670ca9c90SGreg Roach        // Expect it in both fields.
8770ca9c90SGreg Roach        if ($value_x !== $z || $value_y !== $z) {
8870ca9c90SGreg Roach            return true;
8970ca9c90SGreg Roach        }
9070ca9c90SGreg Roach
91e6b1f16dSGreg Roach        // If the form was returned too quickly, then probably a robot.
92eeec557aSGreg Roach        return Registry::timeFactory()->now() < $t + self::MINIMUM_FORM_TIME;
9370ca9c90SGreg Roach    }
9470ca9c90SGreg Roach}
95