Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace core_ai;
18
 
19
use Psr\Clock\ClockInterface;
20
 
21
/**
22
 * Rate limiting functionality that can be used by AI providers.
23
 *
24
 * @package    core_ai
25
 * @copyright  2024 Matt Porritt <matt.porritt@moodle.com>
26
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27
 */
28
class rate_limiter {
29
    /** @var int TIME_WINDOW Time window in seconds (1 hour). */
30
    public const TIME_WINDOW = HOURSECS;
31
 
32
    /** @var null|rate_limiter Singleton instance of the rate limiter. */
33
    private static ?rate_limiter $instance = null;
34
 
35
    /** @var \cache_application Cache instance for rate limiter. */
36
    private \cache_application $cache;
37
 
38
    /**
39
     * Constructor.
40
     *
41
     * @param ClockInterface $clock Clock instance for time management.
42
     */
43
    public function __construct(
44
        /** @var ClockInterface Clock instance for time management. */
45
        private ClockInterface $clock,
46
    ) {
47
        $this->cache = \cache::make('core', 'ai_ratelimit');
48
    }
49
 
50
    /**
51
     * Check global rate limit for a component.
52
     *
53
     * @param string $component Name of the component.
54
     * @param int $ratelimit Number of requests per time window.
55
     * @return bool True if request is allowed, false otherwise.
56
     */
57
    public function check_global_rate_limit(string $component, int $ratelimit): bool {
58
        $currenttime = $this->clock->now()->getTimestamp();
59
        return $this->check_limit("global_{$component}", $ratelimit, $currenttime);
60
    }
61
 
62
    /**
63
     * Check user rate limit for a component.
64
     *
65
     * @param string $component Name of the component.
66
     * @param int $ratelimit Number of requests per time window.
67
     * @param int $userid User ID for user-specific rate limit.
68
     * @return bool True if request is allowed, false otherwise.
69
     */
70
    public function check_user_rate_limit(string $component, int $ratelimit, int $userid): bool {
71
        $currenttime = $this->clock->now()->getTimestamp();
72
 
73
        // Check and update user limit.
74
        return $this->check_limit("user_{$component}_{$userid}", $ratelimit, $currenttime);
75
    }
76
 
77
    /**
78
     * Helper function to check limit in cache.
79
     *
80
     * @param string $key Cache key.
81
     * @param int $ratelimit Number of requests per time window.
82
     * @param int $currenttime Current timestamp.
83
     * @return bool True if request is allowed, false otherwise.
84
     */
85
    private function check_limit(string $key, int $ratelimit, int $currenttime): bool {
86
        $ratedata = $this->cache->get($key);
87
 
88
        if ($ratedata === false) {
89
            // No data found, initialize rate data.
90
            $ratedata = ['count' => 0, 'start_time' => $currenttime];
91
        }
92
 
93
        // Remove expired rate data.
94
        if ($currenttime - $ratedata['start_time'] > self::TIME_WINDOW) {
95
            $ratedata['count'] = 0;
96
            $ratedata['start_time'] = $currenttime;
97
        }
98
 
99
        // Check rate limit.
100
        if ($ratedata['count'] < $ratelimit) {
101
            $ratedata['count']++;
102
            $this->cache->set($key, $ratedata);
103
            return true;
104
        }
105
 
106
        // Rate limit exceeded.
107
        return false;
108
    }
109
}